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;
{
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) {
}
+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)
{
*/
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,
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)) {
#ifndef _NJS_CORE_H_INCLUDED_
#define _NJS_CORE_H_INCLUDED_
+
#include <nxt_auto_config.h>
#include <nxt_unix.h>
#include <njs_error.h>
#include <njs_event.h>
-
#include <njs_extern.h>
+#include <njs_module.h>
+
#endif /* _NJS_CORE_H_INCLUDED_ */
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
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) \
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");
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;
+ module = node->right->scope->module;
+
+ name = module ? &njs_entry_module : &njs_entry_anonymous;
- ret = njs_generate_function_scope(vm, lambda, node, &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;
}
njs_vmcode_try_return_t *try_return;
ret = njs_generator(vm, generator, node->right);
-
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
}
+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)
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;
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;
}
NJS_TOKEN_SET_IMMEDIATE,
NJS_TOKEN_CLEAR_TIMEOUT,
+ NJS_TOKEN_IMPORT,
+ NJS_TOKEN_FROM,
+ NJS_TOKEN_EXPORT,
+
NJS_TOKEN_RESERVED,
} njs_token_t;
{ 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 },
{ 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 },
#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
};
+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)
{
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);
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,
#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
if (lexer->file.length != 0) {
nxt_file_basename(&lexer->file, &scope->file);
+ nxt_file_dirname(&lexer->file, &scope->cwd);
}
parent = parser->scope;
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) {
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");
}
+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);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ parent->right = njs_parser_chain_top(parser);
+ parent->right->token_line = 1;
+
+ 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_parser_token(vm, parser);
+ if (nxt_slow_path(token != NJS_TOKEN_DEFAULT)) {
+ njs_parser_syntax_error(vm, parser,
+ "Non-default export is not supported");
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ token = njs_parser_token(vm, parser);
+ 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;
+
+ n = 0;
+
+ for (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)
{
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 arguments_object;
+ uint8_t module;
};
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);
((vm)->options.accumulative && (scope)->type == NJS_SCOPE_GLOBAL)
+#define njs_parser_text(parser) \
+ &(parser)->lexer->lexer_token->text
+
+
#define njs_parser_syntax_error(vm, parser, fmt, ...) \
njs_parser_lexer_error(vm, parser, NJS_OBJECT_SYNTAX_ERROR, fmt, \
##__VA_ARGS__)
typedef struct {
char *file;
+ size_t n_paths;
+ char **paths;
nxt_int_t version;
nxt_int_t disassemble;
nxt_int_t interactive;
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);
ret = njs_process_file(&opts, &vm_options);
}
+ if (opts.paths != NULL) {
+ free(opts.paths);
+ }
+
return (ret == NXT_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}
" -d print disassembled code.\n"
" -q disable interactive introduction prompt.\n"
" -s sandbox mode.\n"
+ " -p set path prefix for modules.\n"
" -v print njs version and exit.\n"
" <filename> | - run code from a file or stdin.\n";
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;
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;
}
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;
}
}
+static njs_vm_t *
+njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options)
+{
+ char *p, *start;
+ 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;
+ }
+ }
+
+ start = getenv("NJS_PATH");
+ if (start == NULL) {
+ return vm;
+ }
+
+ for ( ;; ) {
+ p = strchr(start, ':');
+
+ path.start = (u_char *) start;
+ path.length = (p != NULL) ? (size_t) (p - start) : strlen(start);
+
+ ret = njs_vm_add_path(vm, &path);
+ if (ret != NXT_OK) {
+ fprintf(stderr, "failed to add path\n");
+ return NULL;
+ }
+
+ if (p == NULL) {
+ break;
+ }
+
+ start = p + 1;
+ }
+
+ return vm;
+}
+
+
static void
njs_output(njs_vm_t *vm, njs_opts_t *opts, njs_ret_t ret)
{
return NXT_OK;
}
- if (scope->parent == NULL) {
+ if (scope->module || scope->parent == NULL) {
/* A global scope. */
vr->scope = scope;
const nxt_str_t njs_entry_main = nxt_string("main");
+const nxt_str_t njs_entry_module = nxt_string("module");
const nxt_str_t njs_entry_native = nxt_string("native");
const nxt_str_t njs_entry_unknown = nxt_string("unknown");
const nxt_str_t njs_entry_anonymous = nxt_string("anonymous");
/* 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];
nxt_lvlhsh_t variables_hash;
nxt_lvlhsh_t values_hash;
+
+ nxt_array_t *modules;
nxt_lvlhsh_t modules_hash;
uint32_t event_id;
extern const njs_value_t njs_string_memory_error;
extern const nxt_str_t njs_entry_main;
+extern const nxt_str_t njs_entry_module;
extern const nxt_str_t njs_entry_native;
extern const nxt_str_t njs_entry_unknown;
extern const nxt_str_t njs_entry_anonymous;
--- /dev/null
+import lib from 'lib3.js';
+
+lib.exception();
+
--- /dev/null
+var a = 1;
+
+export default {a}
+export default {a}
--- /dev/null
+var a = 1;
+
+export a {a}
--- /dev/null
+function hash() {
+ var h = crypto.createHash('md5');
+ var v = h.update('AB').digest('hex');
+ return v;
+}
+
+import crypto from 'crypto';
+
+var state = {count:0}
+
+function inc() {
+ state.count++;
+}
+
+function get() {
+ return state.count;
+}
+
+export default {hash, inc, get};
--- /dev/null
+import lib3 from 'lib3.js';
+
+function hash() {
+ return lib3.hash();
+}
+
+export default {hash};
--- /dev/null
+function hash() {
+ return sub.hash();
+}
+
+function exception() {
+ return sub.error();
+}
+
+import sub from './sub/sub1.js';
+
+export default {hash, exception};
--- /dev/null
+function hash() {
+ var h = crypto.createHash('md5');
+ var v = h.update('AB').digest('hex');
+ return v;
+}
+
+import crypto from 'crypto';
+
+export default {hash};
--- /dev/null
+throw Error('loading exception');
+
+export default {};
--- /dev/null
+import lib1 from 'lib1.js';
+import lib2 from 'lib2.js';
+import lib1_2 from 'lib1.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!");
+}
+
+if (lib1.get() != 0) {
+ console.log("failed!");
+}
+
+if (lib1_2.get() != 0) {
+ console.log("failed!");
+}
+
+lib1.inc();
+
+if (lib1.get() != 1) {
+ console.log("failed!");
+}
+
+if (lib1_2.get() != 1) {
+ console.log("failed!");
+}
+
+console.log("passed!");
--- /dev/null
+
+
+import lib from './recursive.js';
--- /dev/null
+function hash() {
+ return sub2.hash(crypto);
+}
+
+function error() {
+ return {}.a.a;
+}
+
+import sub2 from 'sub2.js';
+import crypto from 'crypto';
+
+export default {hash, error};
--- /dev/null
+function hash(crypto) {
+ return hashlib.hash();
+}
+
+import hashlib from 'hash.js';
+
+export default {hash};
spawn -nottycopy njs
} else {
- spawn -nottycopy njs $opts
+ eval spawn -nottycopy njs $opts
}
expect -re "interactive njs \\d+\.\\d+\.\\d+\r\n\r"
}
proc njs_run {opts output} {
- spawn -nottycopy njs $opts
+ eval spawn -nottycopy njs $opts
expect -re $output
expect eof
}
"'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 error \\(sub1.js:5\\)\r\n at exception \\(lib3.js:5\\)"
+
+njs_run "-p njs/test/module ./njs/test/module/recursive.js" \
+ "SyntaxError: Cannot import itself \"./recursive.js\" in recursive.js:3"
+
# CLI OPTIONS
# help
{"var crypto = require('crypto')\r\n"
"undefined\r\n"}
} "-s"
+
+# modules
+
+njs_test {
+ {"import lib1 from 'lib1.js'\r\n"
+ "undefined\r\n"}
+ {"import lib2 from 'lib1.js'\r\n"
+ "undefined\r\n"}
+ {"lib2.inc()\r\n"
+ "undefined\r\n"}
+ {"lib1.get()\r\n"
+ "1\r\n"}
+ {"import m from 'return.js'\r\n"
+ "Illegal return statement in return.js:1\r\n"}
+ {"import m from 'empty.js'\r\n"
+ "export statement is required in empty.js:1\r\n"}
+ {"import m from 'export.js'\r\n"
+ "Identifier \"default\" has already been declared in export.js:5\r\n"}
+ {"import m from 'export_non_default.js'\r\n"
+ "Non-default export is not supported in export_non_default.js:3\r\n"}
+ {"import m from 'loading_exception.js'\r\n"
+ "Error: loading exception\r\n at module \\(loading_exception.js:1\\)"}
+ {"import lib3 from 'lib1.js'\r\n"
+ "undefined\r\n"}
+} "-p njs/test/module/"
+
+njs_run "-p njs/test/module/libs/ -d ./njs/test/module/normal.js" \
+ "passed!"
{ nxt_string("typeof(null) === \"object\""),
nxt_string("true") },
+ /* Module. */
+
+ { nxt_string("import;"),
+ nxt_string("SyntaxError: Non-default import is not supported in 1") },
+
+ { nxt_string("import {x} from y"),
+ nxt_string("SyntaxError: Non-default import is not supported in 1") },
+
+ { nxt_string("import x from y"),
+ nxt_string("SyntaxError: Unexpected token \"y\" in 1") },
+
+ { nxt_string("import x from {"),
+ nxt_string("SyntaxError: Unexpected token \"{\" in 1") },
+
+ { nxt_string("import x from ''"),
+ nxt_string("SyntaxError: Cannot find module \"\" in 1") },
+
+ { nxt_string("import x from 'crypto'"),
+ nxt_string("undefined") },
+
+ { nxt_string("import x from 'crypto' 1"),
+ nxt_string("SyntaxError: Unexpected token \"1\" in 1") },
+
+ { nxt_string("if (1) {import x from 'crypto'}"),
+ nxt_string("SyntaxError: Illegal import statement in 1") },
+
+ { nxt_string("export"),
+ nxt_string("SyntaxError: Illegal export statement in 1") },
+
};