summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-08-06 14:23:44 +0200
committerJo-Philipp Wich <jo@mein.io>2022-08-06 23:25:11 +0200
commitfcc49e6944ab29ab48e8363d2d72e9ca10d3fb76 (patch)
tree1ccab2a62017db277b5de73e0ce6a4ed93aa751a
parentc9442f12ee056fd50f314408052917cc5f359bb4 (diff)
compiler: add import statement support for dynamic extensions
Utilize the new I_DYNLINK vm opcode to support import statements referring to dynamic extension modules. During compilation, the compiler will try to infer the type of the imported module from the resolved file path; if it ends with `.so`, the module is assumed to by a dynamic extension and loading/binding of the module is deferred to runtime using I_DYNLINK opcodes. Additionally, the `-c` cli option gained support for a new compiler flag `dynlink=...` which allows forcing a particular module name expression to be treated as dynamic extension. This is useful to e.g. force resolving `import { x } from "foo"` to a dynamic extension `foo.so` loaded at runtime even if a plain `foo.uc` exists in the search path during compilation or if no such module is available at build time. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--compiler.c70
-rw-r--r--include/ucode/types.h1
-rw-r--r--main.c14
-rw-r--r--tests/cram/test_basic.t3
4 files changed, 82 insertions, 6 deletions
diff --git a/compiler.c b/compiler.c
index 7fd4da2..1199d82 100644
--- a/compiler.c
+++ b/compiler.c
@@ -3330,6 +3330,68 @@ uc_compiler_acquire_source(uc_compiler_t *compiler, const char *path)
}
static bool
+uc_compiler_compile_dynload(uc_compiler_t *compiler, const char *name, uc_value_t *imports)
+{
+ uc_value_t *modname = ucv_string_new(name);
+ size_t i, n_imports;
+ uc_value_t *import;
+
+ for (i = 0, n_imports = 0; i < ucv_array_length(imports); i++) {
+ import = ucv_array_get(imports, i);
+
+ if (ucv_boolean_get(import)) {
+ uc_compiler_emit_insn(compiler, 0, I_DYNLOAD);
+ uc_compiler_emit_u32(compiler, 0, 0);
+ uc_compiler_emit_constant_index(compiler, 0, modname);
+ }
+ else {
+ n_imports++;
+ }
+ }
+
+ if (n_imports > 0) {
+ uc_compiler_emit_insn(compiler, 0, I_DYNLOAD);
+ uc_compiler_emit_u32(compiler, 0, n_imports | ((compiler->upvals.count - n_imports) << 16));
+ uc_compiler_emit_constant_index(compiler, 0, modname);
+
+ for (i = 0; i < ucv_array_length(imports); i++) {
+ import = ucv_get(ucv_array_get(imports, i));
+
+ if (!import)
+ import = ucv_string_new("default");
+
+ if (!ucv_boolean_get(import))
+ uc_compiler_emit_constant_index(compiler, 0, import);
+
+ ucv_put(import);
+ }
+ }
+
+ ucv_put(modname);
+
+ return true;
+}
+
+static bool
+uc_compiler_is_dynlink_module(uc_compiler_t *compiler, const char *name, const char *path)
+{
+ uc_search_path_t *dynlink_list = &compiler->parser->config->force_dynlink_list;
+ size_t i;
+ char *p;
+
+ for (i = 0; i < dynlink_list->count; i++)
+ if (!strcmp(dynlink_list->entries[i], name))
+ return true;
+
+ if (!path)
+ return false;
+
+ p = strrchr(path, '.');
+
+ return (p && !strcmp(p, ".so"));
+}
+
+static bool
uc_compiler_compile_module(uc_compiler_t *compiler, const char *name, uc_value_t *imports)
{
uc_source_t *source;
@@ -3341,7 +3403,10 @@ uc_compiler_compile_module(uc_compiler_t *compiler, const char *name, uc_value_t
path = uc_compiler_resolve_module_path(compiler, name);
- if (path) {
+ if (uc_compiler_is_dynlink_module(compiler, name, path)) {
+ res = uc_compiler_compile_dynload(compiler, name, imports);
+ }
+ else if (path) {
source = uc_compiler_acquire_source(compiler, path);
if (source) {
@@ -3361,6 +3426,8 @@ uc_compiler_compile_module(uc_compiler_t *compiler, const char *name, uc_value_t
res = false;
}
+
+ uc_source_put(source);
}
else {
uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
@@ -3369,7 +3436,6 @@ uc_compiler_compile_module(uc_compiler_t *compiler, const char *name, uc_value_t
return false;
}
- uc_source_put(source);
free(path);
return res;
diff --git a/include/ucode/types.h b/include/ucode/types.h
index 636d6e4..e20f3d6 100644
--- a/include/ucode/types.h
+++ b/include/ucode/types.h
@@ -227,6 +227,7 @@ typedef struct {
bool strict_declarations;
bool raw_mode;
uc_search_path_t module_search_path;
+ uc_search_path_t force_dynlink_list;
} uc_parse_config_t;
extern uc_parse_config_t uc_default_parse_config;
diff --git a/main.c b/main.c
index 087efff..02df3b1 100644
--- a/main.c
+++ b/main.c
@@ -90,7 +90,8 @@ print_usage(const char *app)
"-c[flag,flag,...]\n"
" Compile the given source file(s) to bytecode instead of executing them.\n"
" Supported flags: no-interp (omit interpreter line), interp=... (over-\n"
- " ride interpreter line with ...)\n\n"
+ " ride interpreter line with ...), dynlink=... (force import from ... to\n"
+ " be treated as shared extensions loaded at runtime).\n\n"
"-o path\n"
" Output file path when compiling. If omitted, the compiled byte code\n"
@@ -204,7 +205,7 @@ parse_template_modeflags(char *opt, uc_parse_config_t *config)
}
static void
-parse_compile_flags(char *opt, char **interp)
+parse_compile_flags(char *opt, char **interp, uc_search_path_t *dynlink_list)
{
char *p, *k, *v;
@@ -230,6 +231,12 @@ parse_compile_flags(char *opt, char **interp)
else
*interp = v;
}
+ else if (!strcmp(k, "dynlink")) {
+ if (!v)
+ fprintf(stderr, "Compile flag \"%s\" requires a value, ignoring\n", k);
+ else
+ uc_vector_push(dynlink_list, v);
+ }
else {
fprintf(stderr, "Unrecognized -c flag \"%s\", ignoring\n", k);
}
@@ -577,7 +584,7 @@ main(int argc, char **argv)
case 'c':
outfile = "./uc.out";
- parse_compile_flags(optarg, &interp);
+ parse_compile_flags(optarg, &interp, &config.force_dynlink_list);
break;
case 's':
@@ -640,6 +647,7 @@ main(int argc, char **argv)
out:
uc_search_path_free(&config.module_search_path);
+ uc_vector_clear(&config.force_dynlink_list);
uc_source_put(source);
diff --git a/tests/cram/test_basic.t b/tests/cram/test_basic.t
index 7308002..5911ac9 100644
--- a/tests/cram/test_basic.t
+++ b/tests/cram/test_basic.t
@@ -61,7 +61,8 @@ check that ucode provides exepected help:
-c[flag,flag,...]
Compile the given source file(s) to bytecode instead of executing them.
Supported flags: no-interp (omit interpreter line), interp=... (over-
- ride interpreter line with ...)
+ ride interpreter line with ...), dynlink=... (force import from ... to
+ be treated as shared extensions loaded at runtime).
-o path
Output file path when compiling. If omitted, the compiled byte code