contrib: introduce ucode-mod-lua

The ucode-mod-lua library provides an ucode-to-Lua bridge and a set of
functions to instantiate Lua VMs, invoke Lua functions as well as
exchanging data structures between ucode and Lua.

Example usage:

    #!/usr/bin/ucode

    'use strict';

    const lua = require("lua");

    let vm = lua.create();

    vm.set({
    	hello: function(...args) {
    		print(`A ucode "Hello world" function called from Lua! Got arguments: ${args}\n`);
    	},

    	data_from_ucode: {
    		bool: true,
    		float: 1.3,
    		int: 0x11223344,
    		string: "Hello from ucode!",
    		array: [ 1, 2, 3, null, 5 ],
    		object: {
    			apple: "green",
    			banana: "yellow",
    			[5]: "foo",
    			[-1]: null,
    			nested: {
    				a: [ 5, 6 ],
    				b: { c: NaN }
    			}
    		},
    		regexp: /foo/
    	}
    });

    vm.invoke("hello", true, 123, "Foo");
    vm.eval('print("Print from Lua!", data_from_ucode.int * data_from_ucode.float);');

    try {
    	vm.invoke("error", "Throwing a Lua exception...");
    }
    catch (e) {
    	print(`Caught exception: ${e}\n`);
    }

    print(`Lua VM version is: ${vm.get('_G', '_VERSION').value()}\n`);

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2022-05-19 17:10:17 +02:00
parent d6dbedd9e2
commit 70ff5c3c4a
2 changed files with 988 additions and 0 deletions

View file

@ -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))

View file

@ -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);
}