Merge pull request #5818 from jow-/ucode-mod-lua
contrib: introduce ucode-mod-lua
This commit is contained in:
commit
24723a0064
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