Skip to content

Instantly share code, notes, and snippets.

@xeioex
Last active March 1, 2019 18:20
Show Gist options
  • Save xeioex/2cf233c9d2e532c3cd794362b8d21553 to your computer and use it in GitHub Desktop.
Save xeioex/2cf233c9d2e532c3cd794362b8d21553 to your computer and use it in GitHub Desktop.
# 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