--- /dev/null
+/*
+ * 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);
+}