From: Jo-Philipp Wich Date: Sat, 20 Aug 2022 23:32:12 +0000 (+0200) Subject: ucode-mod-lua: support prototype lookups and method calls on ucode values X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=42201e336d53412c63dbfde934786841d7bf29d2;p=project%2Fluci.git 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 --- diff --git a/contrib/package/ucode-mod-lua/src/lua.c b/contrib/package/ucode-mod-lua/src/lua.c index 679425fae5..241f4a92ff 100644 --- a/contrib/package/ucode-mod-lua/src/lua.c +++ b/contrib/package/ucode-mod-lua/src/lua.c @@ -155,30 +155,46 @@ ucv_to_lua(uc_vm_t *vm, uc_value_t *uv, lua_State *L, struct lh_table *visited) case UC_ARRAY: case UC_OBJECT: - if (!visited) { - freetbl = true; - visited = lh_kptr_table_new(16, NULL); + 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) { + freetbl = true; + visited = lh_kptr_table_new(16, NULL); + } - if (visited) { - if (lua_table_new_or_ref(L, visited, uv)) { - if (ucv_type(uv) == UC_ARRAY) { - for (i = 0; i < ucv_array_length(uv); i++) { - e = ucv_array_get(uv, i); - ucv_to_lua(vm, e, L, visited); - lua_rawseti(L, -2, (int)i + 1); + if (visited) { + if (lua_table_new_or_ref(L, visited, uv)) { + if (ucv_type(uv) == UC_ARRAY) { + for (i = 0; i < ucv_array_length(uv); i++) { + e = ucv_array_get(uv, i); + ucv_to_lua(vm, e, L, visited); + lua_rawseti(L, -2, (int)i + 1); + } } - } - else { - ucv_object_foreach(uv, key, val) { - ucv_to_lua(vm, val, L, visited); - lua_setfield(L, -2, key); + else { + ucv_object_foreach(uv, key, val) { + ucv_to_lua(vm, val, L, visited); + lua_setfield(L, -2, key); + } } } } - } - else { - lua_pushnil(L); + else { + lua_pushnil(L); + } } break; @@ -401,17 +417,28 @@ lua_uv_call(lua_State *L) ucv_userdata_t *ud = luaL_checkudata(L, 1, "ucode.value"); int nargs = lua_gettop(L), i; uc_value_t *rv; + lua_Debug ar; + bool mcall; if (!ucv_is_callable(ud->uv)) return luaL_error(L, "%s: Invoked value is not a function", 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)); - 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)); - 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); return luaL_error(L, "%s: %s%s%s", @@ -428,6 +455,26 @@ lua_uv_call(lua_State *L) 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 lua_uv_tostring(lua_State *L) { @@ -443,6 +490,7 @@ lua_uv_tostring(lua_State *L) static const luaL_reg ucode_ud_methods[] = { { "__gc", lua_uv_gc }, { "__call", lua_uv_call }, + { "__index", lua_uv_index }, { "__tostring", lua_uv_tostring }, { } @@ -899,8 +947,6 @@ uc_lua_create(uc_vm_t *vm, size_t nargs) luaL_newmetatable(L, "ucode.value"); luaL_register(L, NULL, ucode_ud_methods); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); lua_pop(L, 1); return uc_resource_new(vm_type, L);