[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Strange way to determine whether it is Lua or C function
- From: Viacheslav Usov <via.usov@...>
- Date: Sun, 23 Aug 2020 17:26:25 +0200
On Sun, Aug 23, 2020 at 8:45 AM Egor Skriptunoff
<egor.skriptunoff@gmail.com> wrote:
> local function is_C_function(func)
> local x = coroutine.create(func)
> debug.sethook(x, function() x=debug.getlocal(2,2) coroutine.yield() end, "c")
> coroutine.resume(x)
> return not x
> end
The first argument to getlocal() selects the function that is the
subject of the test.
This is so because the "c" hook is invoked before the function is
called, yet the context (stack frame) of the function is already
established. So its stack frame is number 2.
Despite its name, getlocal() may return a register in the specified
stack frame. In a Lua function, there is always enough stack for two
registers, that is why getlocal(*, 2) will always return something for
a Lua function. This is assured by initializing the 'maxstacksize'
field of the function's "prototype" with 2. Why this is so, I do not
know.
On the other hand, for a C function, getlocal() for a non-zero level
only looks through the function's actual stack frame. It might seem
that it should be empty, because is_C_function() passes no arguments
to it, and the hook is called before the function itself has a chance
to execute; however, the invocation of a Lua hook function leaves an
artifact on the hooked function's stack frame, which is the hook
table. So getlocal(*, 1) would return something in any case, while
getlocal(>0, 2) returns nil for a C function called without arguments,
and which itself pushes nothing on the stack.
The latest condition is "naturally" ensured by the "c" hook.
The coroutine business ensures that the test subjects never actually run.
So the distinction is made by using an interplay of two implementation
details that are not intrinsically connected; moreover, at least for
one of them there is no really solid reason why it is so. Very
fragile.
Given that this technique already requires the debug library, I'd say
there is a far more regular and simpler way to achieve the goal.
Cheers,
V.