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:
parent
d6dbedd9e2
commit
70ff5c3c4a
2 changed files with 988 additions and 0 deletions
31
contrib/package/ucode-mod-lua/Makefile
Normal file
31
contrib/package/ucode-mod-lua/Makefile
Normal 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))
|
957
contrib/package/ucode-mod-lua/src/lua.c
Normal file
957
contrib/package/ucode-mod-lua/src/lua.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue