[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: stack level abstraction [was Re: first class ':']
- From: David Manura <dm.lua@...>
- Date: Thu, 17 Sep 2009 00:49:53 -0400
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