debug.getparams - Get the names of a function's arguments
27 replies, posted
ChewGum was asking if this was possible. I promptly replied no, but then thought again.
This was the result:
[lua]
function debug.getparams(f)
local co = coroutine.create(f)
local params = {}
debug.sethook(co, function()
local i, k = 1, debug.getlocal(co, 2, 1)
while k do
if k ~= "(*temporary)" then
table.insert(params, k)
end
i = i+1
k = debug.getlocal(co, 2, i)
end
error("~~end~~")
end, "c")
local res, err = coroutine.resume(co)
if res then
error("The function provided defies the laws of the universe.", 2)
elseif string.sub(tostring(err), -7) ~= "~~end~~" then
error("The function failed with the error: "..tostring(err), 2)
end
return params
end
[/lua]
Example usage:
[lua]
function f(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,...)
print(x+1,y,z) -- this proves that the code within the function does not run
end
for k,v in pairs(debug.getparams(f)) do
print("l:", k, v)
end
--[[ Output:
l: 1 a
l: 2 b
l: 3 c
l: 4 d
l: 5 e
l: 6 f
l: 7 g
l: 8 h
l: 9 i
l: 10 j
l: 11 k
l: 12 l
l: 13 m
l: 14 n
l: 15 o
l: 16 p
l: 17 q
l: 18 r
l: 19 s
l: 20 t
l: 21 u
l: 22 v
l: 23 w
l: 24 x
l: 25 y
l: 26 z
l: 27 arg
]]
[/lua]
Or just a single liner:
[lua]
print(table.concat(debug.getparams(f), ", "))
[/lua]
Handy override of print:
[lua]
do
local oprint = print
function print(...)
local args = {...}
for i = 1, #args do
local arg = args[i]
if type(arg) == "function" then
local name = debug.getinfo(arg).name
args[i] = (name and name ~= "" and name or tostring(arg)).."("..table.concat(debug.getparams(arg), ",")..")"
end
end
oprint(unpack(args))
end
end
[/lua]
Please inform me of any bugs.
Thanks to ChewGum for the inspiration and annoyance.
Yay for the [url=http://www.lua.org/manual/5.1/manual.html#5.9]Lua Manual[/url].
Enjoy!
Very nice, I really love that. :D
Clever. But "defies", not "defys".
[QUOTE=mahalis;19844971]Clever. But "defies", not "defys".[/QUOTE]
What are you talking about :ninja:
[quote=Wiki]Don't bother actually, Garry broke them.[/quote]
I take it Garry fixed them. What exactly is the point of using one, though?
[QUOTE=Entoros;19846936]I take it Garry fixed them. What exactly is the point of using one, though?[/QUOTE]
what
Wrong thread?
Ohshit, I just realized it sound like I have no clue what I was talking about. :downswords: I was talking about coroutines.
Well, they seem to work perfectly with this.
Bravo.
This is really cool.
This seems to work on some, but not others:
[code]
] lua_run_cl print(table.concat(debug.getparams(PrintTable), ", "))
t, indent, done
] lua_run_cl print(table.concat(debug.getparams(Derma_StringRequest), ", "))
:17: bad argument #1 to 'resume' (Vector expected, got thread)
[/code]
[b]Edit:[/b] And now it works perfectly. I bet that as soon as I stop debugging it and actually try to use it, it'll error again.
can't do inlines.
[lua]function snib(f)
print(table.concat(debug.getparams(f), ", "))
end
snib(function(a,b,c) end);
--
print(table.concat(debug.getparams(function(a, waffke, plib) end), ", "))
[/lua]
both print an empty line.
[code]
> print(table.concat(debug.getparams(function(a,b,c) end), ", "))
> print(table.concat(debug.getparams(function(a,b,c) x=y end), ", "))
a, b, c
[/code]
It appears Lua doesn't bother with the localising of the arguments if the function has no code.
This is awesome :D
Thanks to all for testing this.
I hope it makes someone's life easier.
I optimised it a slight bit, even though it won't make much of a difference.
If anyone has an idea for another debug function they'd like, please tell.
I'm confused, I thought coroutines were broke. :l
Can you lighten my on this subject?
Oh man, this is going to be extremely useful. I could see it being used for hacks.. Not that i will or ever have used them :ninja:
But seriously, epic man, epic.
Give me one example for which this could be used for a hack. All of this info is publically accessable anyhow, you can just find it with a grep of your dua files :l
Well, it would save th whole trawl through the Dua files (or the search), among other things.
A very cool function Deco, I can't think of any uses for it right now though.
Sorry for the bump, but this seems to be the most relavent search result for my query: Could this be modified by someone to output the number and type of arguments a function takes? I ask for the sake of filling in [b][url=http://wiki.garrysmod.com/?title=Special:WantedPages]wanted pages[img]http://wiki.garrysmod.com/favicon.ico[/img][/url][/b] at [b][url=http://wiki.garrysmod.com/]Garry's Mod Wiki [img]http://wiki.garrysmod.com/favicon.ico[/img][/url][/b]
Nope, it only works on lua functions too, the problem currently is that garry broke courtines pretty badly(they mess up randomly)
[QUOTE=KnowledgeJunkie;23541710]Sorry for the bump, but this seems to be the most relavent search result for my query: Could this be modified by someone to output the number and type of arguments a function takes? I ask for the sake of filling in [b][url=http://wiki.garrysmod.com/?title=Special:WantedPages]wanted pages[img]http://wiki.garrysmod.com/favicon.ico[/img][/url][/b] at [b][url=http://wiki.garrysmod.com/]Garry's Mod Wiki [img]http://wiki.garrysmod.com/favicon.ico[/img][/url][/b][/QUOTE]
There is no way of determining the type of arguments, because that isn't defined anywhere.
This is amazing. My face exploded from how awesome this snippet is.
But don't worry, I'll concatenate it.
From my understandings it creates a new thread so if it infinity loops you won't freeze it retrieves the locals stores them in a table and later it just returns them.
A coroutine is a new Lua state. Creating a coroutine in Lua is like creating another thread, as in they both have the same global state, but they cannot be run concurrently. Instead, the master state controls the operation of the substate (in reality, though, it can be done either way round).
I create a coroutine out of the function, not only so it is in a contained environment, but so I can use debug.sethook on it without interference.
The hook provided to debug.sethook is called on the event "Call", that is, whenever Lua enters the code of a function.
When the coroutine starts, it prepares the arguments to the function as locals, prepares the temporary data needed to run the function, and it [i]would[i] then run the function, but first the hook is called.
In that hook, after gathering the data it needs using debug.getlocals, I force the coroutine to error out with a unique error message.
And, tada. Params table is filled.
[b]Edit:[/b]
This is a pretty complex thing to be trying to understand if you're new at Lua.
It took me alot of work with Lua (not high-level Lua, like most GMod coders use) to understand some of the semantics involved.
Spontaneous bump!
[url=http://www.facepunch.com/member.php?u=130371]_Undefined[/url] requested – via PM – for a version of this that works with LuaJIT 2.0 .
Well, here you go:
[lua]
function debug.getparamnames(func)
local func_paramInfoDict = debug.getinfo(func, 'u')
local func_paramCount = func_paramInfoDict.nparams
local func_isVarArg = func_paramInfoDict.isvararg
local paramNameList = {}
for i = 1, func_paramCount do
local param_name, param_value = debug.getlocal(func, i)
paramNameList[i] = param_name
end
return paramNameList, func_isVarArg
end
[/lua]
[lua]
do local paramNameList, isVarArg = debug.getparamnames(
function() end
)
print( "func("
.. table.concat(paramNameList, ", ")
.. (isVarArg and ((#paramNameList > 0 and ", " or "").."...") or "")
.. ")"
)
assert(#paramNameList == 0)
assert(not isVarArg)
end
do local paramNameList, isVarArg = debug.getparamnames(
function(a,b,c) end
)
print( "func("
.. table.concat(paramNameList, ", ")
.. (isVarArg and ((#paramNameList > 0 and ", " or "").."...") or "")
.. ")"
)
assert( #paramNameList == 3
and paramNameList[1] == 'a'
and paramNameList[2] == 'b'
and paramNameList[3] == 'c'
)
assert(not isVarArg)
end
do local paramNameList, isVarArg = debug.getparamnames(
function(a,b,c,...) end
)
print( "func("
.. table.concat(paramNameList, ", ")
.. (isVarArg and ((#paramNameList > 0 and ", " or "").."...") or "")
.. ")"
)
assert( #paramNameList == 3
and paramNameList[1] == 'a'
and paramNameList[2] == 'b'
and paramNameList[3] == 'c'
)
assert(isVarArg)
end
do local paramNameList, isVarArg = debug.getparamnames(
function(...) end
)
print( "func("
.. table.concat(paramNameList, ", ")
.. (isVarArg and ((#paramNameList > 0 and ", " or "").."...") or "")
.. ")"
)
assert(#paramNameList == 0)
assert(isVarArg)
end
[/lua]
The new [i]'u'[/i] option accepted by [i]debug.getinfo[/i], which offers [i]nparams[/i] and [i]isvararg[/i], allows this to be possible without needing a coroutine (which means no more unreliability!).
I hope this is useful.
Sorry, you need to Log In to post a reply to this thread.