ucode-mod-lua: support prototype lookups and method calls on ucode values
Expose ucode arrays and objects with prototypes as userdata proxy objects to Lua and extend the userdata metadatable with an __index metamethod to lookup not found properties in the ucode values prototype chain. Also extend the __call metamethod implementation to infer method call status from the activation record in order to invoke ucode functions with the correct `this` context when called as method from Lua. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
7e4c087f86
commit
42201e336d
1 changed files with 70 additions and 24 deletions
|
@ -155,6 +155,21 @@ ucv_to_lua(uc_vm_t *vm, uc_value_t *uv, lua_State *L, struct lh_table *visited)
|
||||||
|
|
||||||
case UC_ARRAY:
|
case UC_ARRAY:
|
||||||
case UC_OBJECT:
|
case UC_OBJECT:
|
||||||
|
if (ucv_prototype_get(uv)) {
|
||||||
|
ud = lua_newuserdata(L, sizeof(*ud));
|
||||||
|
|
||||||
|
if (ud) {
|
||||||
|
ud->vm = vm;
|
||||||
|
ud->uv = ucv_get(uv);
|
||||||
|
|
||||||
|
luaL_getmetatable(L, "ucode.value");
|
||||||
|
lua_setmetatable(L, -2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushnil(L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (!visited) {
|
if (!visited) {
|
||||||
freetbl = true;
|
freetbl = true;
|
||||||
visited = lh_kptr_table_new(16, NULL);
|
visited = lh_kptr_table_new(16, NULL);
|
||||||
|
@ -180,6 +195,7 @@ ucv_to_lua(uc_vm_t *vm, uc_value_t *uv, lua_State *L, struct lh_table *visited)
|
||||||
else {
|
else {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -401,17 +417,28 @@ lua_uv_call(lua_State *L)
|
||||||
ucv_userdata_t *ud = luaL_checkudata(L, 1, "ucode.value");
|
ucv_userdata_t *ud = luaL_checkudata(L, 1, "ucode.value");
|
||||||
int nargs = lua_gettop(L), i;
|
int nargs = lua_gettop(L), i;
|
||||||
uc_value_t *rv;
|
uc_value_t *rv;
|
||||||
|
lua_Debug ar;
|
||||||
|
bool mcall;
|
||||||
|
|
||||||
if (!ucv_is_callable(ud->uv))
|
if (!ucv_is_callable(ud->uv))
|
||||||
return luaL_error(L, "%s: Invoked value is not a function",
|
return luaL_error(L, "%s: Invoked value is not a function",
|
||||||
uc_exception_type_name(EXCEPTION_TYPE));
|
uc_exception_type_name(EXCEPTION_TYPE));
|
||||||
|
|
||||||
|
if (!lua_getstack(L, 0, &ar) || !lua_getinfo(L, "n", &ar))
|
||||||
|
return luaL_error(L, "%s: Unable to obtain stackframe information",
|
||||||
|
uc_exception_type_name(EXCEPTION_RUNTIME));
|
||||||
|
|
||||||
|
mcall = !strcmp(ar.namewhat, "method");
|
||||||
|
|
||||||
|
if (mcall)
|
||||||
|
uc_vm_stack_push(ud->vm, lua_to_ucv(L, 2, ud->vm, NULL));
|
||||||
|
|
||||||
uc_vm_stack_push(ud->vm, ucv_get(ud->uv));
|
uc_vm_stack_push(ud->vm, ucv_get(ud->uv));
|
||||||
|
|
||||||
for (i = 2; i <= nargs; i++)
|
for (i = 2 + mcall; i <= nargs; i++)
|
||||||
uc_vm_stack_push(ud->vm, lua_to_ucv(L, i, ud->vm, NULL));
|
uc_vm_stack_push(ud->vm, lua_to_ucv(L, i, ud->vm, NULL));
|
||||||
|
|
||||||
if (uc_vm_call(ud->vm, false, nargs - 1)) {
|
if (uc_vm_call(ud->vm, mcall, nargs - 1 - mcall)) {
|
||||||
rv = ucv_object_get(ucv_array_get(ud->vm->exception.stacktrace, 0), "context", NULL);
|
rv = ucv_object_get(ucv_array_get(ud->vm->exception.stacktrace, 0), "context", NULL);
|
||||||
|
|
||||||
return luaL_error(L, "%s: %s%s%s",
|
return luaL_error(L, "%s: %s%s%s",
|
||||||
|
@ -428,6 +455,26 @@ lua_uv_call(lua_State *L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
lua_uv_index(lua_State *L)
|
||||||
|
{
|
||||||
|
ucv_userdata_t *ud = luaL_checkudata(L, 1, "ucode.value");
|
||||||
|
const char *key = luaL_checkstring(L, 2);
|
||||||
|
uc_value_t *proto, *rv;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
proto = ucv_prototype_get(ud->uv);
|
||||||
|
rv = ucv_object_get(proto, key, &found);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ucv_to_lua(ud->vm, rv, L, NULL);
|
||||||
|
ucv_put(rv);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
lua_uv_tostring(lua_State *L)
|
lua_uv_tostring(lua_State *L)
|
||||||
{
|
{
|
||||||
|
@ -443,6 +490,7 @@ lua_uv_tostring(lua_State *L)
|
||||||
static const luaL_reg ucode_ud_methods[] = {
|
static const luaL_reg ucode_ud_methods[] = {
|
||||||
{ "__gc", lua_uv_gc },
|
{ "__gc", lua_uv_gc },
|
||||||
{ "__call", lua_uv_call },
|
{ "__call", lua_uv_call },
|
||||||
|
{ "__index", lua_uv_index },
|
||||||
{ "__tostring", lua_uv_tostring },
|
{ "__tostring", lua_uv_tostring },
|
||||||
|
|
||||||
{ }
|
{ }
|
||||||
|
@ -899,8 +947,6 @@ uc_lua_create(uc_vm_t *vm, size_t nargs)
|
||||||
|
|
||||||
luaL_newmetatable(L, "ucode.value");
|
luaL_newmetatable(L, "ucode.value");
|
||||||
luaL_register(L, NULL, ucode_ud_methods);
|
luaL_register(L, NULL, ucode_ud_methods);
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setfield(L, -2, "__index");
|
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
return uc_resource_new(vm_type, L);
|
return uc_resource_new(vm_type, L);
|
||||||
|
|
Loading…
Reference in a new issue