summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-09-30 10:23:16 +0200
committerJo-Philipp Wich <jo@mein.io>2022-09-30 10:48:09 +0200
commitd64d5d685d86b38dda8a314b7d1404633e26b346 (patch)
tree963cd227a5bc82ed530fdbb985386e078dba4d4a
parentf4b4ded660cb6901af9c6a6548d8750b9e89982b (diff)
vm: maintain export symbol tables per program
Instead of having one global export table per VM instance maintain one table per program instance. This is required to avoid clobbering the export list in case `import` using code is loaded at runtime through `require()`, `loadfile()` etc. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--include/ucode/types.h5
-rw-r--r--tests/custom/04_modules/16_multi_program_imports143
-rw-r--r--types.c10
-rw-r--r--vm.c22
4 files changed, 163 insertions, 17 deletions
diff --git a/include/ucode/types.h b/include/ucode/types.h
index 6041b22..bae2dd5 100644
--- a/include/ucode/types.h
+++ b/include/ucode/types.h
@@ -208,12 +208,14 @@ uc_declare_vector(uc_resource_types_t, uc_resource_type_t *);
/* Program structure definitions */
uc_declare_vector(uc_sources_t, uc_source_t *);
+uc_declare_vector(uc_modexports_t, uc_upvalref_t *);
typedef struct uc_program {
uc_value_t header;
uc_value_list_t constants;
uc_weakref_t functions;
uc_sources_t sources;
+ uc_modexports_t exports;
} uc_program_t;
@@ -277,7 +279,6 @@ typedef struct {
uc_declare_vector(uc_callframes_t, uc_callframe_t);
uc_declare_vector(uc_stack_t, uc_value_t *);
-uc_declare_vector(uc_modexports_t, uc_upvalref_t *);
typedef struct printbuf uc_stringbuf_t;
@@ -294,7 +295,7 @@ struct uc_vm {
uc_source_t *sources;
uc_weakref_t values;
uc_resource_types_t restypes;
- uc_modexports_t exports;
+ char _reserved[sizeof(uc_modexports_t)];
union {
uint32_t u32;
int32_t s32;
diff --git a/tests/custom/04_modules/16_multi_program_imports b/tests/custom/04_modules/16_multi_program_imports
new file mode 100644
index 0000000..32d54fe
--- /dev/null
+++ b/tests/custom/04_modules/16_multi_program_imports
@@ -0,0 +1,143 @@
+This testcase asserts that import/export symbols in runtime loaded code
+are properly dealt with.
+
+Since the compiler has no knowledge about runtime loaded code it cannot
+reserve slots in the export table, requiring the VM to maintain one export
+table per program instance.
+
+The dependency tree is:
+
+root
+ + import: mod1
+ + import: mod2
+ + require: mod3
+ + import: mod1
+ + import: mod2
+
+-- Testcase --
+import { mod2sym1, mod2sym2 } from 'mod2';
+import { mod1sym1, mod1sym2 } from 'mod1';
+
+mod3 = require('mod3');
+
+printf("root: %.J\n", { mod1sym1, mod1sym2, mod2sym1, mod2sym2, mod3 });
+-- End --
+
+-- File mod1.uc --
+export const mod1sym1 = "a";
+export const mod1sym2 = "b";
+-- End --
+
+-- File mod2.uc --
+export const mod2sym1 = "c";
+export const mod2sym2 = "d";
+-- End --
+
+-- File mod3.uc --
+import { mod1sym2, mod1sym1 } from 'mod1';
+import { mod2sym2, mod2sym1 } from 'mod2';
+
+printf("mod3: %.J\n", { mod1sym1, mod1sym2, mod2sym1, mod2sym2 });
+
+return { mod1sym1, mod1sym2, mod2sym1, mod2sym2 };
+-- End --
+
+-- Args --
+-R -L files/
+-- End --
+
+-- Expect stdout --
+mod3: {
+ "mod1sym1": "a",
+ "mod1sym2": "b",
+ "mod2sym1": "c",
+ "mod2sym2": "d"
+}
+root: {
+ "mod1sym1": "a",
+ "mod1sym2": "b",
+ "mod2sym1": "c",
+ "mod2sym2": "d",
+ "mod3": {
+ "mod1sym1": "a",
+ "mod1sym2": "b",
+ "mod2sym1": "c",
+ "mod2sym2": "d"
+ }
+}
+-- End --
+
+
+A variation of the above testcase using wildcard imports.
+
+root
+ + import: mod4
+ + import: mod5
+ + require: mod6
+ + import: mod4
+ + import: mod5
+
+-- Testcase --
+import * as mod5 from 'mod5';
+import * as mod4 from 'mod4';
+
+mod6 = require('mod6');
+
+printf("root: %.J\n", { mod4, mod5, mod6 });
+-- End --
+
+-- File mod4.uc --
+export const sym1 = "a";
+export const sym2 = "b";
+-- End --
+
+-- File mod5.uc --
+export const sym1 = "c";
+export const sym2 = "d";
+-- End --
+
+-- File mod6.uc --
+import * as mod4 from 'mod4';
+import * as mod5 from 'mod5';
+
+printf("mod6: %.J\n", { mod4, mod5 });
+
+return { mod4, mod5 };
+-- End --
+
+-- Args --
+-R -L files/
+-- End --
+
+-- Expect stdout --
+mod6: {
+ "mod4": {
+ "sym1": "a",
+ "sym2": "b"
+ },
+ "mod5": {
+ "sym1": "c",
+ "sym2": "d"
+ }
+}
+root: {
+ "mod4": {
+ "sym1": "a",
+ "sym2": "b"
+ },
+ "mod5": {
+ "sym1": "c",
+ "sym2": "d"
+ },
+ "mod6": {
+ "mod4": {
+ "sym1": "a",
+ "sym2": "b"
+ },
+ "mod5": {
+ "sym1": "c",
+ "sym2": "d"
+ }
+ }
+}
+-- End --
diff --git a/types.c b/types.c
index 5274d23..1a430ac 100644
--- a/types.c
+++ b/types.c
@@ -203,6 +203,9 @@ ucv_gc_mark(uc_value_t *uv)
for (i = 0; i < program->sources.count; i++)
ucv_gc_mark(&program->sources.entries[i]->header);
+ for (i = 0; i < program->exports.count; i++)
+ ucv_gc_mark(&program->exports.entries[i]->header);
+
break;
default:
@@ -297,7 +300,11 @@ ucv_free(uc_value_t *uv, bool retain)
for (i = 0; i < program->sources.count; i++)
ucv_put_value(&program->sources.entries[i]->header, retain);
+ for (i = 0; i < program->exports.count; i++)
+ ucv_put_value(&program->exports.entries[i]->header, retain);
+
uc_vector_clear(&program->sources);
+ uc_vector_clear(&program->exports);
break;
case UC_SOURCE:
@@ -2227,9 +2234,6 @@ ucv_gc_common(uc_vm_t *vm, bool final)
for (i = 0; i < vm->restypes.count; i++)
ucv_gc_mark(vm->restypes.entries[i]->proto);
-
- for (i = 0; i < vm->exports.count; i++)
- ucv_gc_mark(vm->exports.entries[i]->value);
}
/* unref unreachable objects */
diff --git a/vm.c b/vm.c
index d8f0074..3bf9836 100644
--- a/vm.c
+++ b/vm.c
@@ -184,9 +184,6 @@ void uc_vm_free(uc_vm_t *vm)
for (i = 0; i < vm->restypes.count; i++)
ucv_put(vm->restypes.entries[i]->proto);
- for (i = 0; i < vm->exports.count; i++)
- ucv_put(&vm->exports.entries[i]->header);
-
uc_vm_reset_callframes(vm);
uc_vm_reset_stack(vm);
uc_vector_clear(&vm->stack);
@@ -200,7 +197,6 @@ void uc_vm_free(uc_vm_t *vm)
free(vm->restypes.entries[i]);
uc_vector_clear(&vm->restypes);
- uc_vector_clear(&vm->exports);
}
static uc_chunk_t *
@@ -2362,6 +2358,7 @@ static void
uc_vm_insn_import(uc_vm_t *vm, uc_vm_insn_t insn)
{
uc_callframe_t *frame = uc_vm_current_frame(vm);
+ uc_program_t *prog = uc_vm_current_program(vm);
uint16_t from = vm->arg.u32 & 0xffff;
uint16_t to = vm->arg.u32 >> 16;
uc_value_t *name, *modobj;
@@ -2376,8 +2373,8 @@ uc_vm_insn_import(uc_vm_t *vm, uc_vm_insn_t insn)
* first module export and `from` times u32 values containing
* the constant indexes of the names */
for (from = frame->ip[0] * 0x100 + frame->ip[1], frame->ip += 2;
- from < to && from < vm->exports.count;
- from++) {
+ from < prog->exports.count && to > 0;
+ from++, to--) {
cidx = (
frame->ip[0] * 0x1000000UL +
@@ -2390,9 +2387,9 @@ uc_vm_insn_import(uc_vm_t *vm, uc_vm_insn_t insn)
name = uc_program_get_constant(uc_vm_current_program(vm), cidx);
- if (ucv_type(name) == UC_STRING && vm->exports.entries[from])
+ if (ucv_type(name) == UC_STRING && prog->exports.entries[from])
ucv_object_add(modobj, ucv_string_get(name),
- ucv_get(&vm->exports.entries[from]->header));
+ ucv_get(&prog->exports.entries[from]->header));
ucv_put(name);
}
@@ -2403,9 +2400,9 @@ uc_vm_insn_import(uc_vm_t *vm, uc_vm_insn_t insn)
}
/* module export available, patch into upvalue */
- else if (from < vm->exports.count && vm->exports.entries[from]) {
- frame->closure->upvals[to] = vm->exports.entries[from];
- ucv_get(&vm->exports.entries[from]->header);
+ else if (from <= prog->exports.count && prog->exports.entries[from]) {
+ frame->closure->upvals[to] = prog->exports.entries[from];
+ ucv_get(&prog->exports.entries[from]->header);
}
/* module export missing, e.g. due to premature return in module,
@@ -2420,9 +2417,10 @@ static void
uc_vm_insn_export(uc_vm_t *vm, uc_vm_insn_t insn)
{
uc_callframe_t *frame = uc_vm_current_frame(vm);
+ uc_program_t *prog = uc_vm_current_program(vm);
uc_upvalref_t *ref = uc_vm_capture_upval(vm, frame->stackframe + vm->arg.u32);
- uc_vector_push(&vm->exports, ref);
+ uc_vector_push(&prog->exports, ref);
ucv_get(&ref->header);
}