lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Miles Bader schrieb:
> "loadin" (etc; I assume they'll provide analogues for other load
> variants) combines the operations of parsing and environment-setting;
> that suffices to replace _some_ current uses of load+setfenv, but people
> actually make use of the Lua's current ability to execute those
> operations separately -- e.g., to re-evaluate the same loaded contents
> in different environments without the overhead/uncertainty [e.g. .. file
> goes away!] of re-loading/parsing.  loadin (etc) do not replace the
> latter usage.

I would like to second this and add an example. I am writing an
experimental MUD server which would have several domains of "untrusted"
code, where each domain has its own environment.

They would still share a lot of code and as a replacement for dofile ()
I wrote the following code:

  objects.includememory = objects.includememory or
        setmetatable ({}, {__mode = "v"})

  function objects.include (file, env)
    local env = env or getfenv (2)
    local buf = objects.includememory[file]
    if not buf then
      local code, err = objects.safereadfile (file)
      if not code then error (err, 2) end
      if code:byte () == 27 then
        error ("attempt to load a binary chunk", 2)
      end
      buf, err = loadstring (code, "@"..file)
      if not buf then error (err, 2) end
      objects.includememory[file] = buf
    end
    setfenv (buf, env)
    return buf ()
  end

[objects.safereadfile () is just a function that makes sure the filename
is nice and returns the file contents.]

This function would reuse the loaded code as long as it is still in
memory for different environments. For this I would need setfenv or an
equivalent feature.

It also uses by default the environment of the caller using getfenv (2).
Without getfenv or something equivalent this wouldn't be possible. When
calling it, you would always need to specify an environment. (So each
environment would need something like a _G variable to pass to the
include function).

The above code can be implemented using loadin and I already did so, but
it isn't as efficient as it can't reuse the compiled code for different
environments and could only reuse the loaded code.

Here is what it would look like:

  objects.includecodememory = objects.includecodememory or
     setmetatable ({}, {__mode = "v"})

  function objects.include (file, env)
    env.includememory = env.includememory or
       setmetatable ({}, {__mode = "v"})
    local buf = env.includememory[file]
    if buf then return buf () end

    local s = objects.includecodememory[file]
    local err
    if not s then
      s, err = safereadfile (file)
      if not s then error (err, 2) end
      objects.includecodememory[file] = s
    end

    local f, err = loadin (env, s, "@"..file, "t")
    if not f then error (err, 2) end
    env.includememory[file] = f
    return f ()
  end

Best regards,

David