summaryrefslogtreecommitdiffhomepage
path: root/lib.c
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2022-08-23 15:43:25 +0200
committerJo-Philipp Wich <jo@mein.io>2022-08-24 15:33:18 +0200
commit131d99c45e586e06d2fa3adba32e92c0370ad022 (patch)
tree0a089858e2f2fbf2c19440be8dda7e945667d83b /lib.c
parent8e8dae0eb0f90dea3cb4b244c79f7fa855219f92 (diff)
lib: introduce three new functions call(), loadstring() and loadfile()
Introduce new functions dealing with on-the-fly compilation of code and execution of functions with different global scope. The `loadstring()` and `loadfile()` functions will compile the given ucode source string or ucode file path respectively and return the entry function of the resulting program. An optional dictionary specifying parse options may be given as second argument. Both functions return `null` on invalid arguments and throw an exception in case of compilation errors. The `call()` function allows invoking a given function value with a different `this` context and/or a different global environment. Finally refactor the existing `uc_require_ucode()` implementation to reuse the new `uc_loadfile()` and `uc_call()` implementations and adjust as well as simplify affected testcases. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'lib.c')
-rw-r--r--lib.c262
1 files changed, 213 insertions, 49 deletions
diff --git a/lib.c b/lib.c
index fcdd17b..37563c2 100644
--- a/lib.c
+++ b/lib.c
@@ -1638,71 +1638,44 @@ uc_require_so(uc_vm_t *vm, const char *path, uc_value_t **res)
return true;
}
+static uc_value_t *
+uc_loadfile(uc_vm_t *vm, size_t nargs);
+
+static uc_value_t *
+uc_callfunc(uc_vm_t *vm, size_t nargs);
+
static bool
uc_require_ucode(uc_vm_t *vm, const char *path, uc_value_t *scope, uc_value_t **res, bool raw_mode)
{
- uc_parse_config_t config = { 0 };
- uc_exception_type_t extype;
- uc_program_t *program;
- uc_value_t *prev_scope;
+ uc_parse_config_t config = *vm->config, *prev_config = vm->config;
uc_value_t *closure;
- uc_source_t *source;
struct stat st;
- bool prev_mode;
- char *err;
if (stat(path, &st))
return false;
- source = uc_source_new_file(path);
+ config.raw_mode = raw_mode;
+ vm->config = &config;
- if (!source) {
- uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
- "Unable to open file '%s': %s", path, strerror(errno));
+ uc_vm_stack_push(vm, ucv_string_new(path));
- return true;
- }
-
- if (!vm->config)
- vm->config = &config;
-
- prev_mode = vm->config->raw_mode;
- vm->config->raw_mode = raw_mode;
-
- program = uc_compile(vm->config, source, &err);
+ closure = uc_loadfile(vm, 1);
- uc_source_put(source);
-
- if (!program) {
- uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
- "Unable to compile module '%s':\n%s", path, err);
+ ucv_put(uc_vm_stack_pop(vm));
- free(err);
+ if (closure) {
+ uc_vm_stack_push(vm, closure);
+ uc_vm_stack_push(vm, NULL);
+ uc_vm_stack_push(vm, scope);
- vm->config->raw_mode = prev_mode;
+ *res = uc_callfunc(vm, 3);
- return true;
- }
-
- closure = ucv_closure_new(vm, uc_program_entry(program), false);
-
- uc_vm_stack_push(vm, closure);
- uc_program_put(program);
-
- if (scope) {
- prev_scope = ucv_get(uc_vm_scope_get(vm));
- uc_vm_scope_set(vm, ucv_get(scope));
+ uc_vm_stack_pop(vm);
+ uc_vm_stack_pop(vm);
+ uc_vm_stack_pop(vm);
}
- extype = uc_vm_call(vm, false, 0);
-
- if (scope)
- uc_vm_scope_set(vm, prev_scope);
-
- if (extype == EXCEPTION_NONE)
- *res = uc_vm_stack_pop(vm);
-
- vm->config->raw_mode = prev_mode;
+ vm->config = prev_config;
return true;
}
@@ -3589,6 +3562,194 @@ uc_gc(uc_vm_t *vm, size_t nargs)
return NULL;
}
+static void
+uc_compile_parse_config(uc_parse_config_t *config, uc_value_t *spec)
+{
+ uc_value_t *v, *p;
+ size_t i, j;
+ bool found;
+
+ struct {
+ const char *key;
+ bool *flag;
+ uc_search_path_t *path;
+ } fields[] = {
+ { "lstrip_blocks", &config->lstrip_blocks, NULL },
+ { "trim_blocks", &config->trim_blocks, NULL },
+ { "strict_declarations", &config->strict_declarations, NULL },
+ { "raw_mode", &config->raw_mode, NULL },
+ { "module_search_path", NULL, &config->module_search_path },
+ { "force_dynlink_list", NULL, &config->force_dynlink_list }
+ };
+
+ for (i = 0; i < ARRAY_SIZE(fields); i++) {
+ v = ucv_object_get(spec, fields[i].key, &found);
+
+ if (!found)
+ continue;
+
+ if (fields[i].flag) {
+ *fields[i].flag = ucv_is_truish(v);
+ }
+ else if (fields[i].path) {
+ fields[i].path->count = 0;
+ fields[i].path->entries = NULL;
+
+ for (j = 0; j < ucv_array_length(v); j++) {
+ p = ucv_array_get(v, j);
+
+ if (ucv_type(p) != UC_STRING)
+ continue;
+
+ uc_vector_push(fields[i].path, ucv_string_get(p));
+ }
+ }
+ }
+}
+
+static uc_value_t *
+uc_load_common(uc_vm_t *vm, size_t nargs, uc_source_t *source)
+{
+ uc_parse_config_t conf = *vm->config;
+ uc_program_t *program;
+ uc_value_t *closure;
+ char *err = NULL;
+
+ uc_compile_parse_config(&conf, uc_fn_arg(1));
+
+ program = uc_compile(&conf, source, &err);
+ closure = program ? ucv_closure_new(vm, uc_program_entry(program), false) : NULL;
+
+ uc_program_put(program);
+
+ if (!vm->config || conf.module_search_path.entries != vm->config->module_search_path.entries)
+ uc_vector_clear(&conf.module_search_path);
+
+ if (!vm->config || conf.force_dynlink_list.entries != vm->config->force_dynlink_list.entries)
+ uc_vector_clear(&conf.force_dynlink_list);
+
+ if (!closure) {
+ uc_error_message_indent(&err);
+
+ if (source->buffer)
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
+ "Unable to compile source string:\n\n%s", err);
+ else
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
+ "Unable to compile source file '%s':\n\n%s", source->filename, err);
+ }
+
+ uc_source_put(source);
+ free(err);
+
+ return closure;
+}
+
+static uc_value_t *
+uc_loadstring(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *code = uc_fn_arg(0);
+ uc_source_t *source;
+ size_t len;
+ char *s;
+
+ if (ucv_type(code) == UC_STRING) {
+ len = ucv_string_length(code);
+ s = xalloc(len);
+ memcpy(s, ucv_string_get(code), len);
+ }
+ else {
+ s = ucv_to_string(vm, code);
+ len = strlen(s);
+ }
+
+ source = uc_source_new_buffer("[loadstring argument]", s, len);
+
+ if (!source) {
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
+ "Unable to allocate source buffer: %s",
+ strerror(errno));
+
+ return NULL;
+ }
+
+ return uc_load_common(vm, nargs, source);
+}
+
+static uc_value_t *
+uc_loadfile(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *path = uc_fn_arg(0);
+ uc_source_t *source;
+
+ if (ucv_type(path) != UC_STRING)
+ return NULL;
+
+ source = uc_source_new_file(ucv_string_get(path));
+
+ if (!source) {
+ uc_vm_raise_exception(vm, EXCEPTION_RUNTIME,
+ "Unable to open source file %s: %s",
+ ucv_string_get(path), strerror(errno));
+
+ return NULL;
+ }
+
+ return uc_load_common(vm, nargs, source);
+}
+
+static uc_value_t *
+uc_callfunc(uc_vm_t *vm, size_t nargs)
+{
+ size_t argoff = vm->stack.count - nargs, i;
+ uc_value_t *fn_scope, *prev_scope, *res;
+ uc_value_t *fn = uc_fn_arg(0);
+ uc_value_t *this = uc_fn_arg(1);
+ uc_value_t *scope = uc_fn_arg(2);
+
+ if (!ucv_is_callable(fn))
+ return NULL;
+
+ if (scope && ucv_type(scope) != UC_OBJECT)
+ return NULL;
+
+ if (ucv_prototype_get(scope)) {
+ fn_scope = ucv_get(scope);
+ }
+ else if (scope) {
+ fn_scope = ucv_object_new(vm);
+
+ ucv_object_foreach(scope, k, v)
+ ucv_object_add(fn_scope, k, ucv_get(v));
+
+ ucv_prototype_set(fn_scope, ucv_get(uc_vm_scope_get(vm)));
+ }
+ else {
+ fn_scope = NULL;
+ }
+
+ uc_vm_stack_push(vm, ucv_get(this));
+ uc_vm_stack_push(vm, ucv_get(fn));
+
+ for (i = 3; i < nargs; i++)
+ uc_vm_stack_push(vm, ucv_get(vm->stack.entries[3 + argoff++]));
+
+ if (fn_scope) {
+ prev_scope = ucv_get(uc_vm_scope_get(vm));
+ uc_vm_scope_set(vm, fn_scope);
+ }
+
+ if (uc_vm_call(vm, true, i - 3) == EXCEPTION_NONE)
+ res = uc_vm_stack_pop(vm);
+ else
+ res = NULL;
+
+ if (fn_scope)
+ uc_vm_scope_set(vm, prev_scope);
+
+ return res;
+}
+
const uc_function_list_t uc_stdlib_functions[] = {
{ "chr", uc_chr },
@@ -3656,7 +3817,10 @@ const uc_function_list_t uc_stdlib_functions[] = {
{ "clock", uc_clock },
{ "hexdec", uc_hexdec },
{ "hexenc", uc_hexenc },
- { "gc", uc_gc }
+ { "gc", uc_gc },
+ { "loadstring", uc_loadstring },
+ { "loadfile", uc_loadfile },
+ { "call", uc_callfunc },
};