Last active
March 1, 2019 18:20
-
-
Save xeioex/2cf233c9d2e532c3cd794362b8d21553 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # HG changeset patch | |
| # User hongzhidao <[email protected]> | |
| # Date 1551362279 -28800 | |
| # Thu Feb 28 21:57:59 2019 +0800 | |
| # Node ID 28019254dad6a30d0282525edbf2d043817a0188 | |
| # Parent 195158f4a6a791bae680bd26842494a174b8940a | |
| Added initial modules support. | |
| diff --git a/njs/njs.c b/njs/njs.c | |
| --- a/njs/njs.c | |
| +++ b/njs/njs.c | |
| @@ -335,6 +335,8 @@ njs_vm_clone(njs_vm_t *vm, njs_external_ | |
| nvm->variables_hash = vm->variables_hash; | |
| nvm->values_hash = vm->values_hash; | |
| + | |
| + nvm->modules = vm->modules; | |
| nvm->modules_hash = vm->modules_hash; | |
| nvm->externals_hash = vm->externals_hash; | |
| @@ -583,6 +585,11 @@ njs_vm_start(njs_vm_t *vm) | |
| { | |
| njs_ret_t ret; | |
| + ret = njs_module_load(vm); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return ret; | |
| + } | |
| + | |
| ret = njs_vmcode_interpreter(vm); | |
| if (ret == NJS_STOP) { | |
| @@ -631,6 +638,30 @@ njs_vm_handle_events(njs_vm_t *vm) | |
| } | |
| +nxt_int_t | |
| +njs_vm_add_path(njs_vm_t *vm, nxt_str_t *path) | |
| +{ | |
| + nxt_str_t *item; | |
| + | |
| + if (vm->paths == NULL) { | |
| + vm->paths = nxt_array_create(4, sizeof(nxt_str_t), | |
| + &njs_array_mem_proto, vm->mem_pool); | |
| + if (nxt_slow_path(vm->paths == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + } | |
| + | |
| + item = nxt_array_add(vm->paths, &njs_array_mem_proto, vm->mem_pool); | |
| + if (nxt_slow_path(item == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + *item = *path; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| nxt_noinline njs_value_t * | |
| njs_vm_retval(njs_vm_t *vm) | |
| { | |
| diff --git a/njs/njs.h b/njs/njs.h | |
| --- a/njs/njs.h | |
| +++ b/njs/njs.h | |
| @@ -215,6 +215,8 @@ NXT_EXPORT nxt_int_t njs_vm_run(njs_vm_t | |
| */ | |
| NXT_EXPORT nxt_int_t njs_vm_start(njs_vm_t *vm); | |
| +NXT_EXPORT nxt_int_t njs_vm_add_path(njs_vm_t *vm, nxt_str_t *path); | |
| + | |
| NXT_EXPORT const njs_extern_t *njs_vm_external_prototype(njs_vm_t *vm, | |
| njs_external_t *external); | |
| NXT_EXPORT nxt_int_t njs_vm_external_create(njs_vm_t *vm, | |
| diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c | |
| --- a/njs/njs_builtin.c | |
| +++ b/njs/njs_builtin.c | |
| @@ -294,6 +294,8 @@ njs_builtin_objects_create(njs_vm_t *vm) | |
| return NJS_ERROR; | |
| } | |
| + module->function.native = 1; | |
| + | |
| ret = njs_object_hash_create(vm, &module->object.shared_hash, | |
| obj->properties, obj->items); | |
| if (nxt_slow_path(ret != NXT_OK)) { | |
| diff --git a/njs/njs_core.h b/njs/njs_core.h | |
| --- a/njs/njs_core.h | |
| +++ b/njs/njs_core.h | |
| @@ -7,6 +7,7 @@ | |
| #ifndef _NJS_CORE_H_INCLUDED_ | |
| #define _NJS_CORE_H_INCLUDED_ | |
| + | |
| #include <nxt_auto_config.h> | |
| #include <nxt_unix.h> | |
| @@ -45,7 +46,8 @@ | |
| #include <njs_error.h> | |
| #include <njs_event.h> | |
| +#include <njs_extern.h> | |
| +#include <njs_module.h> | |
| -#include <njs_extern.h> | |
| #endif /* _NJS_CORE_H_INCLUDED_ */ | |
| diff --git a/njs/njs_generator.c b/njs/njs_generator.c | |
| --- a/njs/njs_generator.c | |
| +++ b/njs/njs_generator.c | |
| @@ -153,6 +153,10 @@ static nxt_int_t njs_generate_try_statem | |
| njs_generator_t *generator, njs_parser_node_t *node); | |
| static nxt_int_t njs_generate_throw_statement(njs_vm_t *vm, | |
| njs_generator_t *generator, njs_parser_node_t *node); | |
| +static nxt_int_t njs_generate_import_statement(njs_vm_t *vm, | |
| + njs_generator_t *generator, njs_parser_node_t *node); | |
| +static nxt_int_t njs_generate_export_statement(njs_vm_t *vm, | |
| + njs_generator_t *generator, njs_parser_node_t *node); | |
| static nxt_noinline njs_index_t njs_generate_dest_index(njs_vm_t *vm, | |
| njs_generator_t *generator, njs_parser_node_t *node); | |
| static nxt_noinline njs_index_t | |
| @@ -466,6 +470,12 @@ njs_generator(njs_vm_t *vm, njs_generato | |
| case NJS_TOKEN_THROW: | |
| return njs_generate_throw_statement(vm, generator, node); | |
| + case NJS_TOKEN_IMPORT: | |
| + return njs_generate_import_statement(vm, generator, node); | |
| + | |
| + case NJS_TOKEN_EXPORT: | |
| + return njs_generate_export_statement(vm, generator, node); | |
| + | |
| default: | |
| nxt_thread_log_debug("unknown token: %d", node->token); | |
| njs_internal_error(vm, "Generator failed: unknown token"); | |
| @@ -3010,6 +3020,67 @@ njs_generate_throw_statement(njs_vm_t *v | |
| } | |
| +static nxt_int_t | |
| +njs_generate_import_statement(njs_vm_t *vm, njs_generator_t *generator, | |
| + njs_parser_node_t *node) | |
| +{ | |
| + nxt_int_t ret; | |
| + njs_index_t index; | |
| + njs_module_t *module; | |
| + njs_parser_node_t *lvalue, *expr; | |
| + njs_vmcode_object_copy_t *copy; | |
| + | |
| + lvalue = node->left; | |
| + expr = node->right; | |
| + | |
| + index = njs_variable_index(vm, lvalue); | |
| + if (nxt_slow_path(index == NJS_INDEX_ERROR)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + if (expr->left != NULL) { | |
| + ret = njs_generator(vm, generator, expr->left); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return ret; | |
| + } | |
| + } | |
| + | |
| + module = (njs_module_t *) expr->index; | |
| + | |
| + njs_generate_code(generator, njs_vmcode_object_copy_t, copy, | |
| + njs_vmcode_object_copy, 2, 1); | |
| + copy->retval = index; | |
| + copy->object = module->index; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| +njs_generate_export_statement(njs_vm_t *vm, njs_generator_t *generator, | |
| + njs_parser_node_t *node) | |
| +{ | |
| + nxt_int_t ret; | |
| + njs_parser_node_t *obj; | |
| + njs_vmcode_return_t *code; | |
| + | |
| + obj = node->right; | |
| + | |
| + ret = njs_generator(vm, generator, obj); | |
| + | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return ret; | |
| + } | |
| + | |
| + njs_generate_code(generator, njs_vmcode_return_t, code, | |
| + njs_vmcode_return, 1, 0); | |
| + code->retval = obj->index; | |
| + node->index = obj->index; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| static nxt_noinline njs_index_t | |
| njs_generate_dest_index(njs_vm_t *vm, njs_generator_t *generator, | |
| njs_parser_node_t *node) | |
| diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c | |
| --- a/njs/njs_lexer_keyword.c | |
| +++ b/njs/njs_lexer_keyword.c | |
| @@ -93,6 +93,11 @@ static const njs_keyword_t njs_keywords | |
| { nxt_string("setImmediate"), NJS_TOKEN_SET_IMMEDIATE, 0 }, | |
| { nxt_string("clearTimeout"), NJS_TOKEN_CLEAR_TIMEOUT, 0 }, | |
| + /* Module. */ | |
| + { nxt_string("import"), NJS_TOKEN_IMPORT, 0 }, | |
| + { nxt_string("from"), NJS_TOKEN_FROM, 0 }, | |
| + { nxt_string("export"), NJS_TOKEN_EXPORT, 0 }, | |
| + | |
| /* Reserved words. */ | |
| { nxt_string("await"), NJS_TOKEN_RESERVED, 0 }, | |
| @@ -100,10 +105,8 @@ static const njs_keyword_t njs_keywords | |
| { nxt_string("const"), NJS_TOKEN_RESERVED, 0 }, | |
| { nxt_string("debugger"), NJS_TOKEN_RESERVED, 0 }, | |
| { nxt_string("enum"), NJS_TOKEN_RESERVED, 0 }, | |
| - { nxt_string("export"), NJS_TOKEN_RESERVED, 0 }, | |
| { nxt_string("extends"), NJS_TOKEN_RESERVED, 0 }, | |
| { nxt_string("implements"), NJS_TOKEN_RESERVED, 0 }, | |
| - { nxt_string("import"), NJS_TOKEN_RESERVED, 0 }, | |
| { nxt_string("interface"), NJS_TOKEN_RESERVED, 0 }, | |
| { nxt_string("let"), NJS_TOKEN_RESERVED, 0 }, | |
| { nxt_string("package"), NJS_TOKEN_RESERVED, 0 }, | |
| diff --git a/njs/njs_module.c b/njs/njs_module.c | |
| --- a/njs/njs_module.c | |
| +++ b/njs/njs_module.c | |
| @@ -8,6 +8,375 @@ | |
| #include <njs_module.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| +#include <errno.h> | |
| +#include <fcntl.h> | |
| +#include <string.h> | |
| +#include <stdlib.h> | |
| +#include <sys/stat.h> | |
| +#include <limits.h> | |
| + | |
| + | |
| +typedef struct { | |
| + int fd; | |
| + nxt_str_t *name; | |
| + nxt_str_t file; | |
| + nxt_bool_t relative; | |
| +} njs_module_info_t; | |
| + | |
| + | |
| +static nxt_int_t njs_module_lookup(njs_vm_t *vm, nxt_str_t *cwd, | |
| + njs_module_info_t *info); | |
| +static nxt_int_t njs_module_absolute_path(njs_vm_t *vm, njs_module_info_t *info); | |
| +static nxt_int_t njs_module_relative_path(njs_vm_t *vm, nxt_str_t *path, | |
| + njs_module_info_t *info); | |
| +static nxt_int_t njs_module_read(njs_vm_t *vm, int fd, nxt_str_t *body); | |
| +static njs_module_t *njs_module_find(njs_vm_t *vm, nxt_str_t *name); | |
| +static njs_module_t *njs_module_add(njs_vm_t *vm, nxt_str_t *name); | |
| +static nxt_int_t njs_module_insert(njs_vm_t *vm, njs_module_t *module); | |
| + | |
| + | |
| +nxt_int_t | |
| +njs_module_load(njs_vm_t *vm) | |
| +{ | |
| + nxt_int_t ret; | |
| + nxt_uint_t i; | |
| + njs_value_t *value; | |
| + njs_module_t **item, *module; | |
| + | |
| + if (vm->modules == NULL) { | |
| + return NXT_OK; | |
| + } | |
| + | |
| + item = vm->modules->start; | |
| + | |
| + for (i = 0; i < vm->modules->items; i++) { | |
| + module = *item; | |
| + | |
| + if (module->function.native) { | |
| + value = njs_vmcode_operand(vm, module->index); | |
| + value->data.u.object = &module->object; | |
| + value->type = NJS_OBJECT; | |
| + value->data.truth = 1; | |
| + | |
| + } else { | |
| + ret = njs_vm_invoke(vm, &module->function, NULL, 0, module->index); | |
| + if (ret == NXT_ERROR) { | |
| + return NXT_ERROR; | |
| + } | |
| + } | |
| + | |
| + item++; | |
| + } | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +nxt_int_t | |
| +njs_parser_module(njs_vm_t *vm, njs_parser_t *parser) | |
| +{ | |
| + u_char ch; | |
| + nxt_int_t ret; | |
| + nxt_str_t *name, body; | |
| + nxt_bool_t native, relative; | |
| + njs_lexer_t *prev, lexer; | |
| + njs_token_t token; | |
| + njs_module_t *module; | |
| + njs_parser_node_t *node; | |
| + njs_module_info_t info; | |
| + | |
| + name = &parser->lexer->text; | |
| + | |
| + parser->node = NULL; | |
| + | |
| + native = 1; | |
| + relative = 0; | |
| + | |
| + ch = name->start[0]; | |
| + | |
| + if (ch == '/' || ch == '.') { | |
| + native = 0; | |
| + relative = (ch != '/') ? 1 : 0; | |
| + } | |
| + | |
| + module = njs_module_find(vm, name); | |
| + if (module != NULL) { | |
| + goto found; | |
| + } | |
| + | |
| + if (native && module == NULL) { | |
| + goto not_found; | |
| + } | |
| + | |
| + if (vm->options.sandbox) { | |
| + goto not_found; | |
| + } | |
| + | |
| + /* non-native module. */ | |
| + | |
| + prev = parser->lexer; | |
| + | |
| + nxt_memzero(&info, sizeof(njs_module_info_t)); | |
| + | |
| + info.relative = relative; | |
| + info.name = name; | |
| + | |
| + ret = njs_module_lookup(vm, &parser->scope->cwd, &info); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + goto fail; | |
| + } | |
| + | |
| + if (nxt_strncmp(prev->file.start, info.file.start, prev->file.length) | |
| + == 0) | |
| + { | |
| + njs_parser_syntax_error(vm, parser, "Cannot import itself \"%V\"", name); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + nxt_memzero(&body, sizeof(nxt_str_t)); | |
| + | |
| + ret = njs_module_read(vm, info.fd, &body); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + goto fail; | |
| + } | |
| + | |
| + nxt_memzero(&lexer, sizeof(njs_lexer_t)); | |
| + | |
| + lexer.file = info.file; | |
| + lexer.start = body.start; | |
| + lexer.end = body.start + body.length; | |
| + lexer.line = 1; | |
| + lexer.keywords_hash = prev->keywords_hash; | |
| + | |
| + parser->lexer = &lexer; | |
| + | |
| + token = njs_parser_token(parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + goto fail; | |
| + } | |
| + | |
| + token = njs_parser_module_lambda(vm, parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + goto fail; | |
| + } | |
| + | |
| + module = njs_module_add(vm, name); | |
| + if (nxt_slow_path(module == NULL)) { | |
| + goto fail; | |
| + } | |
| + | |
| + module->function.u.lambda = parser->node->u.value.data.u.lambda; | |
| + | |
| + nxt_mp_free(vm->mem_pool, body.start); | |
| + | |
| + parser->lexer = prev; | |
| + | |
| +found: | |
| + | |
| + node = njs_parser_node_new(vm, parser, 0); | |
| + if (nxt_slow_path(node == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + node->left = parser->node; | |
| + | |
| + if (module->index == 0) { | |
| + ret = njs_module_insert(vm, module); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NXT_ERROR; | |
| + } | |
| + } | |
| + | |
| + node->index = (njs_index_t) module; | |
| + | |
| + parser->node = node; | |
| + | |
| + return NXT_OK; | |
| + | |
| +not_found: | |
| + | |
| + njs_parser_syntax_error(vm, parser, "Cannot find module \"%V\"", name); | |
| + | |
| + return NXT_ERROR; | |
| + | |
| +fail: | |
| + | |
| + if (body.start != NULL) { | |
| + nxt_mp_free(vm->mem_pool, body.start); | |
| + } | |
| + | |
| + njs_parser_syntax_error(vm, parser, "Cannot find module \"%V\"", name); | |
| + | |
| + parser->lexer = prev; | |
| + | |
| + return NXT_ERROR; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| +njs_module_lookup(njs_vm_t *vm, nxt_str_t *cwd, njs_module_info_t *info) | |
| +{ | |
| + nxt_int_t ret; | |
| + nxt_str_t *path; | |
| + nxt_uint_t i; | |
| + | |
| + if (!info->relative) { | |
| + return njs_module_absolute_path(vm, info); | |
| + } | |
| + | |
| + ret = njs_module_relative_path(vm, cwd, info); | |
| + if (ret == NXT_OK) { | |
| + return ret; | |
| + } | |
| + | |
| + if (vm->paths == NULL) { | |
| + return NXT_DECLINED; | |
| + } | |
| + | |
| + path = vm->paths->start; | |
| + | |
| + for (i = 0; i < vm->paths->items; i++) { | |
| + ret = njs_module_relative_path(vm, path, info); | |
| + if (ret == NXT_OK) { | |
| + return ret; | |
| + } | |
| + | |
| + path++; | |
| + } | |
| + | |
| + return NXT_DECLINED; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| +njs_module_absolute_path(njs_vm_t *vm, njs_module_info_t *info) | |
| +{ | |
| + nxt_str_t file; | |
| + | |
| + file.length = info->name->length + 1; | |
| + file.start = nxt_mp_alloc(vm->mem_pool, file.length); | |
| + if (nxt_slow_path(file.start == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + memcpy(file.start, info->name->start, info->name->length); | |
| + *(file.start + file.length) = '\0'; | |
| + | |
| + file.length--; | |
| + | |
| + info->fd = open((char *) file.start, O_RDONLY); | |
| + if (info->fd == -1) { | |
| + nxt_mp_free(vm->mem_pool, file.start); | |
| + return NXT_DECLINED; | |
| + } | |
| + | |
| + info->file = file; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| +njs_module_relative_path(njs_vm_t *vm, nxt_str_t *path, njs_module_info_t *info) | |
| +{ | |
| + u_char *p; | |
| + nxt_str_t file; | |
| + | |
| + file.length = path->length; | |
| + | |
| + if (path->start[path->length - 1] != '/') { | |
| + file.length++; | |
| + } | |
| + | |
| + file.length += info->name->length + 1; | |
| + | |
| + file.start = nxt_mp_alloc(vm->mem_pool, file.length); | |
| + if (nxt_slow_path(file.start == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + p = file.start; | |
| + | |
| + p = nxt_cpymem(p, path->start, path->length); | |
| + | |
| + if (path->start[path->length - 1] != '/') { | |
| + *p++ = '/'; | |
| + } | |
| + | |
| + p = nxt_cpymem(p, info->name->start, info->name->length); | |
| + *p = '\0'; | |
| + | |
| + file.length--; | |
| + | |
| + info->fd = open((char *) file.start, O_RDONLY); | |
| + if (info->fd == -1) { | |
| + nxt_mp_free(vm->mem_pool, file.start); | |
| + return NXT_DECLINED; | |
| + } | |
| + | |
| + info->file = file; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +#define NJS_MODULE_START "function() {" | |
| +#define NJS_MODULE_END "}" | |
| + | |
| +static nxt_int_t | |
| +njs_module_read(njs_vm_t *vm, int fd, nxt_str_t *body) | |
| +{ | |
| + u_char *p; | |
| + ssize_t n; | |
| + struct stat sb; | |
| + | |
| + if (fstat(fd, &sb) == -1) { | |
| + goto fail; | |
| + } | |
| + | |
| + body->length = nxt_length(NJS_MODULE_START); | |
| + | |
| + if (S_ISREG(sb.st_mode) && sb.st_size) { | |
| + body->length += sb.st_size; | |
| + } | |
| + | |
| + body->length += nxt_length(NJS_MODULE_END); | |
| + | |
| + body->start = nxt_mp_alloc(vm->mem_pool, body->length); | |
| + if (body->start == NULL) { | |
| + goto fail; | |
| + } | |
| + | |
| + p = nxt_cpymem(body->start, NJS_MODULE_START, nxt_length(NJS_MODULE_START)); | |
| + | |
| + n = read(fd, p, sb.st_size); | |
| + | |
| + if (n < 0) { | |
| + goto fail; | |
| + } | |
| + | |
| + if (n != sb.st_size) { | |
| + goto fail; | |
| + } | |
| + | |
| + p += n; | |
| + | |
| + memcpy(p, NJS_MODULE_END, nxt_length(NJS_MODULE_END)); | |
| + | |
| + close(fd); | |
| + | |
| + return NXT_OK; | |
| + | |
| +fail: | |
| + | |
| + if (body->start != NULL) { | |
| + nxt_mp_free(vm->mem_pool, body->start); | |
| + } | |
| + | |
| + close(fd); | |
| + | |
| + return NXT_ERROR; | |
| +} | |
| static nxt_int_t | |
| @@ -36,6 +405,98 @@ const nxt_lvlhsh_proto_t njs_modules_ha | |
| }; | |
| +static njs_module_t * | |
| +njs_module_find(njs_vm_t *vm, nxt_str_t *name) | |
| +{ | |
| + nxt_lvlhsh_query_t lhq; | |
| + | |
| + lhq.key = *name; | |
| + lhq.key_hash = nxt_djb_hash(name->start, name->length);; | |
| + lhq.proto = &njs_modules_hash_proto; | |
| + | |
| + if (nxt_lvlhsh_find(&vm->modules_hash, &lhq) == NXT_OK) { | |
| + return lhq.value; | |
| + } | |
| + | |
| + return NULL; | |
| +} | |
| + | |
| + | |
| +static njs_module_t * | |
| +njs_module_add(njs_vm_t *vm, nxt_str_t *name) | |
| +{ | |
| + nxt_int_t ret; | |
| + njs_module_t *module; | |
| + nxt_lvlhsh_query_t lhq; | |
| + | |
| + module = nxt_mp_zalloc(vm->mem_pool, sizeof(njs_module_t)); | |
| + if (nxt_slow_path(module == NULL)) { | |
| + njs_memory_error(vm); | |
| + return NULL; | |
| + } | |
| + | |
| + ret = njs_name_copy(vm, &module->name, name); | |
| + | |
| + if (nxt_fast_path(ret != NXT_OK)) { | |
| + nxt_mp_free(vm->mem_pool, module); | |
| + return NULL; | |
| + } | |
| + | |
| + lhq.replace = 0; | |
| + lhq.key = *name; | |
| + lhq.key_hash = nxt_djb_hash(name->start, name->length); | |
| + lhq.value = module; | |
| + lhq.pool = vm->mem_pool; | |
| + lhq.proto = &njs_modules_hash_proto; | |
| + | |
| + ret = nxt_lvlhsh_insert(&vm->modules_hash, &lhq); | |
| + | |
| + if (nxt_fast_path(ret == NXT_OK)) { | |
| + return module; | |
| + } | |
| + | |
| + nxt_mp_free(vm->mem_pool, module->name.start); | |
| + nxt_mp_free(vm->mem_pool, module); | |
| + | |
| + njs_internal_error(vm, "lvlhsh insert failed"); | |
| + | |
| + return NULL; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| +njs_module_insert(njs_vm_t *vm, njs_module_t *module) | |
| +{ | |
| + njs_module_t **value; | |
| + njs_parser_scope_t *scope; | |
| + | |
| + scope = njs_parser_global_scope(vm); | |
| + | |
| + module->index = njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL, | |
| + &njs_value_void); | |
| + if (nxt_slow_path(module->index == NJS_INDEX_ERROR)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + if (vm->modules == NULL) { | |
| + vm->modules = nxt_array_create(4, sizeof(njs_module_t *), | |
| + &njs_array_mem_proto, vm->mem_pool); | |
| + if (nxt_slow_path(vm->modules == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + } | |
| + | |
| + value = nxt_array_add(vm->modules, &njs_array_mem_proto, vm->mem_pool); | |
| + if (nxt_slow_path(value == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + *value = module; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| njs_ret_t njs_module_require(njs_vm_t *vm, njs_value_t *args, | |
| nxt_uint_t nargs, njs_index_t unused) | |
| { | |
| diff --git a/njs/njs_module.h b/njs/njs_module.h | |
| --- a/njs/njs_module.h | |
| +++ b/njs/njs_module.h | |
| @@ -11,9 +11,13 @@ | |
| typedef struct { | |
| nxt_str_t name; | |
| njs_object_t object; | |
| + njs_index_t index; | |
| + njs_function_t function; | |
| } njs_module_t; | |
| +nxt_int_t njs_module_load(njs_vm_t *vm); | |
| +nxt_int_t njs_parser_module(njs_vm_t *vm, njs_parser_t *parser); | |
| njs_ret_t njs_module_require(njs_vm_t *vm, njs_value_t *args, | |
| nxt_uint_t nargs, njs_index_t unused); | |
| diff --git a/njs/njs_parser.c b/njs/njs_parser.c | |
| --- a/njs/njs_parser.c | |
| +++ b/njs/njs_parser.c | |
| @@ -50,6 +50,13 @@ static njs_token_t njs_parser_try_statem | |
| static njs_token_t njs_parser_try_block(njs_vm_t *vm, njs_parser_t *parser); | |
| static njs_token_t njs_parser_throw_statement(njs_vm_t *vm, | |
| njs_parser_t *parser); | |
| +static njs_token_t njs_parser_import_statement(njs_vm_t *vm, | |
| + njs_parser_t *parser); | |
| +static njs_token_t njs_parser_export_statement(njs_vm_t *vm, | |
| + njs_parser_t *parser); | |
| +static nxt_int_t njs_parser_import_hoist(njs_vm_t *vm, njs_parser_t *parser, | |
| + njs_parser_node_t *new_node); | |
| +static nxt_int_t njs_parser_export_sink(njs_vm_t *vm, njs_parser_t *parser); | |
| static njs_token_t njs_parser_grouping_expression(njs_vm_t *vm, | |
| njs_parser_t *parser); | |
| static njs_parser_node_t *njs_parser_reference(njs_vm_t *vm, | |
| @@ -237,6 +244,7 @@ njs_parser_scope_begin(njs_vm_t *vm, njs | |
| if (lexer->file.length != 0) { | |
| nxt_file_basename(&lexer->file, &scope->file); | |
| + nxt_file_dirname(&lexer->file, &scope->cwd); | |
| } | |
| parent = parser->scope; | |
| @@ -335,6 +343,12 @@ njs_parser_statement(njs_vm_t *vm, njs_p | |
| case NJS_TOKEN_TRY: | |
| return njs_parser_try_statement(vm, parser); | |
| + case NJS_TOKEN_IMPORT: | |
| + return njs_parser_import_statement(vm, parser); | |
| + | |
| + case NJS_TOKEN_EXPORT: | |
| + return njs_parser_export_statement(vm, parser); | |
| + | |
| case NJS_TOKEN_SEMICOLON: | |
| return njs_parser_token(parser); | |
| @@ -718,6 +732,37 @@ njs_parser_function_expression(njs_vm_t | |
| static njs_token_t | |
| +njs_parser_lambda(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) | |
| +{ | |
| + token = njs_parser_token(parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + if (nxt_slow_path(token != NJS_TOKEN_OPEN_BRACE)) { | |
| + return NJS_TOKEN_ILLEGAL; | |
| + } | |
| + | |
| + token = njs_parser_token(parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + parser->node = NULL; | |
| + | |
| + while (token != NJS_TOKEN_CLOSE_BRACE) { | |
| + token = njs_parser_statement_chain(vm, parser, token, | |
| + &njs_parser_chain_top(parser)); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + } | |
| + | |
| + return njs_parser_token(parser); | |
| +} | |
| + | |
| + | |
| +static njs_token_t | |
| njs_parser_function_lambda(njs_vm_t *vm, njs_parser_t *parser, | |
| njs_function_lambda_t *lambda, njs_token_t token) | |
| { | |
| @@ -788,32 +833,9 @@ njs_parser_function_lambda(njs_vm_t *vm, | |
| lambda->nargs = njs_scope_offset(index) / sizeof(njs_value_t) - 1; | |
| - token = njs_parser_token(parser); | |
| - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| - return token; | |
| - } | |
| - | |
| - if (nxt_slow_path(token != NJS_TOKEN_OPEN_BRACE)) { | |
| - return NJS_TOKEN_ILLEGAL; | |
| - } | |
| - | |
| - token = njs_parser_token(parser); | |
| - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| - return token; | |
| - } | |
| - | |
| parent = parser->node; | |
| - parser->node = NULL; | |
| - | |
| - while (token != NJS_TOKEN_CLOSE_BRACE) { | |
| - token = njs_parser_statement_chain(vm, parser, token, | |
| - &njs_parser_chain_top(parser)); | |
| - if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| - return token; | |
| - } | |
| - } | |
| - | |
| - token = njs_parser_token(parser); | |
| + | |
| + token = njs_parser_lambda(vm, parser, token); | |
| if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| return token; | |
| } | |
| @@ -875,9 +897,13 @@ njs_parser_return_statement(njs_vm_t *vm | |
| njs_parser_scope_t *scope; | |
| for (scope = parser->scope; | |
| - scope->type != NJS_SCOPE_FUNCTION; | |
| + scope != NULL; | |
| scope = scope->parent) | |
| { | |
| + if (scope->type == NJS_SCOPE_FUNCTION && !scope->module) { | |
| + break; | |
| + } | |
| + | |
| if (scope->type == NJS_SCOPE_GLOBAL) { | |
| njs_parser_syntax_error(vm, parser, "Illegal return statement"); | |
| @@ -1798,6 +1824,300 @@ njs_parser_throw_statement(njs_vm_t *vm, | |
| static njs_token_t | |
| +njs_parser_import_statement(njs_vm_t *vm, njs_parser_t *parser) | |
| +{ | |
| + njs_ret_t ret; | |
| + njs_token_t token; | |
| + njs_variable_t *var; | |
| + njs_parser_node_t *name, *import; | |
| + | |
| + if (parser->scope->type != NJS_SCOPE_GLOBAL | |
| + && !parser->scope->module) | |
| + { | |
| + njs_parser_syntax_error(vm, parser, "Illegal import statement"); | |
| + | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + token = njs_parser_token(parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + if (token != NJS_TOKEN_NAME) { | |
| + return NJS_TOKEN_ILLEGAL; | |
| + } | |
| + | |
| + var = njs_parser_variable_add(vm, parser, NJS_VARIABLE_VAR); | |
| + if (nxt_slow_path(var == NULL)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + name = njs_parser_node_new(vm, parser, NJS_TOKEN_NAME); | |
| + if (nxt_slow_path(name == NULL)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + ret = njs_parser_variable_reference(vm, parser, name, NJS_DECLARATION); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + token = njs_parser_token(parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + token = njs_parser_match(vm, parser, token, NJS_TOKEN_FROM); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + if (token != NJS_TOKEN_STRING) { | |
| + return NJS_TOKEN_ILLEGAL; | |
| + } | |
| + | |
| + ret = njs_parser_module(vm, parser); | |
| + | |
| + token = njs_lexer_token(parser->lexer); | |
| + | |
| + switch (token) { | |
| + | |
| + case NJS_TOKEN_LINE_END: | |
| + token = njs_parser_token(parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + /* Fall through. */ | |
| + | |
| + case NJS_TOKEN_SEMICOLON: | |
| + case NJS_TOKEN_END: | |
| + break; | |
| + | |
| + default: | |
| + return NJS_TOKEN_ILLEGAL; | |
| + } | |
| + | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + import = njs_parser_node_new(vm, parser, NJS_TOKEN_IMPORT); | |
| + if (nxt_slow_path(import == NULL)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + import->left = name; | |
| + import->right = parser->node; | |
| + | |
| + ret = njs_parser_import_hoist(vm, parser, import); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + parser->node = NULL; | |
| + | |
| + return token; | |
| +} | |
| + | |
| + | |
| +njs_token_t | |
| +njs_parser_module_lambda(njs_vm_t *vm, njs_parser_t *parser) | |
| +{ | |
| + njs_ret_t ret; | |
| + njs_token_t token; | |
| + njs_parser_node_t *node, *parent; | |
| + njs_function_lambda_t *lambda; | |
| + | |
| + node = njs_parser_node_new(vm, parser, NJS_TOKEN_FUNCTION_EXPRESSION); | |
| + if (nxt_slow_path(node == NULL)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + node->token_line = parser->lexer->token_line; | |
| + | |
| + token = njs_parser_token(parser); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + lambda = nxt_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t)); | |
| + if (nxt_slow_path(lambda == NULL)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + node->u.value.data.u.lambda = lambda; | |
| + parser->node = node; | |
| + | |
| + ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_FUNCTION); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + parser->scope->module = 1; | |
| + | |
| + token = njs_parser_match(vm, parser, token, NJS_TOKEN_OPEN_PARENTHESIS); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + parent = parser->node; | |
| + | |
| + token = njs_parser_lambda(vm, parser, token); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + ret = njs_parser_export_sink(vm, parser); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + parent->right = njs_parser_chain_top(parser); | |
| + | |
| + parser->node = parent; | |
| + | |
| + njs_parser_scope_end(vm, parser); | |
| + | |
| + return token; | |
| +} | |
| + | |
| + | |
| +static njs_token_t | |
| +njs_parser_export_statement(njs_vm_t *vm, njs_parser_t *parser) | |
| +{ | |
| + njs_token_t token; | |
| + njs_parser_node_t *node; | |
| + | |
| + if (!parser->scope->module) { | |
| + njs_parser_syntax_error(vm, parser, "Illegal export statement"); | |
| + | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + node = njs_parser_node_new(vm, parser, NJS_TOKEN_EXPORT); | |
| + if (nxt_slow_path(node == NULL)) { | |
| + return NJS_TOKEN_ERROR; | |
| + } | |
| + | |
| + parser->node = node; | |
| + | |
| + token = njs_lexer_token(parser->lexer); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + token = njs_parser_match(vm, parser, token, NJS_TOKEN_DEFAULT); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + token = njs_parser_expression(vm, parser, token); | |
| + if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { | |
| + return token; | |
| + } | |
| + | |
| + if (parser->node->token != NJS_TOKEN_OBJECT) { | |
| + njs_parser_syntax_error(vm, parser, "Illegal export value"); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + node->right = parser->node; | |
| + parser->node = node; | |
| + | |
| + return token; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| +njs_parser_import_hoist(njs_vm_t *vm, njs_parser_t *parser, | |
| + njs_parser_node_t *new_node) | |
| +{ | |
| + njs_parser_node_t *node, *stmt, **child; | |
| + | |
| + child = &njs_parser_chain_top(parser); | |
| + | |
| + while (*child != NULL) { | |
| + node = *child; | |
| + | |
| + if (node->right != NULL | |
| + && node->right->token == NJS_TOKEN_IMPORT) | |
| + { | |
| + break; | |
| + } | |
| + | |
| + child = &node->left; | |
| + } | |
| + | |
| + stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT); | |
| + if (nxt_slow_path(stmt == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + stmt->left = *child; | |
| + stmt->right = new_node; | |
| + | |
| + *child = stmt; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| +njs_parser_export_sink(njs_vm_t *vm, njs_parser_t *parser) | |
| +{ | |
| + nxt_uint_t n; | |
| + njs_parser_node_t *node, *prev; | |
| + | |
| + for (n = 0, node = njs_parser_chain_top(parser); | |
| + node != NULL; | |
| + node = node->left) | |
| + { | |
| + if (node->right != NULL | |
| + && node->right->token == NJS_TOKEN_EXPORT) | |
| + { | |
| + n++; | |
| + } | |
| + } | |
| + | |
| + if (n != 1) { | |
| + njs_parser_syntax_error(vm, parser, | |
| + (n == 0) ? "export statement is required." | |
| + : "Identifier \"default\" has already been declared"); | |
| + | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + node = njs_parser_chain_top(parser); | |
| + | |
| + if (node->right && node->right->token == NJS_TOKEN_EXPORT) { | |
| + return NXT_OK; | |
| + } | |
| + | |
| + prev = njs_parser_chain_top(parser); | |
| + | |
| + while (prev->left != NULL) { | |
| + node = prev->left; | |
| + | |
| + if (node->right != NULL | |
| + && node->right->token == NJS_TOKEN_EXPORT) | |
| + { | |
| + prev->left = node->left; | |
| + break; | |
| + } | |
| + | |
| + prev = prev->left; | |
| + } | |
| + | |
| + node->left = njs_parser_chain_top(parser); | |
| + njs_parser_chain_top_set(parser, node); | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +static njs_token_t | |
| njs_parser_grouping_expression(njs_vm_t *vm, njs_parser_t *parser) | |
| { | |
| njs_token_t token; | |
| diff --git a/njs/njs_parser.h b/njs/njs_parser.h | |
| --- a/njs/njs_parser.h | |
| +++ b/njs/njs_parser.h | |
| @@ -203,6 +203,10 @@ typedef enum { | |
| NJS_TOKEN_SET_IMMEDIATE, | |
| NJS_TOKEN_CLEAR_TIMEOUT, | |
| + NJS_TOKEN_IMPORT, | |
| + NJS_TOKEN_FROM, | |
| + NJS_TOKEN_EXPORT, | |
| + | |
| NJS_TOKEN_RESERVED, | |
| } njs_token_t; | |
| @@ -253,11 +257,13 @@ struct njs_parser_scope_s { | |
| nxt_array_t *values[2]; /* Array of njs_value_t. */ | |
| njs_index_t next_index[2]; | |
| + nxt_str_t cwd; | |
| nxt_str_t file; | |
| njs_scope_t type:8; | |
| uint8_t nesting; /* 4 bits */ | |
| uint8_t argument_closures; | |
| + uint8_t module; | |
| }; | |
| @@ -323,6 +329,7 @@ njs_token_t njs_parser_var_expression(nj | |
| njs_token_t token); | |
| njs_token_t njs_parser_assignment_expression(njs_vm_t *vm, | |
| njs_parser_t *parser, njs_token_t token); | |
| +njs_token_t njs_parser_module_lambda(njs_vm_t *vm, njs_parser_t *parser); | |
| njs_token_t njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, | |
| njs_token_t token); | |
| njs_token_t njs_parser_property_token(njs_vm_t *vm, njs_parser_t *parser); | |
| diff --git a/njs/njs_shell.c b/njs/njs_shell.c | |
| --- a/njs/njs_shell.c | |
| +++ b/njs/njs_shell.c | |
| @@ -22,6 +22,8 @@ | |
| typedef struct { | |
| char *file; | |
| + size_t n_paths; | |
| + char **paths; | |
| nxt_int_t version; | |
| nxt_int_t disassemble; | |
| nxt_int_t interactive; | |
| @@ -68,6 +70,7 @@ static nxt_int_t njs_console_init(njs_vm | |
| static nxt_int_t njs_externals_init(njs_vm_t *vm, njs_console_t *console); | |
| static nxt_int_t njs_interactive_shell(njs_opts_t *opts, | |
| njs_vm_opt_t *vm_options); | |
| +static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options); | |
| static nxt_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options); | |
| static nxt_int_t njs_process_script(njs_console_t *console, njs_opts_t *opts, | |
| const nxt_str_t *script); | |
| @@ -246,6 +249,8 @@ main(int argc, char **argv) | |
| ret = njs_process_file(&opts, &vm_options); | |
| } | |
| + free(opts.paths); | |
| + | |
| return (ret == NXT_OK) ? EXIT_SUCCESS : EXIT_FAILURE; | |
| } | |
| @@ -263,6 +268,7 @@ njs_get_options(njs_opts_t *opts, int ar | |
| " -d print disassembled code.\n" | |
| " -q disable interactive introduction prompt.\n" | |
| " -s sandbox mode.\n" | |
| + " -p set a path prefix for modules.\n" | |
| " -v print njs version and exit.\n" | |
| " <filename> | - run code from a file or stdin.\n"; | |
| @@ -298,6 +304,23 @@ njs_get_options(njs_opts_t *opts, int ar | |
| opts->sandbox = 1; | |
| break; | |
| + case 'p': | |
| + if (argv[++i] != NULL) { | |
| + opts->n_paths++; | |
| + opts->paths = realloc(opts->paths, | |
| + opts->n_paths * sizeof(char *)); | |
| + if (opts->paths == NULL) { | |
| + fprintf(stderr, "failed to add path\n"); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + opts->paths[opts->n_paths - 1] = argv[i]; | |
| + break; | |
| + } | |
| + | |
| + fprintf(stderr, "option \"-p\" requires directory name\n"); | |
| + return NXT_ERROR; | |
| + | |
| case 'v': | |
| case 'V': | |
| opts->version = 1; | |
| @@ -383,14 +406,8 @@ njs_interactive_shell(njs_opts_t *opts, | |
| return NXT_ERROR; | |
| } | |
| - vm = njs_vm_create(vm_options); | |
| + vm = njs_create_vm(opts, vm_options); | |
| if (vm == NULL) { | |
| - fprintf(stderr, "failed to create vm\n"); | |
| - return NXT_ERROR; | |
| - } | |
| - | |
| - if (njs_externals_init(vm, vm_options->external) != NXT_OK) { | |
| - fprintf(stderr, "failed to add external protos\n"); | |
| return NXT_ERROR; | |
| } | |
| @@ -511,16 +528,8 @@ njs_process_file(njs_opts_t *opts, njs_v | |
| script.length += n; | |
| } | |
| - vm = njs_vm_create(vm_options); | |
| + vm = njs_create_vm(opts, vm_options); | |
| if (vm == NULL) { | |
| - fprintf(stderr, "failed to create vm\n"); | |
| - ret = NXT_ERROR; | |
| - goto done; | |
| - } | |
| - | |
| - ret = njs_externals_init(vm, vm_options->external); | |
| - if (ret != NXT_OK) { | |
| - fprintf(stderr, "failed to add external protos\n"); | |
| ret = NXT_ERROR; | |
| goto done; | |
| } | |
| @@ -549,6 +558,65 @@ close_fd: | |
| } | |
| +static njs_vm_t * | |
| +njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options) | |
| +{ | |
| + char *p, *pp; | |
| + njs_vm_t *vm; | |
| + nxt_int_t ret; | |
| + nxt_str_t path; | |
| + nxt_uint_t i; | |
| + | |
| + vm = njs_vm_create(vm_options); | |
| + if (vm == NULL) { | |
| + fprintf(stderr, "failed to create vm\n"); | |
| + return NULL; | |
| + } | |
| + | |
| + if (njs_externals_init(vm, vm_options->external) != NXT_OK) { | |
| + fprintf(stderr, "failed to add external protos\n"); | |
| + return NULL; | |
| + } | |
| + | |
| + for (i = 0; i < opts->n_paths; i++) { | |
| + path.start = (u_char *) opts->paths[i]; | |
| + path.length = strlen(opts->paths[i]); | |
| + | |
| + ret = njs_vm_add_path(vm, &path); | |
| + if (ret != NXT_OK) { | |
| + fprintf(stderr, "failed to add path\n"); | |
| + return NULL; | |
| + } | |
| + } | |
| + | |
| + pp = getenv("NJS_PATH"); | |
| + if (pp == NULL) { | |
| + return vm; | |
| + } | |
| + | |
| + while (1) { | |
| + p = strchr(pp, ':'); | |
| + | |
| + path.start = (u_char *) pp; | |
| + path.length = (p != NULL) ? (p - pp) : strlen(pp); | |
| + | |
| + ret = njs_vm_add_path(vm, &path); | |
| + if (ret != NXT_OK) { | |
| + fprintf(stderr, "failed to add path\n"); | |
| + return NULL; | |
| + } | |
| + | |
| + if (p == NULL) { | |
| + break; | |
| + } | |
| + | |
| + pp = p + 1; | |
| + } | |
| + | |
| + return vm; | |
| +} | |
| + | |
| + | |
| static void | |
| njs_output(njs_vm_t *vm, njs_opts_t *opts, njs_ret_t ret) | |
| { | |
| diff --git a/njs/njs_variable.c b/njs/njs_variable.c | |
| --- a/njs/njs_variable.c | |
| +++ b/njs/njs_variable.c | |
| @@ -487,7 +487,7 @@ njs_variable_reference_resolve(njs_vm_t | |
| return NXT_OK; | |
| } | |
| - if (scope->parent == NULL) { | |
| + if (scope->module || scope->parent == NULL) { | |
| /* A global scope. */ | |
| vr->scope = scope; | |
| diff --git a/njs/njs_vm.h b/njs/njs_vm.h | |
| --- a/njs/njs_vm.h | |
| +++ b/njs/njs_vm.h | |
| @@ -1023,6 +1023,8 @@ struct njs_vm_s { | |
| /* njs_vm_t must be aligned to njs_value_t due to scratch value. */ | |
| njs_value_t retval; | |
| + nxt_array_t *paths; | |
| + | |
| u_char *current; | |
| njs_value_t *scopes[NJS_SCOPES]; | |
| @@ -1039,6 +1041,8 @@ struct njs_vm_s { | |
| nxt_lvlhsh_t variables_hash; | |
| nxt_lvlhsh_t values_hash; | |
| + | |
| + nxt_array_t *modules; | |
| nxt_lvlhsh_t modules_hash; | |
| uint32_t event_id; | |
| diff --git a/njs/test/module/exception.js b/njs/test/module/exception.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/exception.js | |
| @@ -0,0 +1,4 @@ | |
| +import lib from './lib3.js'; | |
| + | |
| +lib.exception(); | |
| + | |
| diff --git a/njs/test/module/lib1.js b/njs/test/module/lib1.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/lib1.js | |
| @@ -0,0 +1,9 @@ | |
| +function hash() { | |
| + var h = crypto.createHash('md5'); | |
| + var v = h.update('AB').digest('hex'); | |
| + return v; | |
| +} | |
| + | |
| +import crypto from 'crypto'; | |
| + | |
| +export default {hash}; | |
| diff --git a/njs/test/module/lib2.js b/njs/test/module/lib2.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/lib2.js | |
| @@ -0,0 +1,7 @@ | |
| +import lib3 from './lib3.js'; | |
| + | |
| +function hash() { | |
| + return lib3.hash(); | |
| +} | |
| + | |
| +export default {hash}; | |
| diff --git a/njs/test/module/lib3.js b/njs/test/module/lib3.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/lib3.js | |
| @@ -0,0 +1,11 @@ | |
| +function hash() { | |
| + return sub.hash(); | |
| +} | |
| + | |
| +function exception() { | |
| + return sub.exception(); | |
| +} | |
| + | |
| +import sub from './sub/sub1.js'; | |
| + | |
| +export default {hash, exception}; | |
| diff --git a/njs/test/module/libs/hash.js b/njs/test/module/libs/hash.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/libs/hash.js | |
| @@ -0,0 +1,9 @@ | |
| +function hash() { | |
| + var h = crypto.createHash('md5'); | |
| + var v = h.update('AB').digest('hex'); | |
| + return v; | |
| +} | |
| + | |
| +import crypto from 'crypto'; | |
| + | |
| +export default {hash}; | |
| diff --git a/njs/test/module/normal.js b/njs/test/module/normal.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/normal.js | |
| @@ -0,0 +1,16 @@ | |
| +import lib1 from './lib1.js'; | |
| +import lib2 from './lib2.js'; | |
| + | |
| +import crypto from 'crypto'; | |
| +var h = crypto.createHash('md5'); | |
| +var hash = h.update('AB').digest('hex'); | |
| + | |
| +if (lib1.hash() != hash) { | |
| + console.log("failed!"); | |
| +} | |
| + | |
| +if (lib2.hash() != hash) { | |
| + console.log("failed!"); | |
| +} | |
| + | |
| +console.log("passed!"); | |
| diff --git a/njs/test/module/recursive.js b/njs/test/module/recursive.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/recursive.js | |
| @@ -0,0 +1,1 @@ | |
| +import lib from './recursive.js'; | |
| diff --git a/njs/test/module/sub/sub1.js b/njs/test/module/sub/sub1.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/sub/sub1.js | |
| @@ -0,0 +1,12 @@ | |
| +function hash() { | |
| + return sub2.hash(crypto); | |
| +} | |
| + | |
| +function exception() { | |
| + return {}.a.a; | |
| +} | |
| + | |
| +import sub2 from './sub2.js'; | |
| +import crypto from 'crypto'; | |
| + | |
| +export default {hash, exception}; | |
| diff --git a/njs/test/module/sub/sub2.js b/njs/test/module/sub/sub2.js | |
| new file mode 100644 | |
| --- /dev/null | |
| +++ b/njs/test/module/sub/sub2.js | |
| @@ -0,0 +1,7 @@ | |
| +function hash(crypto) { | |
| + return hashlib.hash(); | |
| +} | |
| + | |
| +import hashlib from './hash.js'; | |
| + | |
| +export default {hash}; | |
| diff --git a/njs/test/njs_expect_test.exp b/njs/test/njs_expect_test.exp | |
| --- a/njs/test/njs_expect_test.exp | |
| +++ b/njs/test/njs_expect_test.exp | |
| @@ -9,7 +9,7 @@ proc njs_test {body {opts ""}} { | |
| spawn -nottycopy njs | |
| } else { | |
| - spawn -nottycopy njs $opts | |
| + eval spawn -nottycopy njs $opts | |
| } | |
| expect -re "interactive njs \\d+\.\\d+\.\\d+\r\n\r" | |
| @@ -31,7 +31,7 @@ type console.help() for more information | |
| } | |
| proc njs_run {opts output} { | |
| - spawn -nottycopy njs $opts | |
| + eval spawn -nottycopy njs $opts | |
| expect -re $output | |
| expect eof | |
| } | |
| @@ -593,6 +593,23 @@ njs_test { | |
| "'ABCABC'\r\n>> "} | |
| } | |
| +# Modules | |
| + | |
| +njs_run "-p njs/test/module/libs ./njs/test/module/normal.js" \ | |
| + "passed!" | |
| + | |
| +njs_run "-p njs/test/module/libs/ ./njs/test/module/normal.js" \ | |
| + "passed!" | |
| + | |
| +njs_run "-p njs/test/module -p njs/test/module/libs ./njs/test/module/normal.js" \ | |
| + "passed!" | |
| + | |
| +njs_run "./njs/test/module/normal.js" \ | |
| + "SyntaxError: Cannot find module \"./hash.js\" in sub2.js:5" | |
| + | |
| +njs_run "-p njs/test/module/libs ./njs/test/module/exception.js" \ | |
| + "at exception \\(sub1.js:5\\)" | |
| + | |
| # CLI OPTIONS | |
| # help | |
| @@ -625,3 +642,10 @@ njs_test { | |
| {"var crypto = require('crypto')\r\n" | |
| "undefined\r\n"} | |
| } "-s" | |
| + | |
| +# path | |
| + | |
| +njs_test { | |
| + {"import lib from './lib1.js'\r\n" | |
| + "undefined\r\n"} | |
| +} "-p njs/test/module/" | |
| diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c | |
| --- a/njs/test/njs_unit_test.c | |
| +++ b/njs/test/njs_unit_test.c | |
| @@ -11401,6 +11401,20 @@ static njs_unit_test_t njs_test[] = | |
| { nxt_string("typeof(null) === \"object\""), | |
| nxt_string("true") }, | |
| + /* Module. */ | |
| + | |
| + { nxt_string("import;"), | |
| + nxt_string("SyntaxError: Unexpected token \";\" in 1") }, | |
| + | |
| + { nxt_string("import x from y;"), | |
| + nxt_string("SyntaxError: Unexpected token \"y\" in 1") }, | |
| + | |
| + { nxt_string("import x from 'crypto';"), | |
| + nxt_string("undefined") }, | |
| + | |
| + { nxt_string("export;"), | |
| + nxt_string("SyntaxError: Illegal export statement in 1") }, | |
| + | |
| }; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment