lua-users home
lua-l archive

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


on 2/15/05 12:44 PM, Mark Hamburg at mhamburg@adobe.com wrote:

> * A metatable entry __assoc that would lead to a fully weak table. When
> marking an object, the GC would also mark obj.mt.__assoc[ obj ] (where
> obj.mt is the object metatable). This could be made reasonably efficient by
> using the same logic as the other fast metamethods.

I've got a prototype implementation in Lua 5.1w4. It was pretty simple.

The semantics are as follows:

* If a table or userdata has a metatable and that metatable has an entry
under the key __assoc and that entry is a table, then when we mark the table
or userdata, we also mark the value found by indirecting through the __assoc
table.

Usage
=====

Proxy tables to shield hidden implementations
---------------------------------------------

    local proxy2imp = setmetatable( {}, { __mode = "kv" } )
        -- fully weak so no fear of semi-weak cycle issues

    local proxyMethods = {}

    local proxyMetatable = {
        __index = proxyMethods,
        __metatable = "< protected >",
        __assoc = proxy2imp
    }

    function proxyMethods:setName( name )
        local imp = assert( proxy2imp[ self ] )
            -- This gets us type-checking as well!
        imp.name = name
    end

    function proxyMethods:printName()
        local imp = assert( proxy2imp[ self ] )
        print( imp.name )
    end

    function makeObject( initialName )
        local imp = { name = initialName }
        local proxy = setmetatable( {}, proxyMetatable )
        proxy2imp[ proxy ] = imp
        return proxy
    end


Lua data associated with userdata
----------------------------------

In the metatable for the userdata include an __assoc field and use it to map
from the userdata to the Lua data. This could probably benefit from some C
API support.


Implementation
==============

I'll see if I can figure out what I need to do to build a patch diff, but
here's a summary:

* Added a fast metatable entry TM_ASSOC ("__assoc") (ltm.h, ltm.c)

* In lgc.c:

Added (with a forward declaration):

static void markassociated
       (global_State *g, const TValue *assoc, const TValue *obj) {
  if (ttistable(assoc)) {
    const TValue *associated = luaH_get(hvalue(assoc), obj);
    if (!ttisnil(associated))
      markvalue(g, associated);
  }
}

Modified the LUA_TUSERDATA case of reallymarkobject so that instead of just
marking the metatable it does:

      if (mt) {
        const TValue* assoc = gfasttm(g, mt, TM_ASSOC);
        markobject(g, mt);
        if (assoc) {
          TValue key;
          key.tt = LUA_TUSERDATA;
          key.value.gc = o;
          markassociated(g, assoc, &key);
        }
      }

Modified the metatable handling in traversetable so that it does:

  Table *mt = h->metatable;
  if (mt) {
    const TValue *assoc = gfasttm(g, mt, TM_ASSOC);
    markobject(g, mt);
    if (assoc) {
      TValue key;
      /* We could use sethvalue if it took the global state. It doesn't. */
      key.tt = LUA_TTABLE;
      key.value.gc=cast(GCObject *, (h));
      markassociated(g, assoc, &key);
    }
  }

This could probably be tweaked slightly to share more code.

The cost of these changes is that for tables with metatables, the mark pass
has to check a flag bit in the metatable. It does more if the metatable has
an __assoc entry but we want it to do so there.

Mark