From: Dmitry Volyntsev Date: Mon, 21 Feb 2022 14:49:38 +0000 (+0000) Subject: Refactoring of user modules importing. X-Git-Tag: 0.7.3~7 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=2ff8b264830da18528d6655b0e0654414cae9272;p=njs.git Refactoring of user modules importing. Previously, user modules were compiled as as anonymous functions in a global scope. This is incorrect, because modules should be compiled in their own scope. In addition, this patch introduces HostResolveImportedModule support. When vm->options.ops->module_loader is provided, a module lookup and compilation is delegated to this callback. This closes #443 issue on Github. --- diff --git a/external/njs_crypto_module.c b/external/njs_crypto_module.c index a6028289..fbef019f 100644 --- a/external/njs_crypto_module.c +++ b/external/njs_crypto_module.c @@ -646,7 +646,7 @@ njs_crypto_init(njs_vm_t *vm) return NJS_ERROR; } - module = njs_module_add(vm, &njs_str_value("crypto"), 1); + module = njs_module_add(vm, &njs_str_value("crypto")); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } diff --git a/external/njs_fs_module.c b/external/njs_fs_module.c index a586af0a..71a1d27e 100644 --- a/external/njs_fs_module.c +++ b/external/njs_fs_module.c @@ -3090,7 +3090,7 @@ njs_fs_init(njs_vm_t *vm) return NJS_ERROR; } - module = njs_module_add(vm, &njs_str_value("fs"), 1); + module = njs_module_add(vm, &njs_str_value("fs")); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } diff --git a/external/njs_query_string_module.c b/external/njs_query_string_module.c index ae0327e5..dedc5d82 100644 --- a/external/njs_query_string_module.c +++ b/external/njs_query_string_module.c @@ -967,7 +967,7 @@ njs_query_string_init(njs_vm_t *vm) return NJS_ERROR; } - module = njs_module_add(vm, &njs_str_value("querystring"), 1); + module = njs_module_add(vm, &njs_str_value("querystring")); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } diff --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 40ee8491..84e5ae8e 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -704,7 +704,8 @@ static njs_external_t ngx_http_js_ext_request[] = { static njs_vm_ops_t ngx_http_js_ops = { ngx_http_js_set_timer, - ngx_http_js_clear_timer + ngx_http_js_clear_timer, + NULL, }; @@ -3490,8 +3491,12 @@ ngx_http_js_init_main_conf(ngx_conf_t *cf, void *conf) import = jmcf->imports->elts; for (i = 0; i < jmcf->imports->nelts; i++) { - size += sizeof("import from '';\n") - 1 + import[i].name.len - + import[i].path.len; + + /* import from ''; globalThis. = ; */ + + size += sizeof("import from '';") - 1 + import[i].name.len * 3 + + import[i].path.len + + sizeof(" globalThis. = ;\n") - 1; } start = ngx_pnalloc(cf->pool, size); @@ -3502,11 +3507,18 @@ ngx_http_js_init_main_conf(ngx_conf_t *cf, void *conf) p = start; import = jmcf->imports->elts; for (i = 0; i < jmcf->imports->nelts; i++) { + + /* import from ''; globalThis. = ; */ + p = ngx_cpymem(p, "import ", sizeof("import ") - 1); p = ngx_cpymem(p, import[i].name.data, import[i].name.len); p = ngx_cpymem(p, " from '", sizeof(" from '") - 1); p = ngx_cpymem(p, import[i].path.data, import[i].path.len); - p = ngx_cpymem(p, "';\n", sizeof("';\n") - 1); + p = ngx_cpymem(p, "'; globalThis.", sizeof("'; globalThis.") - 1); + p = ngx_cpymem(p, import[i].name.data, import[i].name.len); + p = ngx_cpymem(p, " = ", sizeof(" = ") - 1); + p = ngx_cpymem(p, import[i].name.data, import[i].name.len); + p = ngx_cpymem(p, ";\n", sizeof(";\n") - 1); } njs_vm_opt_init(&options); diff --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index b2b30907..ab3368da 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -456,7 +456,8 @@ static njs_external_t ngx_stream_js_ext_session[] = { static njs_vm_ops_t ngx_stream_js_ops = { ngx_stream_js_set_timer, - ngx_stream_js_clear_timer + ngx_stream_js_clear_timer, + NULL, }; @@ -1512,8 +1513,11 @@ ngx_stream_js_init_main_conf(ngx_conf_t *cf, void *conf) import = jmcf->imports->elts; for (i = 0; i < jmcf->imports->nelts; i++) { - size += sizeof("import from '';\n") - 1 + import[i].name.len - + import[i].path.len; + /* import from ''; globalThis. = ; */ + + size += sizeof("import from '';") - 1 + import[i].name.len * 3 + + import[i].path.len + + sizeof(" globalThis. = ;\n") - 1; } start = ngx_pnalloc(cf->pool, size); @@ -1524,11 +1528,18 @@ ngx_stream_js_init_main_conf(ngx_conf_t *cf, void *conf) p = start; import = jmcf->imports->elts; for (i = 0; i < jmcf->imports->nelts; i++) { + + /* import from ''; globalThis. = ; */ + p = ngx_cpymem(p, "import ", sizeof("import ") - 1); p = ngx_cpymem(p, import[i].name.data, import[i].name.len); p = ngx_cpymem(p, " from '", sizeof(" from '") - 1); p = ngx_cpymem(p, import[i].path.data, import[i].path.len); - p = ngx_cpymem(p, "';\n", sizeof("';\n") - 1); + p = ngx_cpymem(p, "'; globalThis.", sizeof("'; globalThis.") - 1); + p = ngx_cpymem(p, import[i].name.data, import[i].name.len); + p = ngx_cpymem(p, " = ", sizeof(" = ") - 1); + p = ngx_cpymem(p, import[i].name.data, import[i].name.len); + p = ngx_cpymem(p, ";\n", sizeof(";\n") - 1); } njs_vm_opt_init(&options); diff --git a/src/njs.h b/src/njs.h index d6805be0..fa37d071 100644 --- a/src/njs.h +++ b/src/njs.h @@ -28,6 +28,7 @@ typedef uintptr_t njs_index_t; typedef struct njs_vm_s njs_vm_t; +typedef struct njs_mod_s njs_mod_t; typedef union njs_value_s njs_value_t; typedef struct njs_function_s njs_function_t; typedef struct njs_vm_shared_s njs_vm_shared_t; @@ -183,11 +184,14 @@ typedef njs_host_event_t (*njs_set_timer_t)(njs_external_ptr_t external, uint64_t delay, njs_vm_event_t vm_event); typedef void (*njs_event_destructor_t)(njs_external_ptr_t external, njs_host_event_t event); +typedef njs_mod_t *(*njs_module_loader_t)(njs_vm_t *vm, + njs_external_ptr_t external, njs_str_t *name); typedef struct { njs_set_timer_t set_timer; njs_event_destructor_t clear_timer; + njs_module_loader_t module_loader; } njs_vm_ops_t; @@ -254,6 +258,8 @@ NJS_EXPORT njs_vm_t *njs_vm_create(njs_vm_opt_t *options); NJS_EXPORT void njs_vm_destroy(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end); +NJS_EXPORT njs_mod_t *njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, + u_char **start, u_char *end); NJS_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external); NJS_EXPORT njs_vm_event_t njs_vm_add_event(njs_vm_t *vm, diff --git a/src/njs_buffer.c b/src/njs_buffer.c index 0473bcaf..5daf7376 100644 --- a/src/njs_buffer.c +++ b/src/njs_buffer.c @@ -3015,7 +3015,7 @@ njs_buffer_init(njs_vm_t *vm) return NJS_ERROR; } - module = njs_module_add(vm, &njs_str_value("buffer"), 1); + module = njs_module_add(vm, &njs_str_value("buffer")); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } diff --git a/src/njs_builtin.c b/src/njs_builtin.c index 03fac9c9..d23551b1 100644 --- a/src/njs_builtin.c +++ b/src/njs_builtin.c @@ -761,7 +761,9 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function, break; } - if (njs_is_object(&module->value)) { + if (njs_is_object(&module->value) + && !njs_object(&module->value)->shared) + { ctx.match = module->name; ret = njs_object_traverse(vm, njs_object(&module->value), &ctx, diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c index 4c93a04f..1fccd058 100644 --- a/src/njs_disassembler.c +++ b/src/njs_disassembler.c @@ -171,15 +171,6 @@ njs_disassembler(njs_vm_t *vm) code = vm->codes->start; n = vm->codes->items; - while (n != 0) { - if (code->start == vm->start) { - break; - } - - code++; - n--; - } - while (n != 0) { njs_printf("%V:%V\n", &code->file, &code->name); njs_disassemble(code); @@ -207,6 +198,7 @@ njs_disassemble(njs_vm_code_t *code) njs_vmcode_3addr_t *code3; njs_vmcode_array_t *array; njs_vmcode_catch_t *catch; + njs_vmcode_import_t *import; njs_vmcode_finally_t *finally; njs_vmcode_try_end_t *try_end; njs_vmcode_move_arg_t *move_arg; @@ -398,6 +390,18 @@ njs_disassemble(njs_vm_code_t *code) continue; } + if (operation == NJS_VMCODE_IMPORT) { + import = (njs_vmcode_import_t *) p; + + njs_printf("%5uD | %05uz IMPORT %04Xz %V\n", + line, p - start, (size_t) import->retval, + &import->module->name); + + p += sizeof(njs_vmcode_import_t); + + continue; + } + if (operation == NJS_VMCODE_TRY_START) { try_start = (njs_vmcode_try_start_t *) p; diff --git a/src/njs_function.c b/src/njs_function.c index c07f55c4..c9d4d973 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -1209,7 +1209,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, } } - ret = njs_generator_init(&generator, 0, 1); + ret = njs_generator_init(&generator, &file, 0, 1); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_generator_init() failed"); return NJS_ERROR; diff --git a/src/njs_generator.c b/src/njs_generator.c index 22f1bf82..4d803e71 100644 --- a/src/njs_generator.c +++ b/src/njs_generator.c @@ -319,8 +319,6 @@ static njs_int_t njs_generate_throw_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_import_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); -static njs_int_t njs_generate_import_statement_end(njs_vm_t *vm, - njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_export_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_export_statement_end(njs_vm_t *vm, @@ -424,8 +422,8 @@ static njs_int_t njs_generate_index_release(njs_vm_t *vm, njs_code_offset_diff(generator, patch->jump_offset) -#define njs_generate_syntax_error(vm, node, fmt, ...) \ - njs_parser_node_error(vm, node, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, \ +#define njs_generate_syntax_error(vm, node, file, fmt, ...) \ + njs_parser_node_error(vm, NJS_OBJ_TYPE_SYNTAX_ERROR, node, file, fmt, \ ##__VA_ARGS__) @@ -436,13 +434,14 @@ static const njs_str_t undef_label = { 0xffffffff, (u_char *) "" }; njs_int_t -njs_generator_init(njs_generator_t *generator, njs_int_t depth, - njs_bool_t runtime) +njs_generator_init(njs_generator_t *generator, njs_str_t *file, + njs_int_t depth, njs_bool_t runtime) { njs_memzero(generator, sizeof(njs_generator_t)); njs_queue_init(&generator->stack); + generator->file = *file; generator->depth = depth; generator->runtime = runtime; @@ -2312,7 +2311,8 @@ njs_generate_continue_statement(njs_vm_t *vm, njs_generator_t *generator, syntax_error: - njs_generate_syntax_error(vm, node, "Illegal continue statement"); + njs_generate_syntax_error(vm, node, &generator->file, + "Illegal continue statement"); return NJS_ERROR; } @@ -2357,7 +2357,8 @@ njs_generate_break_statement(njs_vm_t *vm, njs_generator_t *generator, syntax_error: - njs_generate_syntax_error(vm, node, "Illegal break statement"); + njs_generate_syntax_error(vm, node, &generator->file, + "Illegal break statement"); return NJS_ERROR; } @@ -3102,17 +3103,13 @@ njs_generate_function(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; - njs_bool_t module; - const njs_str_t *name; njs_function_lambda_t *lambda; njs_vmcode_function_t *function; lambda = node->u.value.data.u.lambda; - module = node->right->scope->module; - - name = module ? &njs_entry_module : &njs_entry_anonymous; - ret = njs_generate_function_scope(vm, generator, lambda, node, name); + ret = njs_generate_function_scope(vm, generator, lambda, node, + &njs_entry_anonymous); if (njs_slow_path(ret != NJS_OK)) { return ret; } @@ -3641,13 +3638,11 @@ njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev, njs_function_lambda_t *lambda, njs_parser_node_t *node, const njs_str_t *name) { - njs_int_t ret; - njs_arr_t *arr; - njs_bool_t module; - njs_uint_t depth; - njs_vm_code_t *code; - njs_generator_t generator; - njs_parser_node_t *file_node; + njs_int_t ret; + njs_arr_t *arr; + njs_uint_t depth; + njs_vm_code_t *code; + njs_generator_t generator; depth = prev->depth; @@ -3656,7 +3651,7 @@ njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev, return NJS_ERROR; } - ret = njs_generator_init(&generator, depth, prev->runtime); + ret = njs_generator_init(&generator, &prev->file, depth, prev->runtime); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_generator_init() failed"); return NJS_ERROR; @@ -3673,11 +3668,6 @@ njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev, return NJS_ERROR; } - module = node->right->scope->module; - file_node = module ? node->right : node; - - code->file = file_node->scope->file; - lambda->start = generator.code_start; lambda->closures = generator.closures->start; lambda->nclosures = generator.closures->items; @@ -3774,7 +3764,7 @@ njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator, code = njs_arr_item(vm->codes, index); code->start = generator->code_start; code->end = generator->code_end; - code->file = scope->file; + code->file = generator->file; code->name = *name; generator->code_size = generator->code_end - generator->code_start; @@ -4620,45 +4610,22 @@ static njs_int_t njs_generate_import_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { - njs_variable_t *var; - njs_parser_node_t *lvalue, *expr; + njs_variable_t *var; + njs_parser_node_t *lvalue; + njs_vmcode_import_t *import; lvalue = node->left; - expr = node->right; var = njs_variable_reference(vm, lvalue); if (njs_slow_path(var == NULL)) { return NJS_ERROR; } - if (expr->left != NULL) { - njs_generator_next(generator, njs_generate, expr->left); - - return njs_generator_after(vm, generator, - njs_queue_first(&generator->stack), node, - njs_generate_import_statement_end, NULL, 0); - } - - return njs_generate_import_statement_end(vm, generator, node); -} - - -static njs_int_t -njs_generate_import_statement_end(njs_vm_t *vm, njs_generator_t *generator, - njs_parser_node_t *node) -{ - njs_mod_t *module; - njs_parser_node_t *expr; - njs_vmcode_object_copy_t *copy; - - expr = node->right; - - module = (njs_mod_t *) expr->index; + njs_generate_code(generator, njs_vmcode_import_t, import, + NJS_VMCODE_IMPORT, 1, node); - njs_generate_code(generator, njs_vmcode_object_copy_t, copy, - NJS_VMCODE_OBJECT_COPY, 2, node); - copy->retval = node->left->index; - copy->object = module->index; + import->module = node->u.module; + import->retval = lvalue->index; return njs_generator_stack_pop(vm, generator, NULL); } diff --git a/src/njs_generator.h b/src/njs_generator.h index b5d8db63..045d598d 100644 --- a/src/njs_generator.h +++ b/src/njs_generator.h @@ -27,6 +27,7 @@ struct njs_generator_s { njs_arr_t *index_cache; njs_arr_t *closures; + njs_str_t file; njs_arr_t *lines; size_t code_size; @@ -40,8 +41,8 @@ struct njs_generator_s { }; -njs_int_t njs_generator_init(njs_generator_t *generator, njs_int_t depth, - njs_bool_t runtime); +njs_int_t njs_generator_init(njs_generator_t *generator, njs_str_t *file, + njs_int_t depth, njs_bool_t runtime); njs_vm_code_t *njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator, njs_parser_scope_t *scope, const njs_str_t *name); uint32_t njs_lookup_line(njs_vm_code_t *code, uint32_t offset); diff --git a/src/njs_module.c b/src/njs_module.c index 5257efed..16e29a57 100644 --- a/src/njs_module.c +++ b/src/njs_module.c @@ -12,286 +12,60 @@ typedef struct { int fd; njs_str_t name; njs_str_t file; + char path[NJS_MAX_PATH + 1]; } njs_module_info_t; -typedef struct { - njs_str_t text; - njs_module_info_t info; - njs_lexer_t *prev; - njs_lexer_t lexer; -} njs_module_temp_t; - - -static njs_int_t njs_parser_module_lambda_after(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_module_after(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current); - static njs_int_t njs_module_lookup(njs_vm_t *vm, const njs_str_t *cwd, njs_module_info_t *info); -static njs_int_t njs_module_relative_path(njs_vm_t *vm, - const njs_str_t *dir, njs_module_info_t *info); -static njs_int_t njs_module_absolute_path(njs_vm_t *vm, +static njs_int_t njs_module_path(njs_vm_t *vm, const njs_str_t *dir, njs_module_info_t *info); -static njs_bool_t njs_module_realpath_equal(const njs_str_t *path1, - const njs_str_t *path2); static njs_int_t njs_module_read(njs_vm_t *vm, int fd, njs_str_t *body); -static njs_mod_t *njs_module_find(njs_vm_t *vm, njs_str_t *name, - njs_bool_t local); -static njs_int_t njs_module_insert(njs_parser_t *parser, njs_mod_t *module); - - -njs_int_t -njs_module_load(njs_vm_t *vm) -{ - njs_int_t ret; - njs_mod_t **item, *module; - njs_uint_t i; - njs_value_t *value; - njs_object_t *object; - - if (vm->modules == NULL) { - return NJS_OK; - } - - item = vm->modules->start; - - for (i = 0; i < vm->modules->items; i++) { - module = *item; +static njs_mod_t *njs_default_module_loader(njs_vm_t *vm, + njs_external_ptr_t external, njs_str_t *name); - if (module->function.native) { - value = njs_scope_valid_value(vm, module->index); - njs_value_assign(value, &module->value); - object = njs_object_value_copy(vm, value); - if (njs_slow_path(object == NULL)) { - return NJS_ERROR; - } - - } else { - ret = njs_vm_invoke(vm, &module->function, NULL, 0, - njs_scope_valid_value(vm, module->index)); - if (ret == NJS_ERROR) { - return ret; - } - } - - item++; - } - - return NJS_OK; -} - - -void -njs_module_reset(njs_vm_t *vm) -{ - njs_mod_t **item, *module; - njs_uint_t i; - njs_lvlhsh_query_t lhq; - - if (vm->modules == NULL) { - return; - } - - item = vm->modules->start; - - for (i = 0; i < vm->modules->items; i++) { - module = *item; - - if (!module->function.native) { - lhq.key = module->name; - lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); - lhq.proto = &njs_modules_hash_proto; - lhq.pool = vm->mem_pool; - - (void) njs_lvlhsh_delete(&vm->modules_hash, &lhq); - } - - item++; - } - - njs_arr_reset(vm->modules); -} - - -njs_int_t -njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) +njs_mod_t * +njs_parser_module(njs_parser_t *parser, njs_str_t *name) { - njs_int_t ret; - njs_str_t name, text; - njs_mod_t *module; - njs_module_temp_t *temp; - njs_module_info_t info; - - name = token->text; - - parser->node = NULL; - - module = njs_module_find(parser->vm, &name, 1); - if (module != NULL && module->function.native) { - njs_lexer_consume_token(parser->lexer, 1); + njs_mod_t *module; + njs_vm_t *vm; + njs_external_ptr_t external; + njs_module_loader_t loader; - parser->target = (njs_parser_node_t *) module; - - return njs_parser_module_after(parser, token, current); - } - - njs_memzero(&text, sizeof(njs_str_t)); - - if (parser->vm->options.sandbox || name.length == 0) { - njs_parser_syntax_error(parser, "Cannot find module \"%V\"", &name); - goto fail; - } - - /* Non-native module. */ - - njs_memzero(&info, sizeof(njs_module_info_t)); - - info.name = name; + vm = parser->vm; - ret = njs_module_lookup(parser->vm, &parser->scope->cwd, &info); - if (njs_slow_path(ret != NJS_OK)) { - njs_parser_syntax_error(parser, "Cannot find module \"%V\"", &name); - goto fail; + if (name->length == 0) { + njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name); + return NULL; } - module = njs_module_find(parser->vm, &info.file, 1); + module = njs_module_find(vm, name, 1); if (module != NULL) { - (void) close(info.fd); - njs_lexer_consume_token(parser->lexer, 1); - - parser->target = (njs_parser_node_t *) module; - - return njs_parser_module_after(parser, token, current); - } - - ret = njs_module_read(parser->vm, info.fd, &text); - - (void) close(info.fd); - - if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(parser->vm, "while reading \"%V\" module", - &info.file); - goto fail; - } - - if (njs_module_realpath_equal(&parser->lexer->file, &info.file)) { - njs_parser_syntax_error(parser, "Cannot import itself \"%V\"", - &info.file); - goto fail; - } - - temp = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_module_temp_t)); - if (njs_slow_path(temp == NULL)) { - return NJS_ERROR; - } - - ret = njs_lexer_init(parser->vm, &temp->lexer, &info.file, text.start, - text.start + text.length, 0); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - - njs_lexer_consume_token(parser->lexer, 1); - - temp->prev = parser->lexer; - temp->info = info; - temp->text = text; - - parser->lexer = &temp->lexer; - - njs_parser_next(parser, njs_parser_module_lambda); - - return njs_parser_after(parser, current, temp, 0, - njs_parser_module_lambda_after); - -fail: - - if (text.start != NULL) { - njs_mp_free(parser->vm->mem_pool, text.start); - } - - return NJS_ERROR; -} - - -static njs_int_t -njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) -{ - njs_mod_t *module; - njs_module_temp_t *temp; - - temp = (njs_module_temp_t *) parser->target; - - if (parser->ret != NJS_OK) { - njs_mp_free(parser->vm->mem_pool, temp->text.start); - njs_mp_free(parser->vm->mem_pool, temp); - - if (token->type == NJS_TOKEN_END) { - return njs_parser_stack_pop(parser); - } - - return njs_parser_failed(parser); + goto done; } - module = njs_module_add(parser->vm, &temp->info.file, 0); - if (njs_slow_path(module == NULL)) { - parser->lexer = temp->prev; - - if (temp->text.start != NULL) { - njs_mp_free(parser->vm->mem_pool, temp->text.start); - } + external = parser; + loader = njs_default_module_loader; - return njs_parser_failed(parser); + if (vm->options.ops != NULL && vm->options.ops->module_loader != NULL) { + loader = vm->options.ops->module_loader; + external = vm->external; } - module->function.args_offset = 1; - module->function.u.lambda = parser->node->u.value.data.u.lambda; - - njs_mp_free(parser->vm->mem_pool, temp->text.start); - - parser->lexer = temp->prev; - parser->target = (njs_parser_node_t *) module; - - njs_mp_free(parser->vm->mem_pool, temp); - - return njs_parser_module_after(parser, token, current); -} - - -static njs_int_t -njs_parser_module_after(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) -{ - njs_int_t ret; - njs_mod_t *module; - njs_parser_node_t *node; - - node = njs_parser_node_new(parser, 0); - if (njs_slow_path(node == NULL)) { - return NJS_ERROR; + module = loader(vm, external, name); + if (module == NULL) { + njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name); + return NULL; } - node->left = parser->node; - - module = (njs_mod_t *) parser->target; +done: if (module->index == 0) { - ret = njs_module_insert(parser, module); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } + module->index = vm->shared->module_items++; } - node->index = (njs_index_t) module; - - parser->node = node; - - return njs_parser_stack_pop(parser); + return module; } @@ -303,10 +77,10 @@ njs_module_lookup(njs_vm_t *vm, const njs_str_t *cwd, njs_module_info_t *info) njs_uint_t i; if (info->name.start[0] == '/') { - return njs_module_absolute_path(vm, info); + return njs_module_path(vm, NULL, info); } - ret = njs_module_relative_path(vm, cwd, info); + ret = njs_module_path(vm, cwd, info); if (ret != NJS_DECLINED) { return ret; @@ -319,7 +93,7 @@ njs_module_lookup(njs_vm_t *vm, const njs_str_t *cwd, njs_module_info_t *info) path = vm->paths->start; for (i = 0; i < vm->paths->items; i++) { - ret = njs_module_relative_path(vm, path, info); + ret = njs_module_path(vm, path, info); if (ret != NJS_DECLINED) { return ret; @@ -333,74 +107,60 @@ njs_module_lookup(njs_vm_t *vm, const njs_str_t *cwd, njs_module_info_t *info) static njs_int_t -njs_module_absolute_path(njs_vm_t *vm, njs_module_info_t *info) +njs_module_path(njs_vm_t *vm, const njs_str_t *dir, njs_module_info_t *info) { - njs_str_t file; - - file.length = info->name.length; - file.start = njs_mp_alloc(vm->mem_pool, file.length + 1); - if (njs_slow_path(file.start == NULL)) { - return NJS_ERROR; - } - - memcpy(file.start, info->name.start, file.length); - file.start[file.length] = '\0'; - - info->fd = open((char *) file.start, O_RDONLY); - if (info->fd < 0) { - njs_mp_free(vm->mem_pool, file.start); - return NJS_DECLINED; - } - - info->file = file; - - return NJS_OK; -} - - -static njs_int_t -njs_module_relative_path(njs_vm_t *vm, const njs_str_t *dir, - njs_module_info_t *info) -{ - u_char *p; - njs_str_t file; + char *p; + size_t length; njs_bool_t trail; + char src[NJS_MAX_PATH + 1]; - file.length = dir->length; + trail = 0; + length = info->name.length; - if (file.length == 0) { - return NJS_DECLINED; - } + if (dir != NULL) { + length = dir->length; - trail = (dir->start[dir->length - 1] != '/'); + if (length == 0) { + return NJS_DECLINED; + } - if (trail) { - file.length++; - } + trail = (dir->start[dir->length - 1] != '/'); - file.length += info->name.length; + if (trail) { + length++; + } + } - file.start = njs_mp_alloc(vm->mem_pool, file.length + 1); - if (njs_slow_path(file.start == NULL)) { + if (njs_slow_path(length > NJS_MAX_PATH)) { return NJS_ERROR; } - p = njs_cpymem(file.start, dir->start, dir->length); + p = &src[0]; - if (trail) { - *p++ = '/'; + if (dir != NULL) { + p = (char *) njs_cpymem(p, dir->start, dir->length); + + if (trail) { + *p++ = '/'; + } } - p = njs_cpymem(p, info->name.start, info->name.length); + p = (char *) njs_cpymem(p, info->name.start, info->name.length); *p = '\0'; - info->fd = open((char *) file.start, O_RDONLY); + p = realpath(&src[0], &info->path[0]); + if (p == NULL) { + return NJS_DECLINED; + } + + info->fd = open(&info->path[0], O_RDONLY); if (info->fd < 0) { - njs_mp_free(vm->mem_pool, file.start); return NJS_DECLINED; } - info->file = file; + + info->file.start = (u_char *) &info->path[0]; + info->file.length = njs_strlen(info->file.start); return NJS_OK; } @@ -412,6 +172,8 @@ njs_module_read(njs_vm_t *vm, int fd, njs_str_t *text) ssize_t n; struct stat sb; + text->start = NULL; + if (fstat(fd, &sb) == -1) { goto fail; } @@ -445,18 +207,6 @@ fail: } -static njs_bool_t -njs_module_realpath_equal(const njs_str_t *path1, const njs_str_t *path2) -{ - char rpath1[MAXPATHLEN], rpath2[MAXPATHLEN]; - - realpath((char *) path1->start, rpath1); - realpath((char *) path2->start, rpath2); - - return (strcmp(rpath1, rpath2) == 0); -} - - static njs_int_t njs_module_hash_test(njs_lvlhsh_query_t *lhq, void *data) { @@ -482,7 +232,7 @@ const njs_lvlhsh_proto_t njs_modules_hash_proto }; -static njs_mod_t * +njs_mod_t * njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared) { njs_int_t ret; @@ -533,11 +283,10 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared) njs_mod_t * -njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared) +njs_module_add(njs_vm_t *vm, njs_str_t *name) { njs_int_t ret; njs_mod_t *module; - njs_lvlhsh_t *hash; njs_lvlhsh_query_t lhq; module = njs_mp_zalloc(vm->mem_pool, sizeof(njs_mod_t)); @@ -559,9 +308,7 @@ njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared) lhq.pool = vm->mem_pool; lhq.proto = &njs_modules_hash_proto; - hash = shared ? &vm->shared->modules_hash : &vm->modules_hash; - - ret = njs_lvlhsh_insert(hash, &lhq); + ret = njs_lvlhsh_insert(&vm->shared->modules_hash, &lhq); if (njs_fast_path(ret == NJS_OK)) { return module; } @@ -575,38 +322,6 @@ njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared) } -static njs_int_t -njs_module_insert(njs_parser_t *parser, njs_mod_t *module) -{ - njs_vm_t *vm; - njs_mod_t **value; - njs_parser_scope_t *scope; - - scope = njs_parser_global_scope(parser); - vm = parser->vm; - - module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL, - NJS_VARIABLE_VAR); - scope->items++; - - if (vm->modules == NULL) { - vm->modules = njs_arr_create(vm->mem_pool, 4, sizeof(njs_mod_t *)); - if (njs_slow_path(vm->modules == NULL)) { - return NJS_ERROR; - } - } - - value = njs_arr_add(vm->modules); - if (njs_slow_path(value == NULL)) { - return NJS_ERROR; - } - - *value = module; - - return NJS_OK; -} - - njs_int_t njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused) @@ -640,3 +355,43 @@ njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return NJS_OK; } + + +static njs_mod_t * +njs_default_module_loader(njs_vm_t *vm, njs_external_ptr_t external, + njs_str_t *name) +{ + njs_int_t ret; + njs_str_t cwd, text; + njs_parser_t *prev; + njs_mod_t *module; + njs_module_info_t info; + + prev = external; + + njs_memzero(&info, sizeof(njs_module_info_t)); + + info.name = *name; + njs_file_dirname(&prev->lexer->file, &cwd); + + ret = njs_module_lookup(vm, &cwd, &info); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + + ret = njs_module_read(vm, info.fd, &text); + + (void) close(info.fd); + + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "while reading \"%V\" module", &info.file); + return NULL; + } + + module = njs_vm_compile_module(vm, &info.file, &text.start, + &text.start[text.length]); + + njs_mp_free(vm->mem_pool, text.start); + + return module; +} diff --git a/src/njs_module.h b/src/njs_module.h index 210f37cf..deb56359 100644 --- a/src/njs_module.h +++ b/src/njs_module.h @@ -8,19 +8,18 @@ #define _NJS_MODULE_H_INCLUDED_ -typedef struct { +struct njs_mod_s { njs_str_t name; njs_value_t value; njs_index_t index; njs_function_t function; -} njs_mod_t; +}; -njs_int_t njs_module_load(njs_vm_t *vm); -void njs_module_reset(njs_vm_t *vm); -njs_mod_t *njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared); -njs_int_t njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current); +njs_mod_t *njs_module_add(njs_vm_t *vm, njs_str_t *name); +njs_mod_t *njs_module_find(njs_vm_t *vm, njs_str_t *name, + njs_bool_t shared); +njs_mod_t *njs_parser_module(njs_parser_t *parser, njs_str_t *name); njs_int_t njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); diff --git a/src/njs_parser.c b/src/njs_parser.c index bfd0e9e7..13c03b00 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -433,10 +433,6 @@ static njs_int_t njs_parser_export_after(njs_parser_t *parser, static njs_int_t njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_import_after(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current); -static njs_int_t njs_parser_module_lambda_after(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_export_sink(njs_parser_t *parser); static njs_parser_node_t *njs_parser_return_set(njs_parser_t *parser, @@ -558,7 +554,9 @@ njs_parser(njs_vm_t *vm, njs_parser_t *parser) njs_set_undefined(&vm->retval); if (parser->scope == NULL) { - ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL, 1); + ret = njs_parser_scope_begin(parser, + parser->module ? NJS_SCOPE_FUNCTION + : NJS_SCOPE_GLOBAL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -619,10 +617,18 @@ njs_parser(njs_vm_t *vm, njs_parser_t *parser) } } - parser->node->token_type = NJS_TOKEN_END; - parser->node->token_line = parser->lexer->line; + if (parser->module) { + ret = njs_parser_export_sink(parser); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + } else { + parser->node->token_type = NJS_TOKEN_END; + parser->node->token_line = parser->lexer->line; - njs_parser_chain_top_set(parser, parser->node); + njs_parser_chain_top_set(parser, parser->node); + } return NJS_OK; } @@ -655,7 +661,6 @@ static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type, njs_bool_t init_this) { - njs_lexer_t *lexer; njs_variable_t *var; njs_parser_scope_t *scope, *parent; const njs_lexer_keyword_entry_t *keyword; @@ -673,13 +678,6 @@ njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type, njs_rbtree_init(&scope->labels, njs_parser_scope_rbtree_compare); njs_rbtree_init(&scope->references, njs_parser_scope_rbtree_compare); - lexer = parser->lexer; - - if (lexer->file.length != 0) { - njs_file_basename(&lexer->file, &scope->file); - njs_file_dirname(&lexer->file, &scope->cwd); - } - parent = parser->scope; scope->parent = parent; parser->scope = scope; @@ -4681,19 +4679,47 @@ static njs_int_t njs_parser_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_parser_node_t *stmt; + njs_parser_node_t *stmt, *last, *new_node, **child; + njs_parser_node_t *node, *top; + + child = &parser->target; + last = *child; + + new_node = parser->node; + + if (new_node->hoist) { + child = &njs_parser_chain_top(parser); + + while (*child != NULL) { + node = *child; + + if (node->hoist) { + break; + } + + child = &node->left; + } + + last = *child; + } stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NJS_ERROR; } - stmt->left = parser->target; - stmt->right = parser->node; + stmt->hoist = new_node->hoist; + stmt->left = last; + stmt->right = new_node; - parser->node = stmt; + *child = stmt; - njs_parser_chain_top_set(parser, stmt); + top = (child != &parser->target) ? njs_parser_chain_top(parser) + : stmt; + + parser->node = top; + + njs_parser_chain_top_set(parser, top); return njs_parser_stack_pop(parser); } @@ -5978,11 +6004,11 @@ njs_parser_return_statement(njs_parser_t *parser, njs_lexer_token_t *token, scope != NULL; scope = scope->parent) { - if (scope->type == NJS_SCOPE_FUNCTION && !scope->module) { + if (scope->type == NJS_SCOPE_FUNCTION) { break; } - if (scope->type == NJS_SCOPE_GLOBAL) { + if (scope->parent == NULL) { njs_parser_syntax_error(parser, "Illegal return statement"); return NJS_ERROR; } @@ -7665,7 +7691,7 @@ njs_parser_export(njs_parser_t *parser, njs_lexer_token_t *token, { njs_parser_node_t *node; - if (!parser->scope->module) { + if (!parser->module) { njs_parser_syntax_error(parser, "Illegal export statement"); return NJS_DONE; } @@ -7710,9 +7736,10 @@ static njs_int_t njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { + njs_variable_t *var; njs_parser_node_t *name, *import; - if (parser->scope->type != NJS_SCOPE_GLOBAL && !parser->scope->module) { + if (parser->scope->parent != NULL) { njs_parser_syntax_error(parser, "Illegal import statement"); return NJS_DONE; } @@ -7722,12 +7749,14 @@ njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, return NJS_DONE; } - name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR, - NULL); + name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_LET, + &var); if (name == NULL) { return NJS_ERROR; } + var->init = 1; + name->token_line = token->line; njs_lexer_consume_token(parser->lexer, 1); @@ -7757,129 +7786,27 @@ njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, return NJS_ERROR; } + import->hoist = 1; import->token_line = parser->line; import->left = name; - njs_parser_next(parser, njs_parser_module); - - return njs_parser_after(parser, current, import, 1, - njs_parser_import_after); -} - - -static njs_int_t -njs_parser_import_after(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) -{ - if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { - return njs_parser_failed(parser); - } - - parser->target->right = parser->node; - - parser->node = parser->target; - - return njs_parser_stack_pop(parser); -} - - -static const njs_lexer_entry_t njs_parser_module_entry = -{ - .name = njs_str("module") -}; - - -njs_int_t -njs_parser_module_lambda(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) -{ - njs_int_t ret; - uintptr_t unique_id; - njs_variable_t *var; - njs_parser_node_t *node, *parent; - njs_function_lambda_t *lambda; - - node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); - if (node == NULL) { - return NJS_ERROR; - } - - ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0); - if (ret != NJS_OK) { - return NJS_ERROR; - } - - node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME); - if (node->left == NULL) { - return NJS_ERROR; - } - - unique_id = (uintptr_t) &njs_parser_module_entry; - - var = njs_variable_scope_add(parser, parser->scope, parser->scope, - unique_id, NJS_VARIABLE_FUNCTION, 1); - if (var == NULL) { - return NJS_ERROR; - } - - ret = njs_parser_variable_reference(parser, parser->scope, node->left, - unique_id, NJS_DECLARATION); - if (ret != NJS_OK) { + import->u.module = njs_parser_module(parser, &token->text); + if (njs_slow_path(import->u.module == NULL)) { return NJS_ERROR; } - node->left->u.reference.variable = var; + njs_lexer_consume_token(parser->lexer, 1); - lambda = njs_function_lambda_alloc(parser->vm, 1); - if (lambda == NULL) { + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { return NJS_ERROR; } - node->token_line = token->line; - node->u.value.data.u.lambda = lambda; - - parser->node = node; - - parser->scope->module = 1; - - parent = parser->node; - parser->node = NULL; - - njs_parser_next(parser, njs_parser_statement_list); - - return njs_parser_after(parser, current, parent, 0, - njs_parser_module_lambda_after); -} - - -static njs_int_t -njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token, - njs_queue_link_t *current) -{ - njs_int_t ret; - njs_variable_t *var, **vv; - - ret = njs_parser_export_sink(parser); - if (ret != NJS_OK) { - return NJS_ERROR; + if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { + return njs_parser_failed(parser); } - parser->target->right = njs_parser_chain_top(parser); - - parser->node = parser->target; - - vv = &parser->target->left->u.reference.variable; - - var = *vv; - *vv = NULL; - - var->index = njs_scope_index(var->scope->type, var->scope->items, - NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); - var->scope->items++; - - parser->node->u.value.data.u.lambda->self = var->index; - - njs_parser_scope_end(parser); + parser->node = import; return njs_parser_stack_pop(parser); } @@ -8074,7 +8001,7 @@ njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token) scope = njs_function_scope(scope->parent); } - if (scope->type == NJS_SCOPE_GLOBAL) { + if (scope->parent == NULL) { njs_parser_syntax_error(parser, "\"%V\" object in global scope", &token->text); return NULL; @@ -8818,21 +8745,18 @@ njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser, static void -njs_parser_scope_error(njs_vm_t *vm, njs_parser_scope_t *scope, - njs_object_type_t type, uint32_t line, const char *fmt, va_list args) +njs_parser_error(njs_vm_t *vm, njs_object_type_t type, njs_str_t *file, + uint32_t line, const char *fmt, va_list args) { size_t width; u_char msg[NJS_MAX_ERROR_STR]; u_char *p, *end; - njs_str_t *file; njs_int_t ret; njs_value_t value; static const njs_value_t file_name = njs_string("fileName"); static const njs_value_t line_number = njs_string("lineNumber"); - file = &scope->file; - p = msg; end = msg + NJS_MAX_ERROR_STR; @@ -8878,20 +8802,20 @@ njs_parser_lexer_error(njs_parser_t *parser, njs_object_type_t type, } va_start(args, fmt); - njs_parser_scope_error(parser->vm, parser->scope, type, - parser->lexer->line, fmt, args); + njs_parser_error(parser->vm, type, &parser->lexer->file, + parser->lexer->line, fmt, args); va_end(args); } void -njs_parser_node_error(njs_vm_t *vm, njs_parser_node_t *node, - njs_object_type_t type, const char *fmt, ...) +njs_parser_node_error(njs_vm_t *vm, njs_object_type_t type, + njs_parser_node_t *node, njs_str_t *file, const char *fmt, ...) { va_list args; va_start(args, fmt); - njs_parser_scope_error(vm, node->scope, type, node->token_line, fmt, args); + njs_parser_error(vm, type, file, node->token_line, fmt, args); va_end(args); } diff --git a/src/njs_parser.h b/src/njs_parser.h index 15c01c7e..d4d188de 100644 --- a/src/njs_parser.h +++ b/src/njs_parser.h @@ -23,11 +23,7 @@ struct njs_parser_scope_s { uint32_t temp; uint32_t items; - njs_str_t cwd; - njs_str_t file; - njs_scope_t type:8; - uint8_t module; uint8_t arrow_function; uint8_t dest_disable; uint8_t async; @@ -38,6 +34,7 @@ struct njs_parser_scope_s { struct njs_parser_node_s { njs_token_type_t token_type:16; uint8_t ctor:1; + uint8_t hoist:1; uint8_t temporary; /* 1 bit */ uint32_t token_line; @@ -47,6 +44,7 @@ struct njs_parser_node_s { njs_value_t value; njs_vmcode_operation_t operation; njs_parser_node_t *object; + njs_mod_t *module; } u; njs_str_t name; @@ -83,7 +81,11 @@ struct njs_parser_s { njs_variable_type_t var_type; njs_int_t ret; uintptr_t undefined_id; + + uint8_t module; njs_bool_t strict_semicolon; + + njs_str_t file; uint32_t line; }; @@ -125,8 +127,6 @@ njs_int_t njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_uint_t runtime); njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser); -njs_int_t njs_parser_module_lambda(njs_parser_t *parser, - njs_lexer_token_t *token, njs_queue_link_t *current); njs_bool_t njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope); njs_variable_t *njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node); @@ -141,8 +141,8 @@ njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token, njs_value_t *value); void njs_parser_lexer_error(njs_parser_t *parser, njs_object_type_t type, const char *fmt, ...); -void njs_parser_node_error(njs_vm_t *vm, njs_parser_node_t *node, - njs_object_type_t type, const char *fmt, ...); +void njs_parser_node_error(njs_vm_t *vm, njs_object_type_t type, + njs_parser_node_t *node, njs_str_t *file, const char *fmt, ...); njs_int_t njs_parser_traverse(njs_vm_t *vm, njs_parser_node_t *root, void *ctx, njs_parser_traverse_cb_t cb); @@ -214,21 +214,6 @@ njs_parser_node_string(njs_vm_t *vm, njs_lexer_token_t *token, } -njs_inline njs_parser_scope_t * -njs_parser_global_scope(njs_parser_t *parser) -{ - njs_parser_scope_t *scope; - - scope = parser->scope; - - while (scope->type != NJS_SCOPE_GLOBAL) { - scope = scope->parent; - } - - return scope; -} - - njs_inline njs_parser_scope_t * njs_function_scope(njs_parser_scope_t *scope) { diff --git a/src/njs_shell.c b/src/njs_shell.c index 257591de..237d6bae 100644 --- a/src/njs_shell.c +++ b/src/njs_shell.c @@ -203,7 +203,8 @@ static const njs_lvlhsh_proto_t njs_timelabel_hash_proto njs_aligned(64) = { static njs_vm_ops_t njs_console_ops = { njs_console_set_timer, - njs_console_clear_timer + njs_console_clear_timer, + NULL, }; @@ -230,7 +231,6 @@ static njs_console_t njs_console; int main(int argc, char **argv) { - char path[MAXPATHLEN], *p; njs_vm_t *vm; njs_int_t ret; njs_opts_t opts; @@ -257,21 +257,8 @@ main(int argc, char **argv) njs_vm_opt_init(&vm_options); if (opts.file == NULL) { - p = getcwd(path, sizeof(path)); - if (p == NULL) { - njs_stderror("getcwd() failed:%s\n", strerror(errno)); - ret = NJS_ERROR; - goto done; - } - - if (opts.command == NULL) { - memcpy(path + njs_strlen(path), "/shell", sizeof("/shell")); - - } else { - memcpy(path + njs_strlen(path), "/string", sizeof("/string")); - } - - opts.file = path; + opts.file = (opts.command == NULL) ? (char *) "shell" + : (char *) "string"; } vm_options.file.start = (u_char *) opts.file; diff --git a/src/njs_variable.c b/src/njs_variable.c index d3a3fd81..9ea34237 100644 --- a/src/njs_variable.c +++ b/src/njs_variable.c @@ -222,7 +222,7 @@ njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope, return root; } - module = parser->vm->options.module || scope->module; + module = parser->vm->options.module || parser->module; if (module) { if (type == NJS_VARIABLE_FUNCTION diff --git a/src/njs_vm.c b/src/njs_vm.c index 86bce1a8..dadec63c 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -148,9 +148,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) njs_generator_t generator; njs_parser_scope_t *scope; - if (vm->modules != NULL) { - njs_module_reset(vm); - } + vm->codes = NULL; ret = njs_parser_init(vm, &parser, vm->global_scope, &vm->options.file, *start, end, 0); @@ -183,7 +181,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) *start = parser.lexer->start; scope = parser.scope; - ret = njs_generator_init(&generator, 0, 0); + ret = njs_generator_init(&generator, &vm->options.file, 0, 0); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_generator_init() failed"); return NJS_ERROR; @@ -247,6 +245,79 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) } +njs_mod_t * +njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start, + u_char *end) +{ + njs_int_t ret; + njs_arr_t *arr; + njs_mod_t *module; + njs_parser_t parser; + njs_vm_code_t *code; + njs_generator_t generator; + njs_parser_scope_t *scope; + njs_function_lambda_t *lambda; + + module = njs_module_find(vm, name, 1); + if (module != NULL) { + return module; + } + + module = njs_module_add(vm, name); + if (njs_slow_path(module == NULL)) { + return NULL; + } + + ret = njs_parser_init(vm, &parser, NULL, name, *start, end, 1); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + + parser.module = 1; + + ret = njs_parser(vm, &parser); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + + *start = parser.lexer->start; + + ret = njs_generator_init(&generator, &module->name, 0, 0); + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "njs_generator_init() failed"); + return NULL; + } + + code = njs_generate_scope(vm, &generator, parser.scope, &njs_entry_module); + if (njs_slow_path(code == NULL)) { + njs_internal_error(vm, "njs_generate_scope() failed"); + + return NULL; + } + + lambda = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t)); + if (njs_fast_path(lambda == NULL)) { + njs_memory_error(vm); + return NULL; + } + + scope = parser.scope; + + lambda->start = generator.code_start; + lambda->nlocal = scope->items; + lambda->temp = scope->temp; + + arr = scope->declarations; + lambda->declarations = (arr != NULL) ? arr->start : NULL; + lambda->ndeclarations = (arr != NULL) ? arr->items : 0; + + module->function.args_offset = 1; + module->function.u.lambda = lambda; + + return module; +} + + njs_vm_t * njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external) { @@ -479,11 +550,6 @@ njs_vm_start(njs_vm_t *vm) { njs_int_t ret; - ret = njs_module_load(vm); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - ret = njs_vmcode_interpreter(vm, vm->start, NULL, NULL); return (ret == NJS_ERROR) ? NJS_ERROR : NJS_OK; diff --git a/src/njs_vm.h b/src/njs_vm.h index b8f09b56..f49a3022 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -229,6 +229,7 @@ struct njs_vm_shared_s { njs_lvlhsh_t arguments_object_instance_hash; njs_lvlhsh_t regexp_instance_hash; + size_t module_items; njs_lvlhsh_t modules_hash; njs_lvlhsh_t env_hash; diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 3039642c..ef0beb5e 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -41,6 +41,8 @@ static njs_jump_off_t njs_vmcode_debugger(njs_vm_t *vm); static njs_jump_off_t njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval); +static njs_jump_off_t njs_vmcode_import(njs_vm_t *vm, njs_mod_t *module, + njs_value_t *retval); static njs_jump_off_t njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, njs_promise_capability_t *pcap, njs_async_ctx_t *actx); @@ -97,6 +99,7 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, void *promise_cap, njs_vmcode_await_t *await; njs_native_frame_t *previous, *native; njs_property_next_t *next; + njs_vmcode_import_t *import; njs_vmcode_finally_t *finally; njs_vmcode_generic_t *vmcode; njs_vmcode_variable_t *var; @@ -826,6 +829,16 @@ next: break; + case NJS_VMCODE_IMPORT: + import = (njs_vmcode_import_t *) pc; + retval = njs_scope_value(vm, import->retval); + ret = njs_vmcode_import(vm, import->module, retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } + + break; + case NJS_VMCODE_AWAIT: await = (njs_vmcode_await_t *) pc; return njs_vmcode_await(vm, await, promise_cap, async_ctx); @@ -1813,6 +1826,61 @@ njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval) } +static njs_jump_off_t +njs_vmcode_import(njs_vm_t *vm, njs_mod_t *module, njs_value_t *retval) +{ + njs_int_t ret; + njs_arr_t *m; + njs_value_t *value; + njs_object_t *object; + + if (vm->modules == NULL) { + vm->modules = njs_arr_create(vm->mem_pool, 4, sizeof(njs_value_t)); + if (njs_slow_path(vm->modules == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } + + m = vm->modules; + + value = njs_arr_add_multiple(m, vm->shared->module_items); + if (njs_slow_path(value == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } + + njs_memzero(m->start, m->items * sizeof(njs_value_t)); + } + + value = njs_arr_item(vm->modules, module->index); + + if (!njs_is_null(value)) { + njs_value_assign(retval, value); + return sizeof(njs_vmcode_import_t); + } + + if (module->function.native) { + njs_value_assign(value, &module->value); + + object = njs_object_value_copy(vm, value); + if (njs_slow_path(object == NULL)) { + return NJS_ERROR; + } + + } else { + njs_set_invalid(value); + ret = njs_vm_invoke(vm, &module->function, NULL, 0, value); + if (ret == NJS_ERROR) { + return ret; + } + } + + njs_value_assign(retval, value); + + return sizeof(njs_vmcode_import_t); +} + + static njs_jump_off_t njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, njs_promise_capability_t *pcap, njs_async_ctx_t *ctx) diff --git a/src/njs_vmcode.h b/src/njs_vmcode.h index 6b8f6558..3bf146cc 100644 --- a/src/njs_vmcode.h +++ b/src/njs_vmcode.h @@ -49,6 +49,7 @@ enum { NJS_VMCODE_THIS, NJS_VMCODE_ARGUMENTS, NJS_VMCODE_PROTO_INIT, + NJS_VMCODE_IMPORT, NJS_VMCODE_AWAIT, @@ -419,6 +420,13 @@ typedef struct { } njs_vmcode_function_copy_t; +typedef struct { + njs_vmcode_t code; + njs_index_t retval; + njs_mod_t *module; +} njs_vmcode_import_t; + + typedef struct { njs_vmcode_t code; njs_index_t dst; diff --git a/test/js/import_cyclic.t.js b/test/js/import_cyclic.t.js new file mode 100644 index 00000000..ef965d69 --- /dev/null +++ b/test/js/import_cyclic.t.js @@ -0,0 +1,11 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module/] +---*/ + +import a from 'cyclic_a.js'; +import b from 'cyclic_b.js'; + +assert.sameValue(a, 10); +assert.sameValue(b, 11); diff --git a/test/js/import_expression.t.js b/test/js/import_expression.t.js index bf6fd5fb..85e4eb21 100644 --- a/test/js/import_expression.t.js +++ b/test/js/import_expression.t.js @@ -6,4 +6,5 @@ paths: [test/js/module] import m from 'export_expression.js'; +assert.sameValue(typeof m, 'object'); assert.sameValue(m.sum(3,4), 7); diff --git a/test/js/import_global_ref.t.js b/test/js/import_global_ref.t.js new file mode 100644 index 00000000..c90288ee --- /dev/null +++ b/test/js/import_global_ref.t.js @@ -0,0 +1,13 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module/] +---*/ + +import http from 'http.js'; +import jwt from 'jwt.js'; + +globalThis.http = http; +globalThis.jwt = jwt; + +assert.sameValue(http.check(), "JWT-OK"); diff --git a/test/js/import_global_ref_var.t.js b/test/js/import_global_ref_var.t.js new file mode 100644 index 00000000..0aae6e01 --- /dev/null +++ b/test/js/import_global_ref_var.t.js @@ -0,0 +1,10 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module/] +---*/ + +var a = 42; +import m from 'export_global_a.js'; + +assert.sameValue(m.f(), 42); diff --git a/test/js/import_order.t.js b/test/js/import_order.t.js new file mode 100644 index 00000000..05de705b --- /dev/null +++ b/test/js/import_order.t.js @@ -0,0 +1,18 @@ +/*--- +includes: [compareArray.js] +flags: [] +paths: [test/js/module/] +---*/ + +if (!globalThis.stages) { + globalThis.stages = []; +} + +globalThis.stages.push('main1'); + +import _ from 'order.js'; +import __ from 'order2.js'; + +globalThis.stages.push('main2'); + +assert.compareArray(globalThis.stages, ["order", "order2", "main1", "main2"]); diff --git a/test/js/import_recursive.t.js b/test/js/import_recursive.t.js index 976e23d3..e68ff013 100644 --- a/test/js/import_recursive.t.js +++ b/test/js/import_recursive.t.js @@ -2,8 +2,8 @@ includes: [] flags: [] paths: [test/js/module/] -negative: - phase: runtime ---*/ -import lib from 'import_recursive.t.js'; +import m from 'recursive.js'; + +assert.sameValue(m, 42); diff --git a/test/js/import_recursive_early_access.t.js b/test/js/import_recursive_early_access.t.js new file mode 100644 index 00000000..a16f48f7 --- /dev/null +++ b/test/js/import_recursive_early_access.t.js @@ -0,0 +1,9 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module/] +negative: + phase: runtime +---*/ + +import _ from 'recursive_early_access.js'; diff --git a/test/js/import_recursive_relative.t.js b/test/js/import_recursive_relative.t.js new file mode 100644 index 00000000..2b7bc74c --- /dev/null +++ b/test/js/import_recursive_relative.t.js @@ -0,0 +1,9 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module/] +---*/ + +import m from './recursive_relative.js'; + +assert.sameValue(m, 42); diff --git a/test/js/import_sinking_export_default.t.js b/test/js/import_sinking_export_default.t.js new file mode 100644 index 00000000..0aa2b999 --- /dev/null +++ b/test/js/import_sinking_export_default.t.js @@ -0,0 +1,10 @@ +/*--- +includes: [] +flags: [] +paths: [test/js/module] +---*/ + +import m from 'sinking_export_default.js'; + +assert.sameValue(typeof m, 'object'); +assert.sameValue(m.a, 42); diff --git a/test/js/module/cyclic_a.js b/test/js/module/cyclic_a.js new file mode 100644 index 00000000..c4ef312d --- /dev/null +++ b/test/js/module/cyclic_a.js @@ -0,0 +1,2 @@ +import _ from 'cyclic_b.js'; +export default 10; diff --git a/test/js/module/cyclic_b.js b/test/js/module/cyclic_b.js new file mode 100644 index 00000000..5a1bce0d --- /dev/null +++ b/test/js/module/cyclic_b.js @@ -0,0 +1,2 @@ +import _ from 'cyclic_a.js'; +export default 11; diff --git a/test/js/module/export_global_a.js b/test/js/module/export_global_a.js new file mode 100644 index 00000000..827c1498 --- /dev/null +++ b/test/js/module/export_global_a.js @@ -0,0 +1,5 @@ +function f() { + return a; +} + +export default {f}; diff --git a/test/js/module/http.js b/test/js/module/http.js new file mode 100644 index 00000000..5a677f1c --- /dev/null +++ b/test/js/module/http.js @@ -0,0 +1,7 @@ +var http = {}; + +http.check = function() { + return jwt.verify(); +} + +export default http; diff --git a/test/js/module/jwt.js b/test/js/module/jwt.js new file mode 100644 index 00000000..a5bfe3ee --- /dev/null +++ b/test/js/module/jwt.js @@ -0,0 +1,7 @@ +var jwt = {}; + +jwt.verify = function() { + return "JWT-OK"; +} + +export default jwt; diff --git a/test/js/module/lib1.js b/test/js/module/lib1.js index 4b249b73..67dd2cf8 100644 --- a/test/js/module/lib1.js +++ b/test/js/module/lib1.js @@ -8,4 +8,9 @@ function get() { return state.count; } +if (globalThis.lib1_is_loaded) { + throw Error("lib1 already loaded"); + globalThis.lib1_is_loaded = true; +} + export default {inc, get} diff --git a/test/js/module/order.js b/test/js/module/order.js new file mode 100644 index 00000000..ce5e53ec --- /dev/null +++ b/test/js/module/order.js @@ -0,0 +1,7 @@ +if (!globalThis.stages) { + globalThis.stages = []; +} + +globalThis.stages.push('order'); + +export default 1; diff --git a/test/js/module/order2.js b/test/js/module/order2.js new file mode 100644 index 00000000..54c95758 --- /dev/null +++ b/test/js/module/order2.js @@ -0,0 +1,7 @@ +if (!globalThis.stages) { + globalThis.stages = []; +} + +globalThis.stages.push('order2'); + +export default 1; diff --git a/test/js/module/recursive.js b/test/js/module/recursive.js new file mode 100644 index 00000000..8e1131ad --- /dev/null +++ b/test/js/module/recursive.js @@ -0,0 +1,2 @@ +import _ from 'recursive.js'; +export default 42; diff --git a/test/js/module/recursive_early_access.js b/test/js/module/recursive_early_access.js new file mode 100644 index 00000000..0bf09ba8 --- /dev/null +++ b/test/js/module/recursive_early_access.js @@ -0,0 +1,5 @@ +import m from 'recursive_early_access.js'; + +m.a; + +export default 42; diff --git a/test/js/module/recursive_relative.js b/test/js/module/recursive_relative.js new file mode 100644 index 00000000..61a2ba46 --- /dev/null +++ b/test/js/module/recursive_relative.js @@ -0,0 +1,2 @@ +import _ from './recursive_relative.js'; +export default 42; diff --git a/test/js/module/sinking_export_default.js b/test/js/module/sinking_export_default.js new file mode 100644 index 00000000..9699ca2d --- /dev/null +++ b/test/js/module/sinking_export_default.js @@ -0,0 +1,5 @@ +let o = {}; + +export default o; + +o.a = 42;