diff options
author | Jo-Philipp Wich <jo@mein.io> | 2022-08-06 14:19:33 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2022-08-06 23:25:11 +0200 |
commit | c9442f12ee056fd50f314408052917cc5f359bb4 (patch) | |
tree | b3b813b7e1a1d1146a6977da4ee7b51f7067ebbd /vm.c | |
parent | b6fd8a2f825ba3d38137bcfbf44ef7dd09161cd2 (diff) |
vm: introduce new I_DYNLOAD opcode
The I_DYNLOAD opcode is basically a bytecode level instruction for
uc_require() with semantics similar to I_IMPORT. It allows loading
a dynamic extension library at runtime and treating values from the
resulting module context object like exports from a compile time source
module.
For example the statement `import { readfile, writefile } from "fs"`
would import the readfile() and writefile() functions of fs.so as
readonly live bindings into the current file scope.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'vm.c')
-rw-r--r-- | vm.c | 89 |
1 files changed, 88 insertions, 1 deletions
@@ -75,7 +75,8 @@ static const int8_t insn_operand_bytes[__I_MAX] = { [I_QMCALL] = 4, [I_IMPORT] = 4, - [I_EXPORT] = 4 + [I_EXPORT] = 4, + [I_DYNLOAD] = 4 }; static const char *exception_type_strings[] = { @@ -2425,6 +2426,88 @@ uc_vm_insn_export(uc_vm_t *vm, uc_vm_insn_t insn) ucv_get(&ref->header); } +static void +uc_vm_insn_dynload(uc_vm_t *vm, uc_vm_insn_t insn) +{ + uc_callframe_t *frame = uc_vm_current_frame(vm); + uc_value_t *name, *export, *modscope, *modobj; + uint16_t count = vm->arg.u32 & 0xffff; + uint16_t to = vm->arg.u32 >> 16; + uint32_t cidx; + bool found; + + /* instruction is followed by u32 containing the constant index of the + * module name string to import and `count` times u32 values containing + * the import name constant indexes */ + + cidx = ( + frame->ip[0] * 0x1000000UL + + frame->ip[1] * 0x10000UL + + frame->ip[2] * 0x100UL + + frame->ip[3] + ); + + frame->ip += 4; + + /* push module name onto stack, then attempt to load module and pop + * name value again. Will raise exception on error */ + name = uc_program_get_constant(uc_vm_current_program(vm), cidx); + modscope = uc_require_library(vm, name, true); + ucv_put(name); + + if (!modscope) + return; + + /* If count is zero, we're doing a wildcard import. Shallow copy module + * object, mark it constant and patch into the target upvalue. */ + if (count == 0) { + modobj = ucv_object_new(vm); + + ucv_object_foreach(modscope, k, v) + ucv_object_add(modobj, k, ucv_get(v)); + + ucv_set_constant(modobj, true); + + uc_vm_stack_push(vm, modobj); + } + + /* ... otherwise we're importing a specific list of names */ + else { + while (count > 0) { + cidx = ( + frame->ip[0] * 0x1000000UL + + frame->ip[1] * 0x10000UL + + frame->ip[2] * 0x100UL + + frame->ip[3] + ); + + frame->ip += 4; + + name = uc_program_get_constant(uc_vm_current_program(vm), cidx); + export = ucv_object_get(modscope, ucv_string_get(name), &found); + + if (!found) { + uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, + "Module does not export %s", + ucv_string_get(name)); + + ucv_put(name); + + return; + } + + ucv_put(name); + + frame->closure->upvals[to] = (uc_upvalref_t *)ucv_upvalref_new(0); + frame->closure->upvals[to]->closed = true; + frame->closure->upvals[to]->value = ucv_get(export); + + count--; + to++; + } + } +} + static uc_value_t * uc_vm_callframe_pop(uc_vm_t *vm) { @@ -2720,6 +2803,10 @@ uc_vm_execute_chunk(uc_vm_t *vm) uc_vm_insn_export(vm, insn); break; + case I_DYNLOAD: + uc_vm_insn_dynload(vm, insn); + break; + default: uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, "unknown opcode %d", insn); break; |