2 * Copyright (C) 2022 Jo-Philipp Wich <jo@mein.io>
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 #include "ucode/module.h"
27 static uc_resource_type_t
*vm_type
, *lv_type
;
41 lua_uv_gc(lua_State
*L
)
43 ucv_userdata_t
*ud
= luaL_checkudata(L
, 1, "ucode.value");
52 lua_table_is_arraylike(lua_State
*L
, int index
)
54 lua_Integer max
= 0, count
= 0;
59 /* check for non-integer keys */
60 while (lua_next(L
, index
)) {
61 if (lua_type(L
, -2) == LUA_TNUMBER
&& (k
= lua_tonumber(L
, -2)) >= 1) {
86 lua_table_new_or_ref(lua_State
*L
, struct lh_table
*visited
, uc_value_t
*uv
)
88 struct lh_entry
*entry
;
91 hash
= lh_get_hash(visited
, uv
);
92 entry
= lh_table_lookup_entry_w_hash(visited
, uv
, hash
);
97 lh_table_insert_w_hash(visited
, uv
,
98 (void *)(intptr_t)luaL_ref(L
, LUA_REGISTRYINDEX
), hash
, 0);
103 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (int)(intptr_t)entry
->v
);
109 ucv_to_lua(uc_vm_t
*vm
, uc_value_t
*uv
, lua_State
*L
, struct lh_table
*visited
);
112 ucv_to_lua(uc_vm_t
*vm
, uc_value_t
*uv
, lua_State
*L
, struct lh_table
*visited
)
114 struct lh_entry
*entry
;
115 bool freetbl
= false;
123 switch (ucv_type(uv
)) {
125 lua_pushboolean(L
, ucv_boolean_get(uv
));
129 lua_pushlstring(L
, ucv_string_get(uv
), ucv_string_length(uv
));
133 lua_pushnumber(L
, (lua_Number
)ucv_double_get(uv
));
138 lua_pushinteger(L
, (lua_Integer
)ucv_int64_get(uv
));
140 lua_pushnumber(L
, (lua_Number
)ucv_int64_get(uv
));
145 s
= ucv_to_string(vm
, uv
);
148 lua_pushstring(L
, s
);
160 visited
= lh_kptr_table_new(16, NULL
);
164 if (lua_table_new_or_ref(L
, visited
, uv
)) {
165 if (ucv_type(uv
) == UC_ARRAY
) {
166 for (i
= 0; i
< ucv_array_length(uv
); i
++) {
167 e
= ucv_array_get(uv
, i
);
168 ucv_to_lua(vm
, e
, L
, visited
);
169 lua_rawseti(L
, -2, (int)i
+ 1);
173 ucv_object_foreach(uv
, key
, val
) {
174 ucv_to_lua(vm
, val
, L
, visited
);
175 lua_setfield(L
, -2, key
);
188 ud
= lua_newuserdata(L
, sizeof(*ud
));
192 ud
->uv
= ucv_get(uv
);
194 luaL_getmetatable(L
, "ucode.value");
195 lua_setmetatable(L
, -2);
204 lv
= (lua_resource_t
**)ucv_resource_dataptr(uv
, "lua.value");
205 lvL
= (lv
&& *lv
) ? (lua_State
**)ucv_resource_dataptr((*lv
)->uvL
, "lua.vm") : NULL
;
207 if (lvL
&& *lvL
== L
)
208 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*lv
)->ref
);
220 lh_foreach(visited
, entry
)
221 luaL_unref(L
, LUA_REGISTRYINDEX
, (int)(intptr_t)entry
->v
);
223 lh_table_free(visited
);
228 ucv_table_new_or_ref(lua_State
*L
, int index
, uc_vm_t
*vm
, struct lh_table
*visited
, lua_Integer
*nkeys
)
230 struct lh_entry
*entry
;
235 tptr
= lua_topointer(L
, index
);
236 hash
= lh_get_hash(visited
, tptr
);
237 entry
= lh_table_lookup_entry_w_hash(visited
, tptr
, hash
);
240 *nkeys
= lua_table_is_arraylike(L
, index
);
241 uv
= (*nkeys
> 0) ? ucv_array_new(vm
) : ucv_object_new(vm
);
242 lh_table_insert_w_hash(visited
, tptr
, uv
, hash
, 0);
248 uv
= (uc_value_t
*)entry
->v
;
254 ucv_this_to_uvL(uc_vm_t
*vm
)
256 uc_value_t
*ctx
= uc_vector_last(&vm
->callframes
)->ctx
;
259 p
= ucv_resource_dataptr(ctx
, "lua.vm");
264 p
= ucv_resource_dataptr(ctx
, "lua.value");
267 return ucv_get((*(lua_resource_t
**)p
)->uvL
);
273 lua_to_ucv(lua_State
*L
, int index
, uc_vm_t
*vm
, struct lh_table
*visited
);
276 lua_to_ucv(lua_State
*L
, int index
, uc_vm_t
*vm
, struct lh_table
*visited
)
278 bool freetbl
= false;
279 lua_Integer nkeys
, i
;
286 switch (lua_type(L
, index
)) {
294 visited
= lh_kptr_table_new(16, NULL
);
297 rv
= ucv_table_new_or_ref(L
, index
, vm
, visited
, &nkeys
);
300 for (i
= 1; i
<= nkeys
; i
++) {
301 lua_rawgeti(L
, index
, i
);
302 ucv_array_push(rv
, lua_to_ucv(L
, lua_gettop(L
), vm
, visited
));
306 else if (nkeys
== -1) {
309 while (lua_next(L
, index
)) {
310 lua_pushvalue(L
, -2);
311 key
= lua_tostring(L
, -1);
314 ucv_object_add(rv
, key
, lua_to_ucv(L
, lua_gettop(L
) - 1, vm
, visited
));
321 lh_table_free(visited
);
326 rv
= ucv_boolean_new(lua_toboolean(L
, index
));
331 if (lua_isinteger(L
, index
))
332 rv
= ucv_int64_new(lua_tointeger(L
, index
));
334 rv
= ucv_double_new(lua_tonumber(L
, index
));
336 lua_Number n
= lua_tonumber(L
, index
);
337 i
= lua_tointeger(L
, index
);
339 if ((lua_Number
)i
== n
)
340 rv
= ucv_int64_new(i
);
342 rv
= ucv_double_new(n
);
348 key
= lua_tolstring(L
, index
, &len
);
349 rv
= ucv_string_new_length(key
, len
);
355 if (lua_getmetatable(L
, index
)) {
356 luaL_getmetatable(L
, "ucode.value");
358 if (lua_rawequal(L
, -1, -2)) {
359 ud
= lua_touserdata(L
, index
);
360 rv
= (ud
->vm
== vm
) ? ucv_get(ud
->uv
) : NULL
;
372 lua_pushvalue(L
, index
);
374 lv
= xalloc(sizeof(*lv
));
375 lv
->ref
= luaL_ref(L
, LUA_REGISTRYINDEX
);
376 lv
->uvL
= ucv_this_to_uvL(vm
);
378 rv
= uc_resource_new(lv_type
, lv
);
386 uc_exception_type_name(uc_exception_type_t type
)
389 case EXCEPTION_SYNTAX
: return "Syntax error";
390 case EXCEPTION_RUNTIME
: return "Runtime error";
391 case EXCEPTION_TYPE
: return "Type error";
392 case EXCEPTION_REFERENCE
: return "Reference error";
393 case EXCEPTION_EXIT
: return "Exit";
394 default: return "Exception";
399 lua_uv_call(lua_State
*L
)
401 ucv_userdata_t
*ud
= luaL_checkudata(L
, 1, "ucode.value");
402 int nargs
= lua_gettop(L
), i
;
405 if (!ucv_is_callable(ud
->uv
))
406 return luaL_error(L
, "%s: Invoked value is not a function",
407 uc_exception_type_name(EXCEPTION_TYPE
));
409 uc_vm_stack_push(ud
->vm
, ucv_get(ud
->uv
));
411 for (i
= 2; i
<= nargs
; i
++)
412 uc_vm_stack_push(ud
->vm
, lua_to_ucv(L
, i
, ud
->vm
, NULL
));
414 if (uc_vm_call(ud
->vm
, false, nargs
- 1)) {
415 rv
= ucv_object_get(ucv_array_get(ud
->vm
->exception
.stacktrace
, 0), "context", NULL
);
417 return luaL_error(L
, "%s: %s%s%s",
418 uc_exception_type_name(ud
->vm
->exception
.type
),
419 ud
->vm
->exception
.message
,
420 rv
? "\n" : "", rv
? ucv_string_get(rv
) : "");
423 rv
= uc_vm_stack_pop(ud
->vm
);
425 ucv_to_lua(ud
->vm
, rv
, L
, NULL
);
432 lua_uv_tostring(lua_State
*L
)
434 ucv_userdata_t
*ud
= luaL_checkudata(L
, 1, "ucode.value");
435 char *s
= ucv_to_string(ud
->vm
, ud
->uv
);
437 lua_pushstring(L
, s
);
443 static const luaL_reg ucode_ud_methods
[] = {
444 { "__gc", lua_uv_gc
},
445 { "__call", lua_uv_call
},
446 { "__tostring", lua_uv_tostring
},
452 uc_lua_vm_claim_result(uc_vm_t
*vm
, lua_State
*L
, int oldtop
)
454 int nargs
= lua_gettop(L
) - oldtop
, i
;
458 uv
= ucv_array_new_length(vm
, nargs
);
460 for (i
= 1; i
<= nargs
; i
++)
461 ucv_array_push(uv
, lua_to_ucv(L
, oldtop
+ i
, vm
, NULL
));
463 else if (nargs
== 1) {
464 uv
= lua_to_ucv(L
, oldtop
+ 1, vm
, NULL
);
474 uc_lua_vm_pcall(uc_vm_t
*vm
, lua_State
*L
, int oldtop
)
478 switch (lua_pcall(L
, lua_gettop(L
) - oldtop
- 1, LUA_MULTRET
, 0)) {
482 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
483 "Lua raised runtime exception: %s",
484 lua_tostring(L
, -1));
490 uv
= uc_lua_vm_claim_result(vm
, L
, oldtop
);
498 uc_lua_vm_invoke(uc_vm_t
*vm
, size_t nargs
)
500 lua_State
**L
= uc_fn_this("lua.vm");
501 uc_value_t
*name
= uc_fn_arg(0);
506 if (!L
|| !*L
|| ucv_type(name
) != UC_STRING
)
509 top
= lua_gettop(*L
);
511 lua_getglobal(*L
, ucv_string_get(name
));
513 for (i
= 1; i
< nargs
; i
++) {
515 ucv_to_lua(vm
, uv
, *L
, NULL
);
518 uv
= uc_lua_vm_pcall(vm
, *L
, top
);
526 uc_lua_vm_eval(uc_vm_t
*vm
, size_t nargs
)
528 lua_State
**L
= uc_fn_this("lua.vm");
529 uc_value_t
*source
= uc_fn_arg(0);
530 uc_value_t
*uv
= NULL
;
533 if (!L
|| !*L
|| ucv_type(source
) != UC_STRING
)
536 top
= lua_gettop(*L
);
538 switch (luaL_loadstring(*L
, ucv_string_get(source
))) {
540 uc_vm_raise_exception(vm
, EXCEPTION_SYNTAX
,
541 "Syntax error while compiling Lua code: %s",
542 lua_tostring(*L
, -1));
547 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
548 "Out of memory while compiling Lua code: %s",
549 lua_tostring(*L
, -1));
554 uv
= uc_lua_vm_pcall(vm
, *L
, top
);
564 uc_lua_vm_include(uc_vm_t
*vm
, size_t nargs
)
566 lua_State
**L
= uc_fn_this("lua.vm");
567 uc_value_t
*path
= uc_fn_arg(0);
568 uc_value_t
*uv
= NULL
;
571 if (!L
|| !*L
|| ucv_type(path
) != UC_STRING
)
574 top
= lua_gettop(*L
);
576 switch (luaL_loadfile(*L
, ucv_string_get(path
))) {
578 uc_vm_raise_exception(vm
, EXCEPTION_SYNTAX
,
579 "Syntax error while compiling Lua file: %s",
580 lua_tostring(*L
, -1));
585 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
586 "IO error while compiling Lua file: %s",
587 lua_tostring(*L
, -1));
592 uc_vm_raise_exception(vm
, EXCEPTION_RUNTIME
,
593 "Out of memory while compiling Lua file: %s",
594 lua_tostring(*L
, -1));
599 uv
= uc_lua_vm_pcall(vm
, *L
, top
);
609 uc_lua_vm_set(uc_vm_t
*vm
, size_t nargs
)
611 lua_State
**L
= uc_fn_this("lua.vm");
612 uc_value_t
*key
= uc_fn_arg(0);
613 uc_value_t
*val
= uc_fn_arg(1);
618 if (ucv_type(key
) == UC_OBJECT
&& !val
) {
619 ucv_object_foreach(key
, k
, v
) {
620 ucv_to_lua(vm
, v
, *L
, NULL
);
621 lua_setglobal(*L
, k
);
624 else if (ucv_type(key
) == UC_STRING
) {
625 ucv_to_lua(vm
, val
, *L
, NULL
);
626 lua_setglobal(*L
, ucv_string_get(key
));
632 return ucv_boolean_new(true);
636 uc_lua_vm_get(uc_vm_t
*vm
, size_t nargs
)
638 lua_State
**L
= uc_fn_this("lua.vm");
639 uc_value_t
*key
= uc_fn_arg(0);
643 if (!L
|| !*L
|| ucv_type(key
) != UC_STRING
)
646 lua_getglobal(*L
, ucv_string_get(key
));
648 for (i
= 1; i
< nargs
; i
++) {
649 ucv_to_lua(vm
, uc_fn_arg(i
), *L
, NULL
);
650 lua_gettable(*L
, -2);
653 lv
= xalloc(sizeof(*lv
));
654 lv
->ref
= luaL_ref(*L
, LUA_REGISTRYINDEX
);
655 lv
->uvL
= ucv_this_to_uvL(vm
);
658 lua_pop(*L
, nargs
- 1);
660 return uc_resource_new(lv_type
, lv
);
665 uc_lua_lv_to_L(lua_resource_t
**lv
)
672 L
= (lua_State
**)ucv_resource_dataptr((*lv
)->uvL
, "lua.vm");
681 uc_lua_lv_call(uc_vm_t
*vm
, size_t nargs
)
683 lua_resource_t
**lv
= uc_fn_this("lua.value");
684 lua_State
*L
= uc_lua_lv_to_L(lv
);
692 oldtop
= lua_gettop(L
);
694 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*lv
)->ref
);
696 for (i
= 0; i
< nargs
; i
++)
697 ucv_to_lua(vm
, uc_fn_arg(i
), L
, NULL
);
699 rv
= uc_lua_vm_pcall(vm
, L
, oldtop
);
701 lua_settop(L
, oldtop
);
707 uc_lua_lv_invoke(uc_vm_t
*vm
, size_t nargs
)
709 lua_resource_t
**lv
= uc_fn_this("lua.value");
710 lua_State
*L
= uc_lua_lv_to_L(lv
);
711 uc_value_t
*method
= uc_fn_arg(0);
719 oldtop
= lua_gettop(L
);
721 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*lv
)->ref
);
722 ucv_to_lua(vm
, method
, L
, NULL
);
724 lua_pushvalue(L
, -2);
726 for (i
= 1; i
< nargs
; i
++)
727 ucv_to_lua(vm
, uc_fn_arg(i
), L
, NULL
);
729 rv
= uc_lua_vm_pcall(vm
, L
, oldtop
+ 1);
731 lua_settop(L
, oldtop
);
737 uc_lua_lv_get_common(uc_vm_t
*vm
, size_t nargs
, bool raw
)
739 lua_resource_t
**lv
= uc_fn_this("lua.value"), *ref
;
740 lua_State
*L
= uc_lua_lv_to_L(lv
);
747 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*lv
)->ref
);
749 for (i
= 0; i
< nargs
; i
++) {
753 if (ucv_type(key
) == UC_INTEGER
) {
754 lua_rawgeti(L
, -1, (int)ucv_int64_get(key
));
757 ucv_to_lua(vm
, key
, L
, NULL
);
762 ucv_to_lua(vm
, key
, L
, NULL
);
767 ref
= xalloc(sizeof(*ref
));
768 ref
->ref
= luaL_ref(L
, LUA_REGISTRYINDEX
);
769 ref
->uvL
= ucv_this_to_uvL(vm
);
773 return uc_resource_new(lv_type
, ref
);
777 uc_lua_lv_get(uc_vm_t
*vm
, size_t nargs
)
779 return uc_lua_lv_get_common(vm
, nargs
, false);
783 uc_lua_lv_getraw(uc_vm_t
*vm
, size_t nargs
)
785 return uc_lua_lv_get_common(vm
, nargs
, true);
789 uc_lua_lv_getmt(uc_vm_t
*vm
, size_t nargs
)
791 lua_resource_t
**lv
= uc_fn_this("lua.value"), *ref
;
792 uc_value_t
*key
= uc_fn_arg(0), *uv
= NULL
;
793 lua_State
*L
= uc_lua_lv_to_L(lv
);
796 if (!L
|| (key
&& ucv_type(key
) != UC_STRING
))
799 oldtop
= lua_gettop(L
);
801 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*lv
)->ref
);
803 if (lua_getmetatable(L
, -1)) {
805 lua_getfield(L
, -1, ucv_string_get(key
));
807 if (!lua_isnil(L
, -1)) {
808 ref
= xalloc(sizeof(*ref
));
809 ref
->ref
= luaL_ref(L
, LUA_REGISTRYINDEX
);
810 ref
->uvL
= ucv_this_to_uvL(vm
);
812 uv
= uc_resource_new(lv_type
, ref
);
816 lua_settop(L
, oldtop
);
822 uc_lua_lv_value(uc_vm_t
*vm
, size_t nargs
)
824 lua_resource_t
**lv
= uc_fn_this("lua.value");
825 lua_State
*L
= uc_lua_lv_to_L(lv
);
831 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*lv
)->ref
);
833 uv
= lua_to_ucv(L
, lua_gettop(L
), vm
, NULL
);
841 uc_lua_lv_tostring(uc_vm_t
*vm
, size_t nargs
)
843 lua_resource_t
**lv
= uc_fn_this("lua.value");
844 lua_State
*L
= uc_lua_lv_to_L(lv
);
845 uc_value_t
*uv
= NULL
;
853 lua_rawgeti(L
, LUA_REGISTRYINDEX
, (*lv
)->ref
);
855 if (luaL_callmeta(L
, -1, "__tostring")) {
856 if (lua_isstring(L
, -1)) {
857 s
= lua_tolstring(L
, -1, &len
);
858 uv
= ucv_string_new_length(s
, len
);
867 buf
= ucv_stringbuf_new();
869 switch (lua_type(L
, lua_gettop(L
))) {
875 uv
= lua_to_ucv(L
, lua_gettop(L
), vm
, NULL
);
876 ucv_to_stringbuf(vm
, buf
, uv
, false);
881 ucv_stringbuf_printf(buf
, "%s (%p)",
882 lua_typename(L
, lua_type(L
, lua_gettop(L
))),
883 lua_topointer(L
, lua_gettop(L
)));
889 return ucv_stringbuf_finish(buf
);
894 uc_lua_create(uc_vm_t
*vm
, size_t nargs
)
896 lua_State
*L
= luaL_newstate();
900 luaL_newmetatable(L
, "ucode.value");
901 luaL_register(L
, NULL
, ucode_ud_methods
);
902 lua_pushvalue(L
, -1);
903 lua_setfield(L
, -2, "__index");
906 return uc_resource_new(vm_type
, L
);
910 static const uc_function_list_t vm_fns
[] = {
911 { "invoke", uc_lua_vm_invoke
},
912 { "eval", uc_lua_vm_eval
},
913 { "include", uc_lua_vm_include
},
914 { "set", uc_lua_vm_set
},
915 { "get", uc_lua_vm_get
},
918 static const uc_function_list_t lv_fns
[] = {
919 { "call", uc_lua_lv_call
},
920 { "invoke", uc_lua_lv_invoke
},
921 { "get", uc_lua_lv_get
},
922 { "getraw", uc_lua_lv_getraw
},
923 { "getmt", uc_lua_lv_getmt
},
924 { "value", uc_lua_lv_value
},
925 { "tostring", uc_lua_lv_tostring
},
928 static const uc_function_list_t lua_fns
[] = {
929 { "create", uc_lua_create
},
944 lua_resource_t
*lv
= ud
;
945 lua_State
**L
= (lua_State
**)ucv_resource_dataptr(lv
->uvL
, "lua.vm");
947 luaL_unref(*L
, LUA_REGISTRYINDEX
, lv
->ref
);
953 dlopen_self(uc_vm_t
*vm
)
955 uc_value_t
*search
, *entry
;
956 char *path
, *wildcard
;
960 search
= ucv_property_get(uc_vm_scope_get(vm
), "REQUIRE_SEARCH_PATH");
962 for (i
= 0; !dlh
&& i
< ucv_array_length(search
); i
++) {
963 entry
= ucv_array_get(search
, i
);
964 path
= ucv_string_get(entry
);
965 wildcard
= path
? strchr(path
, '*') : NULL
;
968 xasprintf(&path
, "%.*slua%s", (int)(wildcard
- path
), path
, wildcard
+ 1);
969 dlh
= dlopen(path
, RTLD_LAZY
|RTLD_GLOBAL
);
970 dlerror(); /* clear error */
976 void uc_module_init(uc_vm_t
*vm
, uc_value_t
*scope
)
978 uc_function_list_register(scope
, lua_fns
);
980 vm_type
= uc_type_declare(vm
, "lua.vm", vm_fns
, free_vm
);
981 lv_type
= uc_type_declare(vm
, "lua.value", lv_fns
, free_lv
);
983 /* reopen ourself using dlopen(RTLD_GLOBAL) to make liblua symbols
984 * available to dynamic Lua extensions loaded by this module through