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,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 (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);
|
||||
}
|
||||
}
|
||||
if (ud) {
|
||||
ud->vm = vm;
|
||||
ud->uv = ucv_get(uv);
|
||||
|
||||
luaL_getmetatable(L, "ucode.value");
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ucv_object_foreach(uv, key, val) {
|
||||
ucv_to_lua(vm, val, L, visited);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue