lua-users home
lua-l archive

stack level abstraction [was Re: first class ':']

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


On Wed, Sep 16, 2009 at 9:22 AM, Luiz Henrique de Figueiredo wrote:
>> You do need to increase the stack level, but I think that David meant
>> some method to make the extra stack level(s) invisible to the debug API.
>
> This can be done in the traceback function itself, can't it?
> I mean, just scan backward and skip your "hidden" functions.
> Of course, you'll need a way to mark those: add them to a table or
> name them with a predefined prefix.
The problems with level in the error function have been described
earlier [1]. However, the concern more generally affects the use of
any stack manipulation function (e.g. getfenv, setfenv, error,
debug.getinfo, etc.) We may redefine these functions in the way you
suggest:
 -- Utility function: identity
 local function identity(...) return ... end
 -- Tells getlevel() to ignore function f.
 local ignore = setmetatable({}, {__mode='k'})
 function ignorelevel(f)
 f = f or debug.getinfo(2,'f').func
 ignore[f] = true
 return f
 end
 -- Calls f with given parameters in a way that getlevel()
 -- ignores it.
 function ignorecall(f, ...)
 return identity(f(...)) -- prevent tail call
 end
 ignorelevel(ignorecall)
 -- Like debug.getinfo(n,'f').func, but skips ignored functions.
 function getlevel(n)
 local fc
 local j=1
 for i=1,n do
 repeat
 fc = debug.getinfo(1+j,'f').func
 if fc == ignorecall then j=j+1 end -- ignore caller too
 j=j+1
 until not ignore[fc]
 end
 return fc
 end
 -- Redefine getfenv with getlevel behavior.
 local getfenv_old = getfenv
 function getfenv(f)
 if type(f) == 'number' and f > 0 then
 f = getlevel(f+1)
 end
 return getfenv_old(f)
 end
 ---- Test
 -- Some arbitrary function that deals with stack levels.
 local function import(x)
 getfenv(2)[x] = {}
 end
 -- Some code wrap the above function (two alternate ways)
 local function tuple(...)
 return {n=select('#',...), ...}
 end
 local function trace1(f)
 return ignorelevel(function(...)
 print'begin'
 local t = tuple(f(...))
 print'end'
 return unpack(t, 1, t.n)
 end)
 end
 local function trace2(f)
 return function(...)
 print'begin'
 local t = tuple(ignorecall(f,...))
 print'end'
 return unpack(t, 1, t.n)
 end
 end
 local import1 = trace1(import)
 local import2 = trace2(import)
 -- Use it.
 setfenv(1,{_G=_G})
 import1 'x'
 import2 'y'
 _G.assert(x)
 _G.assert(y)
 _G.print 'done'
[1] http://lua-users.org/wiki/LuaCarp

AltStyle によって変換されたページ (->オリジナル) /