diff options
-rw-r--r-- | contrib/package/ucode-mod-lua/Makefile | 31 | ||||
-rw-r--r-- | contrib/package/ucode-mod-lua/src/lua.c | 957 |
2 files changed, 988 insertions, 0 deletions
diff --git a/contrib/package/ucode-mod-lua/Makefile b/contrib/package/ucode-mod-lua/Makefile new file mode 100644 index 0000000000..a7937925b0 --- /dev/null +++ b/contrib/package/ucode-mod-lua/Makefile @@ -0,0 +1,31 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=ucode-mod-lua +PKG_RELEASE:=1 +PKG_LICENSE:=ISC +PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io> + +include $(INCLUDE_DIR)/package.mk + +define Package/ucode-mod-lua + SECTION:=utils + CATEGORY:=Utilities + TITLE:=ucode to Lua bridge library + DEPENDS:=+libucode +liblua +endef + +define Package/ucode-mod-lua/install + $(INSTALL_DIR) $(1)/usr/lib/ucode + $(CP) $(PKG_BUILD_DIR)/lua.so $(1)/usr/lib/ucode/ +endef + +define Build/Configure +endef + +define Build/Compile + $(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_LDFLAGS) $(FPIC) \ + -Wall -ffunction-sections -Wl,--gc-sections -shared -Wl,--no-as-needed -llua \ + -o $(PKG_BUILD_DIR)/lua.so $(PKG_BUILD_DIR)/lua.c +endef + +$(eval $(call BuildPackage,ucode-mod-lua)) diff --git a/contrib/package/ucode-mod-lua/src/lua.c b/contrib/package/ucode-mod-lua/src/lua.c new file mode 100644 index 0000000000..7a5b9b9612 --- /dev/null +++ b/contrib/package/ucode-mod-lua/src/lua.c @@ -0,0 +1,957 @@ +/* + * Copyright (C) 2022 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> +#include <errno.h> +#include <string.h> +#include <math.h> + +#include "ucode/module.h" + +static uc_resource_type_t *vm_type, *lv_type; + + +typedef struct { + uc_vm_t *vm; + uc_value_t *uv; +} ucv_userdata_t; + +typedef struct { + uc_value_t *uvL; + int ref; +} lua_resource_t; + +static int +lua_uv_gc(lua_State *L) +{ + ucv_userdata_t *ud = luaL_checkudata(L, 1, "ucode.value"); + + ucv_put(ud->uv); + ud->uv = NULL; + + return 0; +} + +static lua_Integer +lua_table_is_arraylike(lua_State *L, int index) +{ + lua_Integer max = 0, count = 0; + lua_Number k; + + lua_pushnil(L); + + /* check for non-integer keys */ + while (lua_next(L, index)) { + if (lua_type(L, -2) == LUA_TNUMBER && (k = lua_tonumber(L, -2)) >= 1) { + if (floor(k) == k) { + if (k > max) + max = k; + + count++; + + lua_pop(L, 1); + + continue; + } + } + + lua_pop(L, 2); + + return -1; + } + + if (max > count * 2) + return -1; + + return max; +} + +static bool +lua_table_new_or_ref(lua_State *L, struct lh_table *visited, uc_value_t *uv) +{ + struct lh_entry *entry; + unsigned long hash; + + hash = lh_get_hash(visited, uv); + entry = lh_table_lookup_entry_w_hash(visited, uv, hash); + + if (!entry) { + lua_newtable(L); + lua_pushvalue(L, -1); + lh_table_insert_w_hash(visited, uv, + (void *)(intptr_t)luaL_ref(L, LUA_REGISTRYINDEX), hash, 0); + + return true; + } + + lua_rawgeti(L, LUA_REGISTRYINDEX, (int)(intptr_t)entry->v); + + return false; +} + +static void +ucv_to_lua(uc_vm_t *vm, uc_value_t *uv, lua_State *L, struct lh_table *visited); + +static void +ucv_to_lua(uc_vm_t *vm, uc_value_t *uv, lua_State *L, struct lh_table *visited) +{ + struct lh_entry *entry; + bool freetbl = false; + lua_resource_t **lv; + ucv_userdata_t *ud; + lua_State **lvL; + uc_value_t *e; + size_t i; + char *s; + + switch (ucv_type(uv)) { + case UC_BOOLEAN: + lua_pushboolean(L, ucv_boolean_get(uv)); + break; + + case UC_STRING: + lua_pushlstring(L, ucv_string_get(uv), ucv_string_length(uv)); + break; + + case UC_DOUBLE: + lua_pushnumber(L, (lua_Number)ucv_double_get(uv)); + break; + + case UC_INTEGER: +#ifdef LUA_TINT + lua_pushinteger(L, (lua_Integer)ucv_int64_get(uv)); +#else + lua_pushnumber(L, (lua_Number)ucv_int64_get(uv)); +#endif + break; + + case UC_REGEXP: + s = ucv_to_string(vm, uv); + + if (s) + lua_pushstring(L, s); + else + lua_pushnil(L); + + free(s); + + break; + + case UC_ARRAY: + case UC_OBJECT: + 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; + + case UC_CFUNCTION: + case UC_CLOSURE: + 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); + } + + break; + + case UC_RESOURCE: + lv = (lua_resource_t **)ucv_resource_dataptr(uv, "lua.value"); + lvL = (lv && *lv) ? (lua_State **)ucv_resource_dataptr((*lv)->uvL, "lua.vm") : NULL; + + if (lvL && *lvL == L) + lua_rawgeti(L, LUA_REGISTRYINDEX, (*lv)->ref); + else + lua_pushnil(L); + + break; + + default: + lua_pushnil(L); + break; + } + + if (freetbl) { + lh_foreach(visited, entry) + luaL_unref(L, LUA_REGISTRYINDEX, (int)(intptr_t)entry->v); + + lh_table_free(visited); + } +} + +static uc_value_t * +ucv_table_new_or_ref(lua_State *L, int index, uc_vm_t *vm, struct lh_table *visited, lua_Integer *nkeys) +{ + struct lh_entry *entry; + unsigned long hash; + const void *tptr; + uc_value_t *uv; + + tptr = lua_topointer(L, index); + hash = lh_get_hash(visited, tptr); + entry = lh_table_lookup_entry_w_hash(visited, tptr, hash); + + if (!entry) { + *nkeys = lua_table_is_arraylike(L, index); + uv = (*nkeys > 0) ? ucv_array_new(vm) : ucv_object_new(vm); + lh_table_insert_w_hash(visited, tptr, uv, hash, 0); + + return uv; + } + + *nkeys = -2; + uv = (uc_value_t *)entry->v; + + return ucv_get(uv); +} + +static uc_value_t * +ucv_this_to_uvL(uc_vm_t *vm) +{ + uc_value_t *ctx = uc_vector_last(&vm->callframes)->ctx; + void *p; + + p = ucv_resource_dataptr(ctx, "lua.vm"); + + if (p) + return ucv_get(ctx); + + p = ucv_resource_dataptr(ctx, "lua.value"); + + if (p) + return ucv_get((*(lua_resource_t **)p)->uvL); + + return NULL; +} + +static uc_value_t * +lua_to_ucv(lua_State *L, int index, uc_vm_t *vm, struct lh_table *visited); + +static uc_value_t * +lua_to_ucv(lua_State *L, int index, uc_vm_t *vm, struct lh_table *visited) +{ + bool freetbl = false; + lua_Integer nkeys, i; + lua_resource_t *lv; + ucv_userdata_t *ud; + const char *key; + uc_value_t *rv; + size_t len; + + switch (lua_type(L, index)) { + case LUA_TNIL: + rv = NULL; + break; + + case LUA_TTABLE: + if (!visited) { + freetbl = true; + visited = lh_kptr_table_new(16, NULL); + } + + rv = ucv_table_new_or_ref(L, index, vm, visited, &nkeys); + + if (nkeys > 0) { + for (i = 1; i <= nkeys; i++) { + lua_rawgeti(L, index, i); + ucv_array_push(rv, lua_to_ucv(L, lua_gettop(L), vm, visited)); + lua_pop(L, 1); + } + } + else if (nkeys == -1) { + lua_pushnil(L); + + while (lua_next(L, index)) { + lua_pushvalue(L, -2); + key = lua_tostring(L, -1); + + if (key) + ucv_object_add(rv, key, lua_to_ucv(L, lua_gettop(L) - 1, vm, visited)); + + lua_pop(L, 2); + } + } + + if (freetbl) + lh_table_free(visited); + + break; + + case LUA_TBOOLEAN: + rv = ucv_boolean_new(lua_toboolean(L, index)); + break; + + case LUA_TNUMBER: +#ifdef LUA_TINT + if (lua_isinteger(L, index)) + rv = ucv_int64_new(lua_tointeger(L, index)); + else + rv = ucv_double_new(lua_tonumber(L, index)); +#else + lua_Number n = lua_tonumber(L, index); + i = lua_tointeger(L, index); + + if ((lua_Number)i == n) + rv = ucv_int64_new(i); + else + rv = ucv_double_new(n); +#endif + + break; + + case LUA_TSTRING: + key = lua_tolstring(L, index, &len); + rv = ucv_string_new_length(key, len); + break; + + case LUA_TUSERDATA: + rv = NULL; + + if (lua_getmetatable(L, index)) { + luaL_getmetatable(L, "ucode.value"); + + if (lua_rawequal(L, -1, -2)) { + ud = lua_touserdata(L, index); + rv = (ud->vm == vm) ? ucv_get(ud->uv) : NULL; + } + + lua_pop(L, 2); + } + + if (rv) + break; + + /* fall through */ + + default: + lua_pushvalue(L, index); + + lv = xalloc(sizeof(*lv)); + lv->ref = luaL_ref(L, LUA_REGISTRYINDEX); + lv->uvL = ucv_this_to_uvL(vm); + + rv = uc_resource_new(lv_type, lv); + break; + } + + return rv; +} + +static const char * +uc_exception_type_name(uc_exception_type_t type) +{ + switch (type) { + case EXCEPTION_SYNTAX: return "Syntax error"; + case EXCEPTION_RUNTIME: return "Runtime error"; + case EXCEPTION_TYPE: return "Type error"; + case EXCEPTION_REFERENCE: return "Reference error"; + case EXCEPTION_EXIT: return "Exit"; + default: return "Exception"; + } +} + +static int +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; + + if (!ucv_is_callable(ud->uv)) + return luaL_error(L, "%s: Invoked value is not a function", + uc_exception_type_name(EXCEPTION_TYPE)); + + uc_vm_stack_push(ud->vm, ucv_get(ud->uv)); + + for (i = 2; 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)) { + rv = ucv_object_get(ucv_array_get(ud->vm->exception.stacktrace, 0), "context", NULL); + + return luaL_error(L, "%s: %s%s%s", + uc_exception_type_name(ud->vm->exception.type), + ud->vm->exception.message, + rv ? "\n" : "", rv ? ucv_string_get(rv) : ""); + } + + rv = uc_vm_stack_pop(ud->vm); + + ucv_to_lua(ud->vm, rv, L, NULL); + ucv_put(rv); + + return 1; +} + +static int +lua_uv_tostring(lua_State *L) +{ + ucv_userdata_t *ud = luaL_checkudata(L, 1, "ucode.value"); + char *s = ucv_to_string(ud->vm, ud->uv); + + lua_pushstring(L, s); + free(s); + + return 1; +} + +static const luaL_reg ucode_ud_methods[] = { + { "__gc", lua_uv_gc }, + { "__call", lua_uv_call }, + { "__tostring", lua_uv_tostring }, + + { } +}; + +static uc_value_t * +uc_lua_vm_claim_result(uc_vm_t *vm, lua_State *L, int oldtop) +{ + int nargs = lua_gettop(L) - oldtop, i; + uc_value_t *uv; + + if (nargs > 1) { + uv = ucv_array_new_length(vm, nargs); + + for (i = 1; i <= nargs; i++) + ucv_array_push(uv, lua_to_ucv(L, oldtop + i, vm, NULL)); + } + else if (nargs == 1) { + uv = lua_to_ucv(L, oldtop + 1, vm, NULL); + } + else { + uv = NULL; + } + + return uv; +} + +static uc_value_t * +uc_lua_vm_pcall(uc_vm_t *vm, lua_State *L, int oldtop) +{ + uc_value_t *uv; + + switch (lua_pcall(L, lua_gettop(L) - oldtop - 1, LUA_MULTRET, 0)) { + case LUA_ERRRUN: + case LUA_ERRMEM: + case LUA_ERRERR: + uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, + "Lua raised runtime exception: %s", + lua_tostring(L, -1)); + + uv = NULL; + break; + + default: + uv = uc_lua_vm_claim_result(vm, L, oldtop); + break; + } + + return uv; +} + +static uc_value_t * +uc_lua_vm_invoke(uc_vm_t *vm, size_t nargs) +{ + lua_State **L = uc_fn_this("lua.vm"); + uc_value_t *name = uc_fn_arg(0); + uc_value_t *uv; + size_t i; + int top; + + if (!L || !*L || ucv_type(name) != UC_STRING) + return NULL; + + top = lua_gettop(*L); + + lua_getglobal(*L, ucv_string_get(name)); + + for (i = 1; i < nargs; i++) { + uv = uc_fn_arg(i); + ucv_to_lua(vm, uv, *L, NULL); + } + + uv = uc_lua_vm_pcall(vm, *L, top); + + lua_settop(*L, top); + + return uv; +} + +static uc_value_t * +uc_lua_vm_eval(uc_vm_t *vm, size_t nargs) +{ + lua_State **L = uc_fn_this("lua.vm"); + uc_value_t *source = uc_fn_arg(0); + uc_value_t *uv = NULL; + int top; + + if (!L || !*L || ucv_type(source) != UC_STRING) + return NULL; + + top = lua_gettop(*L); + + switch (luaL_loadstring(*L, ucv_string_get(source))) { + case LUA_ERRSYNTAX: + uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, + "Syntax error while compiling Lua code: %s", + lua_tostring(*L, -1)); + + break; + + case LUA_ERRMEM: + uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, + "Out of memory while compiling Lua code: %s", + lua_tostring(*L, -1)); + + break; + + default: + uv = uc_lua_vm_pcall(vm, *L, top); + break; + } + + lua_settop(*L, top); + + return uv; +} + +static uc_value_t * +uc_lua_vm_include(uc_vm_t *vm, size_t nargs) +{ + lua_State **L = uc_fn_this("lua.vm"); + uc_value_t *path = uc_fn_arg(0); + uc_value_t *uv = NULL; + int top; + + if (!L || !*L || ucv_type(path) != UC_STRING) + return NULL; + + top = lua_gettop(*L); + + switch (luaL_loadfile(*L, ucv_string_get(path))) { + case LUA_ERRSYNTAX: + uc_vm_raise_exception(vm, EXCEPTION_SYNTAX, + "Syntax error while compiling Lua file: %s", + lua_tostring(*L, -1)); + + break; + + case LUA_ERRFILE: + uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, + "IO error while compiling Lua file: %s", + lua_tostring(*L, -1)); + + break; + + case LUA_ERRMEM: + uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, + "Out of memory while compiling Lua file: %s", + lua_tostring(*L, -1)); + + break; + + default: + uv = uc_lua_vm_pcall(vm, *L, top); + break; + } + + lua_settop(*L, top); + + return uv; +} + +static uc_value_t * +uc_lua_vm_set(uc_vm_t *vm, size_t nargs) +{ + lua_State **L = uc_fn_this("lua.vm"); + uc_value_t *key = uc_fn_arg(0); + uc_value_t *val = uc_fn_arg(1); + + if (!L || !*L) + return NULL; + + if (ucv_type(key) == UC_OBJECT && !val) { + ucv_object_foreach(key, k, v) { + ucv_to_lua(vm, v, *L, NULL); + lua_setglobal(*L, k); + } + } + else if (ucv_type(key) == UC_STRING) { + ucv_to_lua(vm, val, *L, NULL); + lua_setglobal(*L, ucv_string_get(key)); + } + else { + return NULL; + } + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_lua_vm_get(uc_vm_t *vm, size_t nargs) +{ + lua_State **L = uc_fn_this("lua.vm"); + uc_value_t *key = uc_fn_arg(0); + lua_resource_t *lv; + size_t i; + + if (!L || !*L || ucv_type(key) != UC_STRING) + return NULL; + + lua_getglobal(*L, ucv_string_get(key)); + + for (i = 1; i < nargs; i++) { + ucv_to_lua(vm, uc_fn_arg(i), *L, NULL); + lua_gettable(*L, -2); + } + + lv = xalloc(sizeof(*lv)); + lv->ref = luaL_ref(*L, LUA_REGISTRYINDEX); + lv->uvL = ucv_this_to_uvL(vm); + + if (nargs > 1) + lua_pop(*L, nargs - 1); + + return uc_resource_new(lv_type, lv); +} + + +static lua_State * +uc_lua_lv_to_L(lua_resource_t **lv) +{ + lua_State **L; + + if (!lv || !*lv) + return NULL; + + L = (lua_State **)ucv_resource_dataptr((*lv)->uvL, "lua.vm"); + + if (!L) + return NULL; + + return *L; +} + +static uc_value_t * +uc_lua_lv_call(uc_vm_t *vm, size_t nargs) +{ + lua_resource_t **lv = uc_fn_this("lua.value"); + lua_State *L = uc_lua_lv_to_L(lv); + uc_value_t *rv; + int oldtop; + size_t i; + + if (!L) + return NULL; + + oldtop = lua_gettop(L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, (*lv)->ref); + + for (i = 0; i < nargs; i++) + ucv_to_lua(vm, uc_fn_arg(i), L, NULL); + + rv = uc_lua_vm_pcall(vm, L, oldtop); + + lua_settop(L, oldtop); + + return rv; +} + +static uc_value_t * +uc_lua_lv_invoke(uc_vm_t *vm, size_t nargs) +{ + lua_resource_t **lv = uc_fn_this("lua.value"); + lua_State *L = uc_lua_lv_to_L(lv); + uc_value_t *method = uc_fn_arg(0); + uc_value_t *rv; + int oldtop; + size_t i; + + if (!L) + return NULL; + + oldtop = lua_gettop(L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, (*lv)->ref); + ucv_to_lua(vm, method, L, NULL); + lua_gettable(L, -2); + lua_pushvalue(L, -2); + + for (i = 1; i < nargs; i++) + ucv_to_lua(vm, uc_fn_arg(i), L, NULL); + + rv = uc_lua_vm_pcall(vm, L, oldtop + 1); + + lua_settop(L, oldtop); + + return rv; +} + +static uc_value_t * +uc_lua_lv_get_common(uc_vm_t *vm, size_t nargs, bool raw) +{ + lua_resource_t **lv = uc_fn_this("lua.value"), *ref; + lua_State *L = uc_lua_lv_to_L(lv); + uc_value_t *key; + size_t i; + + if (!L) + return NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, (*lv)->ref); + + for (i = 0; i < nargs; i++) { + key = uc_fn_arg(i); + + if (raw) { + if (ucv_type(key) == UC_INTEGER) { + lua_rawgeti(L, -1, (int)ucv_int64_get(key)); + } + else { + ucv_to_lua(vm, key, L, NULL); + lua_rawget(L, -2); + } + } + else { + ucv_to_lua(vm, key, L, NULL); + lua_gettable(L, -2); + } + } + + ref = xalloc(sizeof(*ref)); + ref->ref = luaL_ref(L, LUA_REGISTRYINDEX); + ref->uvL = ucv_this_to_uvL(vm); + + lua_pop(L, nargs); + + return uc_resource_new(lv_type, ref); +} + +static uc_value_t * +uc_lua_lv_get(uc_vm_t *vm, size_t nargs) +{ + return uc_lua_lv_get_common(vm, nargs, false); +} + +static uc_value_t * +uc_lua_lv_getraw(uc_vm_t *vm, size_t nargs) +{ + return uc_lua_lv_get_common(vm, nargs, true); +} + +static uc_value_t * +uc_lua_lv_getmt(uc_vm_t *vm, size_t nargs) +{ + lua_resource_t **lv = uc_fn_this("lua.value"), *ref; + uc_value_t *key = uc_fn_arg(0), *uv = NULL; + lua_State *L = uc_lua_lv_to_L(lv); + int oldtop; + + if (!L || (key && ucv_type(key) != UC_STRING)) + return NULL; + + oldtop = lua_gettop(L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, (*lv)->ref); + + if (lua_getmetatable(L, -1)) { + if (key) + lua_getfield(L, -1, ucv_string_get(key)); + + if (!lua_isnil(L, -1)) { + ref = xalloc(sizeof(*ref)); + ref->ref = luaL_ref(L, LUA_REGISTRYINDEX); + ref->uvL = ucv_this_to_uvL(vm); + + uv = uc_resource_new(lv_type, ref); + } + } + + lua_settop(L, oldtop); + + return uv; +} + +static uc_value_t * +uc_lua_lv_value(uc_vm_t *vm, size_t nargs) +{ + lua_resource_t **lv = uc_fn_this("lua.value"); + lua_State *L = uc_lua_lv_to_L(lv); + uc_value_t *uv; + + if (!L) + return NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, (*lv)->ref); + + uv = lua_to_ucv(L, lua_gettop(L), vm, NULL); + + lua_pop(L, 1); + + return uv; +} + +static uc_value_t * +uc_lua_lv_tostring(uc_vm_t *vm, size_t nargs) +{ + lua_resource_t **lv = uc_fn_this("lua.value"); + lua_State *L = uc_lua_lv_to_L(lv); + uc_value_t *uv = NULL; + uc_stringbuf_t *buf; + const char *s; + size_t len; + + if (!L) + return NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, (*lv)->ref); + + if (luaL_callmeta(L, -1, "__tostring")) { + if (lua_isstring(L, -1)) { + s = lua_tolstring(L, -1, &len); + uv = ucv_string_new_length(s, len); + lua_pop(L, 2); + + return uv; + } + + lua_pop(L, 1); + } + + buf = ucv_stringbuf_new(); + + switch (lua_type(L, lua_gettop(L))) { + case LUA_TNIL: + case LUA_TTABLE: + case LUA_TBOOLEAN: + case LUA_TNUMBER: + case LUA_TSTRING: + uv = lua_to_ucv(L, lua_gettop(L), vm, NULL); + ucv_to_stringbuf(vm, buf, uv, false); + ucv_put(uv); + break; + + default: + ucv_stringbuf_printf(buf, "%s (%p)", + lua_typename(L, lua_type(L, lua_gettop(L))), + lua_topointer(L, lua_gettop(L))); + break; + } + + lua_pop(L, 1); + + return ucv_stringbuf_finish(buf); +} + + +static uc_value_t * +uc_lua_create(uc_vm_t *vm, size_t nargs) +{ + lua_State *L = luaL_newstate(); + + luaL_openlibs(L); + + 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); +} + + +static const uc_function_list_t vm_fns[] = { + { "invoke", uc_lua_vm_invoke }, + { "eval", uc_lua_vm_eval }, + { "include", uc_lua_vm_include }, + { "set", uc_lua_vm_set }, + { "get", uc_lua_vm_get }, +}; + +static const uc_function_list_t lv_fns[] = { + { "call", uc_lua_lv_call }, + { "invoke", uc_lua_lv_invoke }, + { "get", uc_lua_lv_get }, + { "getraw", uc_lua_lv_getraw }, + { "getmt", uc_lua_lv_getmt }, + { "value", uc_lua_lv_value }, + { "tostring", uc_lua_lv_tostring }, +}; + +static const uc_function_list_t lua_fns[] = { + { "create", uc_lua_create }, +}; + +static void +free_vm(void *ud) +{ + lua_State *L = ud; + + if (L) + lua_close(L); +} + +static void +free_lv(void *ud) +{ + lua_resource_t *lv = ud; + lua_State **L = (lua_State **)ucv_resource_dataptr(lv->uvL, "lua.vm"); + + luaL_unref(*L, LUA_REGISTRYINDEX, lv->ref); + ucv_put(lv->uvL); + free(lv); +} + +void uc_module_init(uc_vm_t *vm, uc_value_t *scope) +{ + uc_function_list_register(scope, lua_fns); + + vm_type = uc_type_declare(vm, "lua.vm", vm_fns, free_vm); + lv_type = uc_type_declare(vm, "lua.value", lv_fns, free_lv); +} |