[njs] Added initial modules support.
Dmitry Volyntsev
xeioex at nginx.com
Fri Mar 22 17:49:42 UTC 2019
details: https://hg.nginx.org/njs/rev/53dc000e8c14
branches:
changeset: 837:53dc000e8c14
user: hongzhidao <hongzhidao at gmail.com>
date: Sat Mar 02 20:31:10 2019 +0800
description:
Added initial modules support.
The following syntax is supported:
1) default import statements:
import lib1 from '../relative/path/lib.js';
import lib2 from '/abs/path/lib.js';
import fs from 'fs'; // built-in modules
2) default export statements:
export default {fun1, fun2, ...}; // export module object
Modules look up procedure:
1) absolute paths (start with '/') are used as is.
2) otherwise the following paths are tried:
dir + '/' + path where dir is
a) the directory of the current file.
b) additions paths provided with njs_vm_add_path().
This closes #91 on Github.
diffstat:
njs/njs.c | 31 ++
njs/njs.h | 2 +
njs/njs_builtin.c | 2 +
njs/njs_core.h | 4 +-
njs/njs_generator.c | 98 ++++++-
njs/njs_lexer.h | 4 +
njs/njs_lexer_keyword.c | 7 +-
njs/njs_module.c | 455 ++++++++++++++++++++++++++++++++++
njs/njs_module.h | 4 +
njs/njs_parser.c | 308 ++++++++++++++++++++++-
njs/njs_parser.h | 7 +
njs/njs_shell.c | 102 ++++++-
njs/njs_variable.c | 2 +-
njs/njs_vm.c | 1 +
njs/njs_vm.h | 5 +
njs/test/module/exception.js | 4 +
njs/test/module/export.js | 4 +
njs/test/module/export_non_default.js | 3 +
njs/test/module/lib1.js | 19 +
njs/test/module/lib2.js | 7 +
njs/test/module/lib3.js | 11 +
njs/test/module/libs/hash.js | 9 +
njs/test/module/loading_exception.js | 3 +
njs/test/module/normal.js | 35 ++
njs/test/module/recursive.js | 3 +
njs/test/module/return.js | 1 +
njs/test/module/sub/sub1.js | 12 +
njs/test/module/sub/sub2.js | 7 +
njs/test/njs_expect_test.exp | 52 +++-
njs/test/njs_unit_test.c | 29 ++
30 files changed, 1191 insertions(+), 40 deletions(-)
diffs (truncated from 1695 to 1000 lines):
diff -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs.c
--- a/njs/njs.c Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs.c Sat Mar 02 20:31:10 2019 +0800
@@ -332,6 +332,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;
@@ -580,6 +582,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) {
@@ -628,6 +635,30 @@ njs_vm_handle_events(njs_vm_t *vm)
}
+nxt_int_t
+njs_vm_add_path(njs_vm_t *vm, const 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 -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs.h
--- a/njs/njs.h Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs.h Sat Mar 02 20:31:10 2019 +0800
@@ -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, const 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 -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_builtin.c
--- a/njs/njs_builtin.c Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_builtin.c Sat Mar 02 20:31:10 2019 +0800
@@ -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 -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_core.h
--- a/njs/njs_core.h Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_core.h Sat Mar 02 20:31:10 2019 +0800
@@ -7,6 +7,7 @@
#ifndef _NJS_CORE_H_INCLUDED_
#define _NJS_CORE_H_INCLUDED_
+
#include <nxt_auto_config.h>
#include <nxt_unix.h>
@@ -46,7 +47,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 -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_generator.c
--- a/njs/njs_generator.c Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_generator.c Sat Mar 02 20:31:10 2019 +0800
@@ -154,6 +154,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
@@ -171,8 +175,9 @@ static nxt_noinline nxt_int_t njs_genera
static nxt_noinline nxt_int_t njs_generate_index_release(njs_vm_t *vm,
njs_generator_t *generator, njs_index_t index);
-static nxt_int_t njs_generate_function_debug(njs_vm_t *vm, nxt_str_t *name,
- njs_function_lambda_t *lambda, njs_parser_node_t *node);
+static nxt_int_t njs_generate_function_debug(njs_vm_t *vm,
+ const nxt_str_t *name, njs_function_lambda_t *lambda,
+ njs_parser_node_t *node);
#define njs_generate_code(generator, type, _code, _operation, nargs, _retval) \
@@ -467,6 +472,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");
@@ -1909,19 +1920,25 @@ njs_generate_function(njs_vm_t *vm, njs_
njs_parser_node_t *node)
{
nxt_int_t ret;
+ nxt_bool_t module;
+ const nxt_str_t *name;
njs_function_lambda_t *lambda;
njs_vmcode_function_t *function;
lambda = node->u.value.data.u.lambda;
-
- ret = njs_generate_function_scope(vm, lambda, node, &njs_entry_anonymous);
+ module = node->right->scope->module;
+
+ name = module ? &njs_entry_module : &njs_entry_anonymous;
+
+ ret = njs_generate_function_scope(vm, lambda, node, name);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
if (vm->debug != NULL) {
- ret = njs_generate_function_debug(vm, NULL, lambda, node);
+ ret = njs_generate_function_debug(vm, name, lambda,
+ module ? node->right : node);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
@@ -2450,7 +2467,6 @@ njs_generate_return_statement(njs_vm_t *
njs_vmcode_try_return_t *try_return;
ret = njs_generator(vm, generator, node->right);
-
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
@@ -3012,6 +3028,66 @@ 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)
@@ -3162,7 +3238,7 @@ njs_generate_index_release(njs_vm_t *vm,
static nxt_int_t
-njs_generate_function_debug(njs_vm_t *vm, nxt_str_t *name,
+njs_generate_function_debug(njs_vm_t *vm, const nxt_str_t *name,
njs_function_lambda_t *lambda, njs_parser_node_t *node)
{
njs_function_debug_t *debug;
@@ -3172,16 +3248,10 @@ njs_generate_function_debug(njs_vm_t *vm
return NXT_ERROR;
}
- if (name != NULL) {
- debug->name = *name;
-
- } else {
- debug->name = no_label;
- }
-
debug->lambda = lambda;
debug->line = node->token_line;
debug->file = node->scope->file;
+ debug->name = (name != NULL) ? *name : no_label;
return NXT_OK;
}
diff -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_lexer.h
--- a/njs/njs_lexer.h Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_lexer.h Sat Mar 02 20:31:10 2019 +0800
@@ -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;
diff -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_lexer_keyword.c
--- a/njs/njs_lexer_keyword.c Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_lexer_keyword.c Sat Mar 02 20:31:10 2019 +0800
@@ -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 -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_module.c
--- a/njs/njs_module.c Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_module.c Sat Mar 02 20:31:10 2019 +0800
@@ -8,6 +8,369 @@
#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 <sys/param.h>
+
+
+typedef struct {
+ int fd;
+ nxt_str_t name;
+ nxt_str_t file;
+} njs_module_info_t;
+
+
+static nxt_int_t njs_module_lookup(njs_vm_t *vm, const nxt_str_t *cwd,
+ njs_module_info_t *info);
+static nxt_noinline nxt_int_t njs_module_relative_path(njs_vm_t *vm,
+ const nxt_str_t *dir, njs_module_info_t *info);
+static nxt_int_t njs_module_absolute_path(njs_vm_t *vm,
+ njs_module_info_t *info);
+static nxt_bool_t njs_module_realpath_equal(const nxt_str_t *path1,
+ const nxt_str_t *path2);
+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) {
+ goto done;
+ }
+ }
+
+ item++;
+ }
+
+ ret = NXT_OK;
+
+done:
+
+ if (vm->options.accumulative) {
+ nxt_array_reset(vm->modules);
+ }
+
+ return ret;
+}
+
+
+nxt_int_t
+njs_parser_module(njs_vm_t *vm, njs_parser_t *parser)
+{
+ nxt_int_t ret;
+ nxt_str_t name, text;
+ njs_lexer_t *prev, lexer;
+ njs_token_t token;
+ njs_module_t *module;
+ njs_parser_node_t *node;
+ njs_module_info_t info;
+
+ name = *njs_parser_text(parser);
+
+ parser->node = NULL;
+
+ module = njs_module_find(vm, &name);
+ if (module != NULL) {
+ goto found;
+ }
+
+ prev = parser->lexer;
+
+ nxt_memzero(&text, sizeof(nxt_str_t));
+
+ if (vm->options.sandbox || name.length == 0) {
+ njs_parser_syntax_error(vm, parser, "Cannot find module \"%V\"", &name);
+ goto fail;
+ }
+
+ /* Non-native module. */
+
+ nxt_memzero(&info, sizeof(njs_module_info_t));
+
+ info.name = name;
+
+ ret = njs_module_lookup(vm, &parser->scope->cwd, &info);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_parser_syntax_error(vm, parser, "Cannot find module \"%V\"", &name);
+ goto fail;
+ }
+
+ ret = njs_module_read(vm, info.fd, &text);
+
+ close(info.fd);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ njs_internal_error(vm, "while reading \"%V\" module", &name);
+ goto fail;
+ }
+
+ if (njs_module_realpath_equal(&prev->file, &info.file)) {
+ njs_parser_syntax_error(vm, parser, "Cannot import itself \"%V\"",
+ &name);
+ goto fail;
+ }
+
+ ret = njs_lexer_init(vm, &lexer, &info.file, text.start,
+ text.start + text.length);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_ERROR;
+ }
+
+ parser->lexer = &lexer;
+
+ token = njs_parser_token(vm, 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, text.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;
+
+fail:
+
+ parser->lexer = prev;
+
+ if (text.start != NULL) {
+ nxt_mp_free(vm->mem_pool, text.start);
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+njs_module_lookup(njs_vm_t *vm, const nxt_str_t *cwd, njs_module_info_t *info)
+{
+ nxt_int_t ret;
+ nxt_str_t *path;
+ nxt_uint_t i;
+
+ if (info->name.start[0] == '/') {
+ 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;
+ file.start = nxt_mp_alloc(vm->mem_pool, file.length + 1);
+ if (nxt_slow_path(file.start == NULL)) {
+ return NXT_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) {
+ nxt_mp_free(vm->mem_pool, file.start);
+ return NXT_DECLINED;
+ }
+
+ info->file = file;
+
+ return NXT_OK;
+}
+
+
+static nxt_noinline nxt_int_t
+njs_module_relative_path(njs_vm_t *vm, const nxt_str_t *dir,
+ njs_module_info_t *info)
+{
+ u_char *p;
+ nxt_str_t file;
+ nxt_bool_t trail;
+
+ file.length = dir->length;
+
+ trail = (dir->start[dir->length - 1] != '/');
+
+ if (trail) {
+ file.length++;
+ }
+
+ file.length += info->name.length;
+
+ file.start = nxt_mp_alloc(vm->mem_pool, file.length + 1);
+ if (nxt_slow_path(file.start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = nxt_cpymem(file.start, dir->start, dir->length);
+
+ if (trail) {
+ *p++ = '/';
+ }
+
+ p = nxt_cpymem(p, info->name.start, info->name.length);
+ *p = '\0';
+
+ info->fd = open((char *) file.start, O_RDONLY);
+ if (info->fd < 0) {
+ 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 *text)
+{
+ u_char *p;
+ ssize_t n;
+ struct stat sb;
+
+ if (fstat(fd, &sb) == -1) {
+ goto fail;
+ }
+
+ text->length = nxt_length(NJS_MODULE_START);
+
+ if (S_ISREG(sb.st_mode) && sb.st_size) {
+ text->length += sb.st_size;
+ }
+
+ text->length += nxt_length(NJS_MODULE_END);
+
+ text->start = nxt_mp_alloc(vm->mem_pool, text->length);
+ if (text->start == NULL) {
+ goto fail;
+ }
+
+ p = nxt_cpymem(text->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));
+
+ return NXT_OK;
+
+fail:
+
+ if (text->start != NULL) {
+ nxt_mp_free(vm->mem_pool, text->start);
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_bool_t
+njs_module_realpath_equal(const nxt_str_t *path1, const nxt_str_t *path2)
+{
+ char rpath1[MAXPATHLEN], rpath2[MAXPATHLEN];
+
+ realpath((char *) path1->start, rpath1);
+ realpath((char *) path2->start, rpath2);
+
+ return (strcmp(rpath1, rpath2) == 0);
+}
static nxt_int_t
@@ -36,6 +399,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_slow_path(ret != NXT_OK)) {
+ nxt_mp_free(vm->mem_pool, module);
+ njs_memory_error(vm);
+ 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_undefined);
+ 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 -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_module.h
--- a/njs/njs_module.h Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_module.h Sat Mar 02 20:31:10 2019 +0800
@@ -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 -r 64dc0795c2f0 -r 53dc000e8c14 njs/njs_parser.c
--- a/njs/njs_parser.c Wed Mar 20 15:56:22 2019 +0300
+++ b/njs/njs_parser.c Sat Mar 02 20:31:10 2019 +0800
@@ -59,6 +59,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,
@@ -87,9 +94,6 @@ static njs_token_t njs_parser_unexpected
#define njs_parser_chain_top_set(parser, node) \
(parser)->scope->top = node
-#define njs_parser_text(parser) \
- &(parser)->lexer->lexer_token->text
-
#define njs_parser_key_hash(parser) \
(parser)->lexer->lexer_token->key_hash
@@ -259,6 +263,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;
@@ -394,6 +399,14 @@ njs_parser_statement(njs_vm_t *vm, njs_p
token = njs_parser_brk_statement(vm, parser, token);
break;
+ case NJS_TOKEN_IMPORT:
+ token = njs_parser_import_statement(vm, parser);
+ break;
+
+ case NJS_TOKEN_EXPORT:
+ token = njs_parser_export_statement(vm, parser);
+ break;
+
case NJS_TOKEN_NAME:
offset = 0;
if (njs_parser_peek_token(vm, parser, &offset) == NJS_TOKEN_COLON) {
@@ -955,9 +968,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");
@@ -1878,6 +1895,289 @@ 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(vm, parser);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ if (token != NJS_TOKEN_NAME) {
+ njs_parser_syntax_error(vm, parser,
+ "Non-default import is not supported");
+ 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(vm, 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);
+ 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 njs_parser_token(vm, parser);
+}
+
+
+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 = njs_parser_token_line(parser);
+
+ token = njs_parser_token(vm, 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_match(vm, parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ token = njs_parser_lambda_statements(vm, parser, token);
+ if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+ return token;
+ }
+
+ ret = njs_parser_export_sink(vm, parser);
More information about the nginx-devel
mailing list