diff options
-rw-r--r-- | include/ucode/types.h | 5 | ||||
-rw-r--r-- | tests/custom/04_modules/16_multi_program_imports | 143 | ||||
-rw-r--r-- | types.c | 10 | ||||
-rw-r--r-- | vm.c | 22 |
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 -- @@ -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 */ @@ -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); } |