lua-users home
lua-l archive

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

Jerome Vuarand writes:
> David Manura wrote:
> >
> The 'module', 'package.seeall', and to some extent the 'require'
> functions are not sandbox-ready in their default implementation....

Sandboxing is an important case, though a more general concern is
interoperability between modules maintained independently by different authors.

> Why 'module' is important, is that if all your third-party modules are
> calling the 'module' functions, you can easily replace the default
> "unsafe" module system with a safer (less easy to use) one. On the other
> hand if every module writer use its own protection mechanism (as good as
> it is), the final application writer cannot automatically and globally
> override inter-module cooperation behaviour.

The possibility to transparently swap in more robust versions of module+require
is a good point, if it can be applied, though the previous concerns, as far as I
have seen, require changes in interface rather than just implementation, and
with that there is the danger of breaking existing code.   Modules that make
very few assumptions would fare better, but many won't unless encouraged.  For
example, there are subtle points such as if module "one" requires module "two"
requires module "two.three" whether module "one" may assume it can directly use
"two.three" (rather than just "two").

Some ideas I've had in the past was for require to load its namespace variables
into the private environment of the caller rather than in the global
environment, as well for the module function to cause the environment seen
internally by the module to be distinct from the externally visible table it
publishes.  Though not perfect, it was roughly like this:

-- Creates both public and private namespaces for module.
-- Global assignments inside module gets placed in both
-- public and private namespaces, while the private namespace
-- falls back to _G.
function module(modname)
  local pub = {}     -- public namespace for module
  local priv = {}    -- private namespace for module
  local privmt = {}
  privmt.__index = _G
  privmt.__newindex = function(priv, k, v)
    rawset(pub, k, v)
    rawset(priv, k, v)
  setmetatable(priv, privmt)
  setfenv(2, priv)

  package.loaded[modname] = pub

-- Require module, but export only public (not private)
-- namespace into caller.
local oldrequire = require
function require(name)
  local result = oldrequire(name)
  rawset(getfenv(2), name, result)
  return result

At some point, one gives up and uses the simple solution that works.  If you
like to generalize it,

  local M = module()
  function M.test() ..... end
  return M

where one conforming implementation of module is simply

  function module() return {} end

Isn't that what we do for classes?

  local Date = class()
  function Date:addyears(years) ... end