lua-users home
lua-l archive

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


On Mon, 17 Aug 2009 18:50:16 -0300 Luiz Henrique de Figueiredo
lhf@tecgraf.puc-rio.br> wrote:
> > I conjecture that a couple of small changes to the implementation of
> > Lua (not to the language) would allow me to shrink the Darwin code to
> > about 500 lines, and also to reduce the run-time space used by the
> > private tables of bindings in each structure.
>
> What changes would that be? Would they be restricted to loadlib.c?

Yes, I believe that a change or two to loadlib (systematically applied
therein) would eliminate my need to reimplement those functions in
Lua. (Let me know if what I need is already possible without changes.)

Here's one: loadlib functions refer to LUA_GLOBALSINDEX. In Darwin,
there is the notion of the *current* global environment. An example is
the Darwin implementation of package.seeall:

local function seeall(module)
if type(module)~="table" then
error("argument to seeall is not a table: "..tostring(module))
end
local mt = getmetatable(module)
if not mt then
mt = {}
setmetatable(module, mt)
end
mt.__index = structure.currentenvironment() -- mt.__index = the
current notion of _G
end

Compare the last line above to the equivalent lines in loadlib.c:

lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index"); /* mt.__index = _G */

I'd like a way to call the functions in loadlib as if LUA_GLOBALSINDEX
were bound to a different table. Not knowing the Lua source well, I
don't know how best to achieve this.

A similar change is needed to make LUA_REGISTRYINDEX._LOADED point to
the package.loaded table of the current environment, because each
structure has its own package table which reflects the set of Darwin
structures and Lua modules that are open in that structure.

Digression_involving_read_only_tables =
[[
An entirely different kind of change would make Darwin more
space-efficient at run-time. Today, when you open a structure S,
Darwin makes you a copy of the bindings exported by S. Instead, I
would prefer to somehow link your environment to a single immutable
table containing the bindings exported by S. Certainly, I could use
the __index metamethod of _G (or a table in _G) to establish the link.
The obstacle is that the binding table of S needs to be read-only.
Using metamethods to make S immutable is how my first implementation
worked. But I *really* want to allow easy re-use of existing code, and
techniques like "strict" rely on being able to modify the metatable of
their environment (_G for "strict" and a module's table for Lua
modules).

I know that read-only tables is a topic that people like to write
about (ironic, eh?), so I won't pursue this (much) further. If I were
contemplating such a feature, I would be tempted to pattern it after
__metatable, such that
getmetatable(S).__readonly=true
would cause any attempt to write into S to throw an error (even
rawset). The readonly state could be revoked by unsetting __readonly.
Locking the metatable would, of course, prevent such a change, giving
a truly immutable table and metatable.
]]

On Mon, 17 Aug 2009 21:52:10 -0400 David Manura dm.lua@math2.org> wrote:
> > If there is interest, I could find a place to post my implementation.
>
> I'm interested.  You may also post a page on the wiki (e.g. if you feel it's not yet ready for LuaForge).

Good idea. I'll make a page with some doc and examples and another
page for the code. When it's up, I'll post the link here on the
mailing list. Possibly this weekend.

> What is the semantics of "open" ?  Does this add the listed symbols to the private environment of the lanes module?

Yes. In a structure declaration, the 'open' clause lists the
structures that Darwin should open after creating a new (empty)
environment for the declared structure. After processing all the
declaration clauses, the 'environment' clause is evaluated to produce
the final environment.

In order to facilitate interactive development, Darwin creates a
structure called "structure" containing useful run-time operations
including these:
open(modname, ...) -- open each modname in the current environment
close(modname) -- close modname and delete its bindings from the
current environment
delete(modname) -- delete the declaration for modname (future attempts
to open modname will fail)
declared() -- return a table of declared structures in the system
(like package.loaders)

By default, "open" puts the bindings in a table named after the
module, just like Lua's module function does. But you can add a
'location' clause in your structure declaration that specifies another
place. (The special location "." means "at top level in the global
environment".) That way it's easy to achieve some common cases, such
as when you have more than one implementation of a module.

E.g. suppose you want to reuse some code that uses the "os" package,
but you want to intercept calls to certain os functions so that you
can enforce certain conditions, e.g. a white list of executables for
os.execute(). You can write your own implementation of the "os"
functions which mostly just reuses the existing "os" module.

If you name your structure "protectedOS", then you can put
location="os" in the declaration and Darwin will put your functions
(date, execute, etc.) in a table called "os". Code that needs "os"
will get your functions, which enforce your conditions.

Also, you can convert an arbitrary file of code that litters the
global environment into a structure that puts those bindings neatly
into a table (or into a mix of some top-level bindings and some in a
table, as you wish).

--Jim