src/njs_encoding.c \
src/njs_buffer.c \
src/njs_iterator.c \
+ src/njs_scope.c \
"
NJS_LIB_TEST_SRCS=" \
handler = c->log->handler;
c->log->handler = NULL;
- ngx_log_error(level, c->log, 0, "js: %*s", msg.length, msg.start);
+ ngx_log_error((ngx_uint_t) level, c->log, 0, "js: %*s",
+ msg.length, msg.start);
c->log->handler = handler;
#define NJS_VM_OPT_UNHANDLED_REJECTION_THROW 1
/*
- * accumulative - enables "accumulative" mode to support incremental compiling.
+ * interactive - enables "interactive" mode.
* (REPL). Allows starting parent VM without cloning.
* disassemble - enables disassemble.
* backtrace - enables backtraces.
* - throwing inside a Promise without a catch block.
* - throwing inside in a finally or catch block.
*/
-
+ uint8_t interactive; /* 1 bit */
uint8_t trailer; /* 1 bit */
uint8_t init; /* 1 bit */
- uint8_t accumulative; /* 1 bit */
uint8_t disassemble; /* 1 bit */
uint8_t backtrace; /* 1 bit */
uint8_t quiet; /* 1 bit */
NJS_EXPORT njs_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *args, njs_uint_t nargs);
NJS_EXPORT njs_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function,
- const njs_value_t *args, njs_uint_t nargs, njs_index_t retval);
+ const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval);
/*
* Runs posted events.
vm->global_object = shared->objects[0];
vm->global_object.shared = 0;
+ njs_set_object(&vm->global_value, &vm->global_object);
+
string_object = &shared->string_object;
njs_lvlhsh_init(&string_object->hash);
string_object->shared_hash = shared->string_instance_hash;
}
var = ((njs_variable_node_t *) node)->variable;
- value = njs_vmcode_operand(vm, var->index);
+ value = njs_scope_valid_value(vm, var->index);
if (!njs_is_object(value)) {
return NULL;
{
njs_int_t ret;
njs_value_t *value;
+ njs_variable_t *var;
+ njs_function_t *function;
njs_rbtree_node_t *rb_node;
njs_lvlhsh_query_t lhq;
njs_variable_node_t *node, var_node;
}
node = (njs_variable_node_t *) rb_node;
- value = njs_vmcode_operand(vm, node->variable->index);
+
+ var = node->variable;
+
+ value = njs_scope_valid_value(vm, var->index);
+
+ if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) {
+ *value = var->value;
+
+ function = njs_function_value_copy(vm, value);
+ if (njs_slow_path(function == NULL)) {
+ return NJS_ERROR;
+ }
+ }
if (setval != NULL) {
*value = *setval;
{ NJS_VMCODE_OBJECT_COPY, sizeof(njs_vmcode_object_copy_t),
njs_str("OBJECT COPY ") },
+ { NJS_VMCODE_FUNCTION_COPY, sizeof(njs_vmcode_function_copy_t),
+ njs_str("FUNCTION COPY ") },
+
{ NJS_VMCODE_PROPERTY_GET, sizeof(njs_vmcode_prop_get_t),
njs_str("PROP GET ") },
{ NJS_VMCODE_GLOBAL_GET, sizeof(njs_vmcode_prop_get_t),
njs_vmcode_catch_t *catch;
njs_vmcode_finally_t *finally;
njs_vmcode_try_end_t *try_end;
+ njs_vmcode_move_arg_t *move_arg;
njs_vmcode_try_start_t *try_start;
njs_vmcode_operation_t operation;
njs_vmcode_cond_jump_t *cond_jump;
continue;
}
+ if (operation == NJS_VMCODE_MOVE_ARG) {
+ move_arg = (njs_vmcode_move_arg_t *) p;
+
+ njs_printf("%5uD | %05uz MOVE ARGUMENT %uD %04Xz\n",
+ line, p - start, move_arg->dst, (size_t) move_arg->src);
+
+ p += sizeof(njs_vmcode_move_arg_t);
+
+ continue;
+ }
+
code_name = code_names;
n = njs_nitems(code_names);
#include <njs_main.h>
-static njs_function_t *njs_function_copy(njs_vm_t *vm,
- njs_function_t *function);
-static njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
-
-
njs_function_t *
-njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
- njs_closure_t *closures[], njs_bool_t shared)
+njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda)
{
size_t size;
- njs_uint_t n, nesting;
njs_function_t *function;
- nesting = lambda->nesting;
- size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+ size = sizeof(njs_function_t) + lambda->nclosures * sizeof(njs_value_t *);
function = njs_mp_zalloc(vm->mem_pool, size);
if (njs_slow_path(function == NULL)) {
function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
function->object.type = NJS_FUNCTION;
- function->object.shared = shared;
function->object.extensible = 1;
- if (nesting != 0 && closures != NULL) {
- function->closure = 1;
-
- n = 0;
-
- do {
- /* GC: retain closure. */
- njs_function_closures(function)[n] = closures[n];
- n++;
- } while (n < nesting);
- }
-
return function;
fail:
}
-njs_inline njs_closure_t **
-njs_function_active_closures(njs_vm_t *vm, njs_function_t *function)
-{
- return (function->closure) ? njs_function_closures(function)
- : njs_frame_closures(vm->active_frame);
-}
-
-
njs_int_t
njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
njs_value_t *name, const char *prefix)
}
-static njs_function_t *
+njs_function_t *
njs_function_copy(njs_vm_t *vm, njs_function_t *function)
{
- size_t size;
- njs_uint_t n, nesting;
- njs_closure_t **closures;
+ size_t size, n;
+ njs_value_t **from, **to;
njs_function_t *copy;
- nesting = (function->native) ? 0 : function->u.lambda->nesting;
+ n = (function->native) ? 0 : function->u.lambda->nclosures;
- size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+ size = sizeof(njs_function_t) + n * sizeof(njs_value_t *);
copy = njs_mp_alloc(vm->mem_pool, size);
if (njs_slow_path(copy == NULL)) {
copy->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
copy->object.shared = 0;
- if (nesting == 0) {
+ if (n == 0) {
return copy;
}
- copy->closure = 1;
+ from = njs_function_closures(function);
+ to = njs_function_closures(copy);
- closures = njs_function_active_closures(vm, function);
+ do {
+ n--;
- n = 0;
+ to[n] = from[n];
- do {
- /* GC: retain closure. */
- njs_function_closures(copy)[n] = closures[n];
- n++;
- } while (n < nesting);
+ } while (n != 0);
return copy;
}
for (n = 0; n < nargs; n++) {
njs_uint32_to_string(&value, n);
- prop = njs_object_prop_alloc(vm, &value, &frame->arguments[n + 1], 1);
+ prop = njs_object_prop_alloc(vm, &value, &frame->arguments[n], 1);
if (njs_slow_path(prop == NULL)) {
return NJS_ERROR;
}
return NJS_ERROR;
}
- if (n <= nargs) {
- i = 0;
- do {
- /* GC: retain. */
- array->start[i++] = frame->arguments[n++];
- } while (n <= nargs);
+ for (i = 0; i < length; i++) {
+ array->start[i] = frame->arguments[i + n - 1];
}
- rest_arguments = &frame->arguments[frame->function->u.lambda->nargs];
+ rest_arguments = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+ if (njs_slow_path(rest_arguments == NULL)) {
+ return NJS_ERROR;
+ }
/* GC: retain. */
njs_set_array(rest_arguments, array);
+ vm->top_frame->local[n] = rest_arguments;
+
return NJS_OK;
}
frame->pc = NULL;
value = (njs_value_t *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE);
+
frame->arguments = value;
+ frame->arguments_offset = value + function->args_offset;
bound = function->bound;
} while (n != 0);
}
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
-
if (args != NULL) {
memcpy(value, args, nargs * sizeof(njs_value_t));
}
const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs,
njs_bool_t ctor)
{
- size_t size;
- njs_uint_t n, max_args, closures;
- njs_value_t *value, *bound;
+ size_t n, frame_size;
+ uint32_t args_count, value_count, value_size, temp_size;
+ njs_value_t *value, *bound, **new, **temp;
njs_frame_t *frame;
njs_function_t *target;
njs_native_frame_t *native_frame;
lambda = target->u.lambda;
}
- max_args = njs_max(nargs, lambda->nargs);
+ args_count = function->args_offset + njs_max(nargs, lambda->nargs);
+ value_count = args_count + njs_max(args_count, lambda->nlocal);
- closures = lambda->nesting + lambda->block_closures;
+ value_size = value_count * sizeof(njs_value_t *);
+ temp_size = lambda->temp * sizeof(njs_value_t *);
- size = njs_frame_size(closures)
- + (function->args_offset + max_args) * sizeof(njs_value_t)
- + lambda->local_size;
+ frame_size = value_size + temp_size
+ + ((value_count + lambda->temp) * sizeof(njs_value_t));
- native_frame = njs_function_frame_alloc(vm, size);
+ native_frame = njs_function_frame_alloc(vm, NJS_FRAME_SIZE + frame_size);
if (njs_slow_path(native_frame == NULL)) {
return NJS_ERROR;
}
+ /* Local */
+
+ new = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE);
+ value = (njs_value_t *) ((u_char *) new + value_size + temp_size);
+
+ n = value_count + lambda->temp;
+
+ while (n != 0) {
+ n--;
+ new[n] = &value[n];
+ njs_set_invalid(new[n]);
+ }
+
+ /* Temp */
+
+ temp = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE
+ + value_size);
+
+ native_frame->arguments = value;
+ native_frame->arguments_offset = value + (function->args_offset - 1);
+ native_frame->local = new + args_count;
+ native_frame->temp = temp;
native_frame->function = target;
native_frame->nargs = nargs;
native_frame->ctor = ctor;
native_frame->native = 0;
native_frame->pc = NULL;
- /* Function arguments. */
+ /* Set this and bound arguments. */
+ *native_frame->local[0] = *this;
- value = (njs_value_t *) ((u_char *) native_frame +
- njs_frame_size(closures));
- native_frame->arguments = value;
-
- if (bound == NULL) {
- *value = *this;
-
- if (njs_slow_path(function->global_this
- && njs_is_null_or_undefined(this))) {
- njs_set_object(value, &vm->global_object);
- }
-
- value++;
+ if (njs_slow_path(function->global_this
+ && njs_is_null_or_undefined(this)))
+ {
+ njs_set_object(native_frame->local[0], &vm->global_object);
+ }
- } else {
+ if (bound != NULL) {
n = function->args_offset;
native_frame->nargs += n - 1;
- if (ctor) {
- *value++ = *this;
- bound++;
- n--;
+ if (!ctor) {
+ *native_frame->local[0] = *bound;
}
+ bound++;
+ n--;
+
while (n != 0) {
*value++ = *bound++;
n--;
};
}
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
+ /* Copy arguments. */
if (args != NULL) {
while (nargs != 0) {
*value++ = *args++;
- max_args--;
nargs--;
}
}
- while (max_args != 0) {
- njs_set_undefined(value++);
- max_args--;
- }
-
frame = (njs_frame_t *) native_frame;
frame->exception.catch = NULL;
frame->exception.next = NULL;
- frame->local = value;
frame->previous_active_frame = vm->active_frame;
return NJS_OK;
size_t spare_size, chunk_size;
njs_native_frame_t *frame;
- /*
- * The size value must be aligned to njs_value_t because vm->top_frame
- * may point to frame->free and vm->top_frame is used as a base pointer
- * in njs_vm_continuation() which is expected to return pointers aligned
- * to njs_value_t.
- */
- size = njs_align_size(size, sizeof(njs_value_t));
-
- spare_size = vm->top_frame->free_size;
+ spare_size = vm->top_frame ? vm->top_frame->free_size : 0;
if (njs_fast_path(size <= spare_size)) {
frame = (njs_native_frame_t *) vm->top_frame->free;
return ret;
}
- ret = njs_function_frame_invoke(vm, (njs_index_t) &dst);
+ ret = njs_function_frame_invoke(vm, &dst);
if (ret == NJS_OK) {
*retval = dst;
njs_int_t
njs_function_lambda_call(njs_vm_t *vm)
{
- size_t size;
+ uint32_t n;
njs_int_t ret;
- njs_uint_t n, nesting;
njs_frame_t *frame;
- njs_value_t *dst, *src;
- njs_closure_t *closure, **closures;
+ njs_value_t *args, **local, *value;
+ njs_value_t **cur_local, **cur_closures, **cur_temp;
njs_function_t *function;
njs_function_lambda_t *lambda;
frame = (njs_frame_t *) vm->top_frame;
function = frame->native.function;
- lambda = function->u.lambda;
+ if (function->global && !function->closure_copied) {
+ ret = njs_function_capture_global_closures(vm, function);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+ }
-#if (NJS_DEBUG)
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL;
-#endif
+ lambda = function->u.lambda;
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
+ args = vm->top_frame->arguments;
+ local = vm->top_frame->local + function->args_offset;
- /* Function local variables and temporary values. */
+ /* Move all arguments. */
- vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
+ for (n = 0; n < function->args_count; n++) {
+ if (!njs_is_valid(args)) {
+ njs_set_undefined(args);
+ }
- memcpy(frame->local, lambda->local_scope, lambda->local_size);
+ *local++ = args++;
+ }
- /* Parent closures values. */
+ /* Store current level. */
- n = 0;
- nesting = lambda->nesting;
+ cur_local = vm->levels[NJS_LEVEL_LOCAL];
+ cur_closures = vm->levels[NJS_LEVEL_CLOSURE];
+ cur_temp = vm->levels[NJS_LEVEL_TEMP];
- if (nesting != 0) {
- closures = njs_function_active_closures(vm, function);
- do {
- closure = *closures++;
+ /* Replace current level. */
- njs_frame_closures(frame)[n] = closure;
- vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
+ vm->levels[NJS_LEVEL_LOCAL] = vm->top_frame->local;
+ vm->levels[NJS_LEVEL_CLOSURE] = njs_function_closures(function);
+ vm->levels[NJS_LEVEL_TEMP] = frame->native.temp;
- n++;
- } while (n < nesting);
+ if (lambda->rest_parameters) {
+ ret = njs_function_rest_parameters_init(vm, &frame->native);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
}
- /* Function closure values. */
+ /* Self */
- if (lambda->block_closures > 0) {
- closure = NULL;
+ if (lambda->self != NJS_INDEX_NONE) {
+ value = njs_scope_value(vm, lambda->self);
- size = lambda->closure_size;
+ if (!njs_is_valid(value)) {
+ njs_set_function(value, function);
+ }
+ }
- if (size != 0) {
- closure = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
- if (njs_slow_path(closure == NULL)) {
- njs_memory_error(vm);
- return NJS_ERROR;
- }
+ vm->active_frame = frame;
- size -= sizeof(njs_value_t);
- closure->u.count = 0;
- dst = closure->values;
+ /* Closures */
- src = lambda->closure_scope;
+ n = lambda->ndeclarations;
- do {
- *dst++ = *src++;
- size -= sizeof(njs_value_t);
- } while (size != 0);
- }
+ while (n != 0) {
+ n--;
- njs_frame_closures(frame)[n] = closure;
- vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
- }
+ function = njs_function(lambda->declarations[n]);
- if (lambda->rest_parameters) {
- ret = njs_function_rest_parameters_init(vm, &frame->native);
+ ret = njs_function_capture_closure(vm, function, function->u.lambda);
if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
+ return ret;
}
}
- vm->active_frame = frame;
+ ret = njs_vmcode_interpreter(vm, lambda->start);
+
+ /* Restore current level. */
+ vm->levels[NJS_LEVEL_LOCAL] = cur_local;
+ vm->levels[NJS_LEVEL_CLOSURE] = cur_closures;
+ vm->levels[NJS_LEVEL_TEMP] = cur_temp;
- return njs_vmcode_interpreter(vm, lambda->start);
+ return ret;
}
njs_function_native_call(njs_vm_t *vm)
{
njs_int_t ret;
- njs_value_t *value;
njs_function_t *function, *target;
njs_native_frame_t *native, *previous;
njs_function_native_t call;
njs_vm_scopes_restore(vm, native, previous);
if (!native->skip) {
- value = njs_vmcode_operand(vm, native->retval);
- /*
- * GC: value external/internal++ depending
- * on vm->retval and retval type
- */
- *value = vm->retval;
+ *native->retval = vm->retval;
}
njs_function_frame_free(vm, native);
}
+njs_int_t
+njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
+ njs_function_lambda_t *lambda)
+{
+ void *start, *end;
+ uint32_t n;
+ njs_value_t *value, **closure;
+ njs_native_frame_t *frame;
+
+ if (lambda->nclosures == 0) {
+ return NJS_OK;
+ }
+
+ frame = &vm->active_frame->native;
+
+ while (frame->native) {
+ frame = frame->previous;
+ }
+
+ start = frame;
+ end = frame->free;
+
+ closure = njs_function_closures(function);
+ n = lambda->nclosures;
+
+ do {
+ n--;
+
+ value = njs_scope_value(vm, lambda->closures[n]);
+
+ if (start <= (void *) value && (void *) value < end) {
+ value = njs_scope_value_clone(vm, lambda->closures[n], value);
+ if (njs_slow_path(value == NULL)) {
+ return NJS_ERROR;
+ }
+ }
+
+ closure[n] = value;
+
+ } while (n != 0);
+
+ return NJS_OK;
+}
+
+
+njs_inline njs_value_t *
+njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
+ void *start, void *end)
+{
+ njs_value_t *value, *newval;
+
+ value = scope[njs_scope_index_value(index)];
+
+ if (start <= (void *) value && end > (void *) value) {
+ newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+ if (njs_slow_path(newval == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ *newval = *value;
+ value = newval;
+ }
+
+ scope[njs_scope_index_value(index)] = value;
+
+ return value;
+}
+
+
+njs_int_t
+njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
+{
+ void *start, *end;
+ uint32_t n;
+ njs_value_t *value, **refs, **global;
+ njs_index_t *indexes, index;
+ njs_native_frame_t *native;
+ njs_function_lambda_t *lambda;
+
+ lambda = function->u.lambda;
+
+ if (lambda->nclosures == 0) {
+ return NJS_OK;
+ }
+
+ native = vm->top_frame;
+
+ while (native->previous->function != NULL) {
+ native = native->previous;
+ }
+
+ start = native;
+ end = native->free;
+
+ indexes = lambda->closures;
+ refs = njs_function_closures(function);
+
+ global = vm->levels[NJS_LEVEL_GLOBAL];
+
+ n = lambda->nclosures;
+
+ while (n > 0) {
+ n--;
+
+ index = indexes[n];
+
+ switch (njs_scope_index_type(index)) {
+ case NJS_LEVEL_LOCAL:
+ value = njs_function_closure_value(vm, native->local, index,
+ start, end);
+ break;
+
+ case NJS_LEVEL_GLOBAL:
+ value = njs_function_closure_value(vm, global, index, start, end);
+ break;
+
+ default:
+ njs_type_error(vm, "unexpected value type for closure \"%uD\"",
+ njs_scope_index_type(index));
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path(value == NULL)) {
+ return NJS_ERROR;
+ }
+
+ refs[n] = value;
+ }
+
+ function->closure_copied = 1;
+
+ return NJS_OK;
+}
+
+
static njs_value_t *
njs_function_property_prototype_set(njs_vm_t *vm, njs_lvlhsh_t *hash,
njs_value_t *prototype)
parser.lexer = &lexer;
- ret = njs_parser(vm, &parser, NULL);
+ ret = njs_parser(vm, &parser);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
type = &safe_ast[0];
for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) {
- if (node == NULL || node->left != NULL) {
+ if (node == NULL) {
+ goto fail;
+ }
+
+ if (node->left != NULL
+ && node->token_type != NJS_TOKEN_FUNCTION_EXPRESSION
+ && node->left->token_type != NJS_TOKEN_NAME)
+ {
goto fail;
}
return ret;
}
- ret = njs_variables_scope_reference(vm, scope);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
njs_memzero(&generator, sizeof(njs_generator_t));
code = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous);
lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
- function = njs_function_alloc(vm, lambda, NULL, 0);
+ function = njs_function_alloc(vm, lambda);
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
+ function->global = 1;
function->global_this = 1;
function->args_count = lambda->nargs - lambda->rest_parameters;
struct njs_function_lambda_s {
- uint32_t nargs;
- uint32_t local_size;
- uint32_t closure_size;
+ njs_index_t *closures;
+ uint32_t nclosures;
+ uint32_t nlocal;
+ uint32_t temp;
+
+ njs_value_t **declarations;
+ uint32_t ndeclarations;
- /* Function nesting level. */
- uint8_t nesting; /* 4 bits */
+ njs_index_t self;
- /* Function internal block closures levels. */
- uint8_t block_closures; /* 4 bits */
+ uint32_t nargs;
uint8_t ctor; /* 1 bit */
uint8_t rest_parameters; /* 1 bit */
- /* Initial values of local scope. */
- njs_value_t *local_scope;
- njs_value_t *closure_scope;
-
u_char *start;
};
njs_align_size(sizeof(njs_native_frame_t), sizeof(njs_value_t))
/* The frame size must be aligned to njs_value_t. */
-#define njs_frame_size(closures) \
- njs_align_size(sizeof(njs_frame_t) + closures * sizeof(njs_closure_t *), \
- sizeof(njs_value_t))
+#define NJS_FRAME_SIZE \
+ njs_align_size(sizeof(njs_frame_t), sizeof(njs_value_t))
-#define NJS_FRAME_SPARE_SIZE 512
+#define NJS_FRAME_SPARE_SIZE (4 * 1024)
struct njs_native_frame_s {
njs_value_t *arguments;
njs_object_t *arguments_object;
-
- njs_index_t retval;
+ njs_value_t *arguments_offset;
+ njs_value_t **local;
+ njs_value_t **temp;
uint32_t size;
uint32_t free_size;
+
+ njs_value_t *retval;
+
uint32_t nargs;
uint8_t native; /* 1 bit */
njs_exception_t exception;
njs_frame_t *previous_active_frame;
-
- njs_value_t *local;
-
-#define njs_frame_closures(frame) \
- ((njs_closure_t **) ((u_char *) frame + sizeof(njs_frame_t)))
};
-njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
- njs_closure_t *closures[], njs_bool_t shared);
+njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda);
njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
njs_int_t njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
njs_value_t *name, const char *prefix);
+njs_function_t *njs_function_copy(njs_vm_t *vm, njs_function_t *function);
njs_int_t njs_function_arguments_object_init(njs_vm_t *vm,
njs_native_frame_t *frame);
njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm,
njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor);
njs_int_t njs_function_lambda_call(njs_vm_t *vm);
njs_int_t njs_function_native_call(njs_vm_t *vm);
+njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame);
+njs_int_t njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
+ njs_function_lambda_t *lambda);
+njs_int_t njs_function_capture_global_closures(njs_vm_t *vm,
+ njs_function_t *function);
njs_inline njs_function_lambda_t *
njs_inline njs_int_t
-njs_function_frame_invoke(njs_vm_t *vm, njs_index_t retval)
+njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
{
njs_native_frame_t *frame;
}
+njs_inline njs_value_t **
+njs_function_closures(const njs_function_t *func)
+{
+ return (njs_value_t **) ((u_char *) func + sizeof(njs_function_t));
+}
+
+
extern const njs_object_type_init_t njs_function_type_init;
extern const njs_object_init_t njs_function_instance_init;
extern const njs_object_init_t njs_arrow_instance_init;
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Dmitry Volyntsev
+ * Copyright (C) Alexander Borisov
* Copyright (C) NGINX, Inc.
*/
* because pointer to u_char accesses only one byte so this does not
* work on big endian platforms.
*/
- njs_jump_off_t jump_offset;
+ njs_jump_off_t jump_offset;
njs_generator_patch_t *next;
njs_str_t label;
static njs_int_t njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
static njs_int_t njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
- njs_parser_node_t *node, njs_reference_type_t type);
+ njs_parser_node_t *node, njs_reference_type_t type,
+ njs_variable_t **retvar);
static njs_int_t njs_generate_var_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_if_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_array(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
+static njs_int_t njs_generate_function_expression(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
static njs_int_t njs_generate_regexp(njs_vm_t *vm, njs_generator_t *generator,
static njs_int_t njs_generate_function_scope(njs_vm_t *vm,
njs_function_lambda_t *lambda, njs_parser_node_t *node,
const njs_str_t *name);
-static njs_int_t njs_generate_lambda_variables(njs_vm_t *vm,
+static int64_t njs_generate_lambda_variables(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_return_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node);
+static njs_int_t njs_generate_move_arguments(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_try_statement(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_throw_statement(njs_vm_t *vm,
case NJS_TOKEN_FALSE:
case NJS_TOKEN_NUMBER:
case NJS_TOKEN_STRING:
- node->index = njs_value_index(vm, &node->u.value, generator->runtime);
- if (njs_fast_path(node->index != NJS_INDEX_NONE)) {
- return NJS_OK;
+ node->index = njs_scope_global_index(vm, &node->u.value,
+ generator->runtime);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
}
- return NJS_ERROR;
+ return NJS_OK;
case NJS_TOKEN_OBJECT_VALUE:
node->index = node->u.object->index;
return njs_generate_array(vm, generator, node);
case NJS_TOKEN_FUNCTION_EXPRESSION:
+ return njs_generate_function_expression(vm, generator, node);
+
+ case NJS_TOKEN_FUNCTION:
return njs_generate_function(vm, generator, node);
case NJS_TOKEN_REGEXP:
case NJS_TOKEN_TEMPLATE_LITERAL:
return njs_generate_template_literal(vm, generator, node);
- case NJS_TOKEN_THIS:
case NJS_TOKEN_EXTERNAL:
return NJS_OK;
case NJS_TOKEN_NAME:
case NJS_TOKEN_ARGUMENTS:
case NJS_TOKEN_EVAL:
- case NJS_TOKEN_NON_LOCAL_THIS:
+ case NJS_TOKEN_THIS:
return njs_generate_name(vm, generator, node);
- case NJS_TOKEN_GLOBAL_OBJECT:
- if (vm->options.module) {
- node->index = njs_value_index(vm, &njs_value_undefined,
- generator->runtime);
- if (njs_fast_path(node->index != NJS_INDEX_NONE)) {
- return NJS_OK;
- }
-
- return NJS_ERROR;
- }
-
- node->index = NJS_INDEX_GLOBAL_OBJECT;
-
- return NJS_OK;
-
- case NJS_TOKEN_FUNCTION:
+ case NJS_TOKEN_FUNCTION_DECLARATION:
return njs_generate_function_declaration(vm, generator, node);
case NJS_TOKEN_FUNCTION_CALL:
njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_variable_t *var;
- njs_vmcode_object_copy_t *copy;
-
- var = njs_variable_resolve(vm, node);
-
- if (var != NULL && var->type == NJS_VARIABLE_FUNCTION) {
+ njs_variable_t *var;
+ njs_vmcode_function_copy_t *copy;
- node->index = njs_generate_dest_index(vm, generator, node);
- if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
- return node->index;
- }
+ var = njs_variable_reference(vm, node);
+ if (njs_slow_path(var == NULL)) {
+ return njs_generate_global_reference(vm, generator, node, 1);
+ }
- njs_generate_code(generator, njs_vmcode_object_copy_t, copy,
- NJS_VMCODE_OBJECT_COPY, 2, node);
+ if (var->function && var->type == NJS_VARIABLE_FUNCTION) {
+ njs_generate_code(generator, njs_vmcode_function_copy_t, copy,
+ NJS_VMCODE_FUNCTION_COPY, 0, node);
+ copy->function = &var->value;
copy->retval = node->index;
- copy->object = var->index;
-
- return NJS_OK;
}
- return njs_generate_variable(vm, generator, node, NJS_REFERENCE);
+ return NJS_OK;
}
static njs_int_t
njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
- njs_parser_node_t *node, njs_reference_type_t type)
+ njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar)
{
- njs_index_t index;
+ njs_variable_t *var;
+ njs_vmcode_function_copy_t *copy;
- index = njs_variable_index(vm, node);
- if (njs_slow_path(index == NJS_INDEX_NONE)) {
+ var = njs_variable_reference(vm, node);
+ if (retvar != NULL) {
+ *retvar = var;
+ }
+
+ if (njs_slow_path(var == NULL)) {
switch (type) {
case NJS_DECLARATION:
return njs_generate_reference_error(vm, generator, node);
}
}
- node->index = index;
+ if (var->function && var->type == NJS_VARIABLE_FUNCTION) {
+ njs_generate_code(generator, njs_vmcode_function_copy_t, copy,
+ NJS_VMCODE_FUNCTION_COPY, 0, node);
+ copy->function = &var->value;
+ copy->retval = node->index;
+ }
return NJS_OK;
}
njs_parser_node_t *node)
{
njs_int_t ret;
- njs_index_t index;
+ njs_variable_t *var;
njs_parser_node_t *lvalue, *expr;
njs_vmcode_move_t *move;
lvalue = node->left;
- index = njs_variable_index(vm, lvalue);
- if (njs_slow_path(index == NJS_INDEX_NONE)) {
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION, &var);
+ if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- lvalue->index = index;
-
+ lvalue->index = var->index;
expr = node->right;
if (expr == NULL) {
foreach = node->left;
+ ret = njs_generator(vm, generator, foreach->left);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
ret = njs_generator(vm, generator, foreach->right);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, prop_offset);
- ret = njs_generator(vm, generator, node->left->left);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next,
NJS_VMCODE_PROPERTY_NEXT, 3, node->left->left);
prop_offset = njs_code_offset(generator, prop_next);
njs_generate_code(generator, njs_vmcode_stop_t, stop,
NJS_VMCODE_STOP, 1, node);
- index = NJS_INDEX_NONE;
+ index = njs_scope_undefined_index(vm, 0);
node = node->right;
- if (node != NULL && node->token_type != NJS_TOKEN_FUNCTION) {
- index = node->index;
- }
-
- if (index == NJS_INDEX_NONE) {
- index = njs_value_index(vm, &njs_value_undefined,
- generator->runtime);
+ if (node != NULL) {
+ if ((node->index != NJS_INDEX_NONE
+ && node->token_type != NJS_TOKEN_FUNCTION_DECLARATION)
+ || node->token_type == NJS_TOKEN_THIS)
+ {
+ index = node->index;
+ }
}
stop->retval = index;
if (lvalue->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+ NULL);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
if (lvalue->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+ NULL);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
}
+static njs_int_t
+njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_variable_t *var;
+ njs_function_lambda_t *lambda;
+ njs_vmcode_function_t *function;
+ const njs_lexer_entry_t *lex_entry;
+
+ var = njs_variable_reference(vm, node->left);
+ if (njs_slow_path(var == NULL)) {
+ return njs_generate_reference_error(vm, generator, node->left);
+ }
+
+ lambda = node->u.value.data.u.lambda;
+
+ lex_entry = njs_lexer_entry(var->unique_id);
+ if (njs_slow_path(lex_entry == NULL)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_generate_function_scope(vm, lambda, node, &lex_entry->name);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(generator, njs_vmcode_function_t, function,
+ NJS_VMCODE_FUNCTION, 1, node);
+ function->lambda = lambda;
+
+ node->index = njs_generate_object_dest_index(vm, generator, node);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ function->retval = node->index;
+
+ return NJS_OK;
+}
+
+
static njs_int_t
njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
expr = node->left;
if (expr->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, expr, NJS_TYPEOF);
+ ret = njs_generate_variable(vm, generator, expr, NJS_TYPEOF, NULL);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
if (lvalue->token_type == NJS_TOKEN_NAME) {
- ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION);
+ ret = njs_generate_variable(vm, generator, lvalue, NJS_DECLARATION,
+ NULL);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
{
njs_int_t ret;
njs_variable_t *var;
+ njs_function_t *function;
njs_function_lambda_t *lambda;
const njs_lexer_entry_t *lex_entry;
- var = njs_variable_resolve(vm, node);
+ var = njs_variable_reference(vm, node);
if (njs_slow_path(var == NULL)) {
return njs_generate_reference_error(vm, generator, node);
}
- if (!njs_is_function(&var->value)) {
- /* A variable was declared with the same name. */
- return NJS_OK;
+ if (njs_is_function(&var->value)) {
+ lambda = njs_function(&var->value)->u.lambda;
+ } else {
+ lambda = var->value.data.u.lambda;
}
- lambda = njs_function_lambda(&var->value);
-
lex_entry = njs_lexer_entry(node->u.reference.unique_id);
if (njs_slow_path(lex_entry == NULL)) {
return NJS_ERROR;
return ret;
}
+ function = njs_function_alloc(vm, lambda);
+ if (njs_slow_path(function == NULL)) {
+ return NJS_ERROR;
+ }
+
+ function->global = njs_function_scope(var->scope)->type == NJS_SCOPE_GLOBAL;
+ function->object.shared = 1;
+ function->args_count = lambda->nargs - lambda->rest_parameters;
+
+ njs_set_function(&var->value, function);
+
return ret;
}
njs_generate_function_scope(njs_vm_t *vm, njs_function_lambda_t *lambda,
njs_parser_node_t *node, const njs_str_t *name)
{
- size_t size;
- njs_arr_t *closure;
+ njs_arr_t *arr;
njs_bool_t module;
njs_vm_code_t *code;
njs_generator_t generator;
njs_memzero(&generator, sizeof(njs_generator_t));
- module = node->right->scope->module;
- file_node = module ? node->right : node;
-
node = node->right;
code = njs_generate_scope(vm, &generator, node->scope, name);
return NJS_ERROR;
}
+ module = node->right->scope->module;
+ file_node = module ? node->right : node;
+
code->file = file_node->scope->file;
- size = 0;
- closure = node->scope->values[1];
+ lambda->start = generator.code_start;
+ lambda->closures = generator.closures->start;
+ lambda->nclosures = generator.closures->items;
+ lambda->nlocal = node->scope->items;
+ lambda->temp = node->scope->temp;
- if (closure != NULL) {
- lambda->block_closures = 1;
- lambda->closure_scope = closure->start;
- size = (1 + closure->items) * sizeof(njs_value_t);
+ if (node->scope->declarations != NULL) {
+ arr = node->scope->declarations;
+ lambda->declarations = arr->start;
+ lambda->ndeclarations = arr->items;
}
- lambda->closure_size = size;
-
- lambda->nesting = node->scope->nesting;
-
- lambda->start = generator.code_start;
- lambda->local_size = generator.scope_size;
- lambda->local_scope = generator.local_scope;
-
return NJS_OK;
}
njs_parser_scope_t *scope, const njs_str_t *name)
{
u_char *p;
- size_t size;
- uintptr_t scope_size;
- njs_int_t ret;
- njs_uint_t n, index;
- njs_value_t *value;
+ int64_t nargs;
+ njs_uint_t index;
njs_vm_code_t *code;
generator->code_size = 128;
generator->code_start = p;
generator->code_end = p;
- ret = njs_generate_lambda_variables(vm, generator, scope->top);
- if (njs_slow_path(ret != NJS_OK)) {
+ nargs = njs_generate_lambda_variables(vm, generator, scope->top);
+ if (njs_slow_path(nargs < NJS_OK)) {
return NULL;
}
generator->lines = code->lines;
}
+ generator->closures = njs_arr_create(vm->mem_pool, 4, sizeof(njs_index_t));
+ if (njs_slow_path(generator->closures == NULL)) {
+ return NULL;
+ }
+
+ scope->closures = generator->closures;
+
if (njs_slow_path(njs_generator(vm, generator, scope->top) != NJS_OK)) {
return NULL;
}
generator->code_size = generator->code_end - generator->code_start;
- scope_size = njs_scope_offset(scope->next_index[0]);
-
- if (scope->type == NJS_SCOPE_GLOBAL) {
- scope_size -= NJS_INDEX_GLOBAL_OFFSET;
- }
-
- generator->local_scope = njs_mp_alloc(vm->mem_pool, scope_size);
- if (njs_slow_path(generator->local_scope == NULL)) {
- return NULL;
- }
-
- generator->scope_size = scope_size;
-
- size = scope->values[0]->items * sizeof(njs_value_t);
-
- njs_thread_log_debug("SCOPE SIZE: %uz %uz", size, scope_size);
-
- p = memcpy(generator->local_scope, scope->values[0]->start, size);
- value = (njs_value_t *) (p + size);
-
- for (n = scope_size - size; n != 0; n -= sizeof(njs_value_t)) {
- njs_set_undefined(value++);
- }
-
return code;
}
-static njs_int_t
+static int64_t
njs_generate_lambda_variables(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_index_t index;
+ int64_t nargs;
njs_variable_t *var;
njs_rbtree_node_t *rb_node;
- njs_vmcode_move_t *move;
- njs_vmcode_this_t *this;
njs_variable_node_t *var_node;
njs_vmcode_arguments_t *arguments;
+ nargs = 0;
+
rb_node = njs_rbtree_min(&node->scope->variables);
while (njs_rbtree_is_there_successor(&node->scope->variables, rb_node)) {
break;
}
- if (var->argument != 0) {
- index = njs_scope_index((var->argument - 1), NJS_SCOPE_ARGUMENTS);
-
- njs_generate_code_move(generator, move, var->index, index, node);
- }
-
- if (var->this_object) {
- njs_generate_code(generator, njs_vmcode_this_t, this,
- NJS_VMCODE_THIS, 1, NULL);
- this->dst = var->index;
+ if (var->argument) {
+ nargs++;
}
if (var->arguments_object) {
rb_node = njs_rbtree_node_successor(&node->scope->variables, rb_node);
}
- return NJS_OK;
+ return nargs;
}
index = node->right->index;
} else {
- index = njs_value_index(vm, &njs_value_undefined, generator->runtime);
+ index = njs_scope_global_index(vm, &njs_value_undefined,
+ generator->runtime);
+ }
+
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
}
immediate = njs_generate_lookup_block(generator->block, NJS_GENERATOR_TRY,
njs_generate_function_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
+ njs_int_t ret, nargs;
njs_jump_off_t func_offset;
+ njs_variable_t *var;
njs_parser_node_t *name;
njs_vmcode_function_frame_t *func;
+ var = NULL;
+
if (node->left != NULL) {
/* Generate function code in function expression. */
ret = njs_generator(vm, generator, node->left);
name = node->left;
} else {
- ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE);
+ ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE, &var);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
func->ctor = node->ctor;
func->name = name->index;
- ret = njs_generate_call(vm, generator, node);
+ nargs = njs_generate_move_arguments(vm, generator, node);
+ if (njs_slow_path(nargs < 0)) {
+ return nargs;
+ }
- if (njs_fast_path(ret >= 0)) {
- func = njs_code_ptr(generator, njs_vmcode_function_frame_t,
- func_offset);
- func->nargs = ret;
- return NJS_OK;
+ func = njs_code_ptr(generator, njs_vmcode_function_frame_t, func_offset);
+ func->nargs = nargs;
+
+ ret = njs_generate_call(vm, generator, node);
+ if (njs_fast_path(ret != NJS_OK)) {
+ return ret;
}
- return ret;
+ return NJS_OK;
}
njs_generate_method_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
+ njs_int_t ret, nargs;
njs_jump_off_t method_offset;
njs_parser_node_t *prop;
njs_vmcode_method_frame_t *method;
method->object = prop->left->index;
method->method = prop->right->index;
- ret = njs_generate_children_indexes_release(vm, generator, prop);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
+ nargs = njs_generate_move_arguments(vm, generator, node);
+ if (njs_slow_path(nargs < 0)) {
+ return nargs;
}
- ret = njs_generate_call(vm, generator, node);
+ method = njs_code_ptr(generator, njs_vmcode_method_frame_t, method_offset);
+ method->nargs = nargs;
- if (njs_fast_path(ret >= 0)) {
- method = njs_code_ptr(generator, njs_vmcode_method_frame_t,
- method_offset);
- method->nargs = ret;
- return NJS_OK;
+ ret = njs_generate_call(vm, generator, node);
+ if (njs_fast_path(ret != NJS_OK)) {
+ return ret;
}
- return ret;
+ return NJS_OK;
}
njs_generate_call(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_uint_t nargs;
njs_index_t retval;
- njs_parser_node_t *arg;
- njs_vmcode_move_t *move;
njs_vmcode_function_call_t *call;
- nargs = 0;
-
- for (arg = node->right; arg != NULL; arg = arg->right) {
- nargs++;
-
- ret = njs_generator(vm, generator, arg->left);
- if (njs_slow_path(ret != NJS_OK)) {
- return ret;
- }
-
- if (arg->index != arg->left->index) {
- njs_generate_code_move(generator, move, arg->index,
- arg->left->index, node);
- }
- }
-
retval = njs_generate_dest_index(vm, generator, node);
if (njs_slow_path(retval == NJS_INDEX_ERROR)) {
return retval;
NJS_VMCODE_FUNCTION_CALL, 1, node);
call->retval = retval;
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_generate_move_arguments(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_int_t ret;
+ njs_uint_t nargs;
+ njs_parser_node_t *arg;
+ njs_vmcode_move_arg_t *move_arg;
+
+ nargs = 0;
+
+ for (arg = node->right; arg != NULL; arg = arg->right) {
+ ret = njs_generator(vm, generator, arg->left);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ njs_generate_code(generator, njs_vmcode_move_arg_t, move_arg,
+ NJS_VMCODE_MOVE_ARG, 0, node);
+ move_arg->src = arg->left->index;
+ move_arg->dst = nargs;
+
+ nargs++;
+ }
+
return nargs;
}
#define njs_generate_code_finally(generator, _code, _retval, _exit, node) \
do { \
njs_generate_code(generator, njs_vmcode_finally_t, _code, \
- NJS_VMCODE_FINALLY, 2, node); \
+ NJS_VMCODE_FINALLY, 1, node); \
_code->retval = _retval; \
_code->exit_value = _exit; \
_code->continue_offset = offsetof(njs_vmcode_finally_t, \
njs_index_t exception_index, exit_index, catch_index;
njs_jump_off_t try_offset, try_end_offset, catch_offset,
catch_end_offset;
+ njs_variable_t *var;
const njs_str_t *dest_label;
njs_vmcode_catch_t *catch;
njs_vmcode_finally_t *finally;
njs_generate_patch_block(vm, generator, try_block->exit);
njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_break,
- NJS_VMCODE_TRY_BREAK, 2, NULL);
+ NJS_VMCODE_TRY_BREAK, 1, NULL);
try_break->exit_value = exit_index;
try_break->offset = -sizeof(njs_vmcode_try_end_t);
njs_generate_patch_block(vm, generator, try_block->continuation);
njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_continue,
- NJS_VMCODE_TRY_CONTINUE, 2, NULL);
+ NJS_VMCODE_TRY_CONTINUE, 1, NULL);
try_continue->exit_value = exit_index;
try_continue->offset = -sizeof(njs_vmcode_try_end_t);
if (node->token_type == NJS_TOKEN_CATCH) {
/* A "try/catch" case. */
- catch_index = njs_variable_index(vm, node->left);
- if (njs_slow_path(catch_index == NJS_INDEX_NONE)) {
+ var = njs_variable_reference(vm, node->left);
+ if (njs_slow_path(var == NULL)) {
return NJS_ERROR;
}
+ catch_index = node->left->index;
+
njs_generate_code_catch(generator, catch, catch_index, node);
ret = njs_generator(vm, generator, node->right);
if (node->left != NULL) {
/* A try/catch/finally case. */
- catch_index = njs_variable_index(vm, node->left->left);
- if (njs_slow_path(catch_index == NJS_INDEX_NONE)) {
+ var = njs_variable_reference(vm, node->left->left);
+ if (njs_slow_path(var == NULL)) {
return NJS_ERROR;
}
+ catch_index = node->left->left->index;
+
njs_generate_code_catch(generator, catch, catch_index, node);
catch_offset = njs_code_offset(generator, catch);
njs_generate_patch_block(vm, generator, catch_block->exit);
njs_generate_code(generator, njs_vmcode_try_trampoline_t,
- try_break, NJS_VMCODE_TRY_BREAK, 2, NULL);
+ try_break, NJS_VMCODE_TRY_BREAK, 1, NULL);
try_break->exit_value = exit_index;
catch_block->continuation);
njs_generate_code(generator, njs_vmcode_try_trampoline_t,
- try_continue, NJS_VMCODE_TRY_CONTINUE, 2,
+ try_continue, NJS_VMCODE_TRY_CONTINUE, 1,
NULL);
try_continue->exit_value = exit_index;
njs_int_t ret;
njs_index_t index;
njs_module_t *module;
+ njs_variable_t *var;
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 (njs_slow_path(index == NJS_INDEX_NONE)) {
+ var = njs_variable_reference(vm, lvalue);
+ if (njs_slow_path(var == NULL)) {
return NJS_ERROR;
}
+ index = lvalue->index;
+
if (expr->left != NULL) {
ret = njs_generator(vm, generator, expr->left);
if (njs_slow_path(ret != NJS_OK)) {
if (dest != NULL && dest->index != NJS_INDEX_NONE) {
index = dest->index;
- if (njs_is_callee_argument_index(index)) {
- /* Assgin object directly to a callee argument. */
- return index;
- }
-
if (node->left == NULL) {
/* Assign empty object directly to variable */
return index;
return *last;
}
- scope = node->scope;
-
- while (scope->type == NJS_SCOPE_BLOCK) {
- scope = scope->parent;
+ scope = njs_function_scope(node->scope);
+ if (njs_slow_path(scope == NULL)) {
+ return NJS_ERROR;
}
- return njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL,
- &njs_value_invalid);
+ return njs_scope_index(scope->type, scope->temp++, NJS_LEVEL_TEMP);
}
3, node);
prop_get->value = index;
- prop_get->object = NJS_INDEX_GLOBAL_OBJECT;
+
+ prop_get->object = njs_scope_global_this_index();
+ if (njs_slow_path(prop_get->object == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
lex_entry = njs_lexer_entry(node->u.reference.unique_id);
if (njs_slow_path(lex_entry == NULL)) {
return NJS_ERROR;
}
- prop_get->property = njs_value_index(vm, &property, generator->runtime);
- if (njs_slow_path(prop_get->property == NJS_INDEX_NONE)) {
+ prop_get->property = njs_scope_global_index(vm, &property,
+ generator->runtime);
+ if (njs_slow_path(prop_get->property == NJS_INDEX_ERROR)) {
return NJS_ERROR;
}
struct njs_generator_s {
njs_value_t *local_scope;
- size_t scope_size;
-
njs_generator_block_t *block;
njs_arr_t *index_cache;
+ njs_arr_t *closures;
njs_arr_t *lines;
NJS_TOKEN_TEMPLATE_LITERAL,
NJS_TOKEN_FUNCTION,
+ NJS_TOKEN_FUNCTION_DECLARATION,
NJS_TOKEN_FUNCTION_EXPRESSION,
NJS_TOKEN_FUNCTION_CALL,
NJS_TOKEN_METHOD_CALL,
NJS_TOKEN_THROW,
NJS_TOKEN_THIS,
- NJS_TOKEN_GLOBAL_OBJECT,
- NJS_TOKEN_NON_LOCAL_THIS,
NJS_TOKEN_ARGUMENTS,
NJS_TOKEN_EVAL,
#include <njs_lexer.h>
#include <njs_parser.h>
#include <njs_generator.h>
+#include <njs_scope.h>
#include <njs_boolean.h>
#include <njs_symbol.h>
module = *item;
if (module->function.native) {
- value = njs_vmcode_operand(vm, module->index);
+ value = njs_scope_valid_value(vm, module->index);
njs_set_object(value, &module->object);
} else {
- ret = njs_vm_invoke(vm, &module->function, NULL, 0, module->index);
+ ret = njs_vm_invoke(vm, &module->function, NULL, 0,
+ njs_scope_valid_value(vm, module->index));
if (ret == NJS_ERROR) {
return ret;
}
scope = njs_parser_global_scope(parser);
vm = parser->vm;
- module->index = njs_scope_next_index(vm, scope, NJS_SCOPE_INDEX_LOCAL,
- &njs_value_undefined);
- if (njs_slow_path(module->index == NJS_INDEX_ERROR)) {
- return NJS_ERROR;
- }
+ module->index = njs_scope_index(scope->type, scope->items, NJS_LEVEL_LOCAL);
+ scope->items++;
if (vm->modules == NULL) {
vm->modules = njs_arr_create(vm->mem_pool, 4, sizeof(njs_module_t *));
#include <njs_main.h>
-static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type);
+static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
+ njs_bool_t init_this);
static void njs_parser_scope_end(njs_parser_t *parser);
static njs_int_t njs_parser_check_error_state(njs_parser_t *parser,
static njs_parser_node_t *njs_parser_return_set(njs_parser_t *parser,
njs_parser_node_t *expr);
static njs_parser_node_t *njs_parser_variable_node(njs_parser_t *parser,
- uintptr_t unique_id, njs_variable_type_t type);
+ uintptr_t unique_id, njs_variable_type_t type, njs_variable_t **retvar);
static njs_parser_node_t *njs_parser_reference(njs_parser_t *parser,
njs_lexer_token_t *token);
njs_int_t
-njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_rbtree_t *prev_vars)
+njs_parser(njs_vm_t *vm, njs_parser_t *parser)
{
njs_int_t ret;
njs_lexer_token_t *token;
njs_set_undefined(&vm->retval);
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
-
- if (prev_vars != NULL) {
- /*
- * Copy the global scope variables from the previous
- * iteration of the accumulative mode.
- */
- ret = njs_variables_copy(vm, &parser->scope->variables, prev_vars);
- if (ret != NJS_OK) {
- return ret;
+ if (parser->scope == NULL) {
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL, 1);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
}
+
+ } else {
+ parser->scope->temp = 0;
+ parser->scope->top = NULL;
+ parser->node = NULL;
+ parser->ret = NJS_OK;
}
njs_queue_init(&parser->stack);
static njs_int_t
-njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type)
+njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type,
+ njs_bool_t init_this)
{
- njs_arr_t *values;
- njs_uint_t nesting;
- njs_lexer_t *lexer;
- njs_parser_scope_t *scope, *parent;
-
- nesting = 0;
-
- if (type == NJS_SCOPE_FUNCTION) {
-
- for (scope = parser->scope; scope != NULL; scope = scope->parent) {
+ njs_lexer_t *lexer;
+ njs_variable_t *var;
+ njs_parser_scope_t *scope, *parent;
+ const njs_lexer_keyword_entry_t *keyword;
- if (scope->type == NJS_SCOPE_FUNCTION) {
- nesting = scope->nesting + 1;
-
- if (nesting < NJS_MAX_NESTING) {
- break;
- }
-
- njs_parser_syntax_error(parser, "The maximum function nesting "
- "level is \"%d\"", NJS_MAX_NESTING);
-
- return NJS_ERROR;
- }
- }
- }
+ static const njs_str_t njs_this_str = njs_str("this");
scope = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_scope_t));
if (njs_slow_path(scope == NULL)) {
scope->type = type;
- if (type == NJS_SCOPE_FUNCTION) {
- scope->next_index[0] = type;
- scope->next_index[1] = NJS_SCOPE_CLOSURE + nesting
- + sizeof(njs_value_t);
-
- } else {
- if (type == NJS_SCOPE_GLOBAL) {
- type += NJS_INDEX_GLOBAL_OFFSET;
- }
-
- scope->next_index[0] = type;
- scope->next_index[1] = 0;
- }
-
- scope->nesting = nesting;
- scope->argument_closures = 0;
-
- njs_queue_init(&scope->nested);
njs_rbtree_init(&scope->variables, njs_parser_scope_rbtree_compare);
njs_rbtree_init(&scope->labels, njs_parser_scope_rbtree_compare);
njs_rbtree_init(&scope->references, njs_parser_scope_rbtree_compare);
- values = NULL;
-
- if (scope->type < NJS_SCOPE_BLOCK) {
- values = njs_arr_create(parser->vm->mem_pool, 4, sizeof(njs_value_t));
- if (njs_slow_path(values == NULL)) {
- return NJS_ERROR;
- }
- }
-
- scope->values[0] = values;
- scope->values[1] = NULL;
-
lexer = parser->lexer;
if (lexer->file.length != 0) {
scope->parent = parent;
parser->scope = scope;
- if (parent != NULL) {
- njs_queue_insert_tail(&parent->nested, &scope->link);
+ if (type == NJS_SCOPE_FUNCTION || type == NJS_SCOPE_GLOBAL) {
+ if (init_this) {
+ /* Add this as first variable. */
+ keyword = njs_lexer_keyword(njs_this_str.start,
+ njs_this_str.length);
+ if (njs_slow_path(keyword == NULL)) {
+ return NJS_ERROR;
+ }
- if (nesting == 0) {
- /* Inherit function nesting in blocks. */
- scope->nesting = parent->nesting;
+ var = njs_variable_add(parser, scope, (uintptr_t) keyword->value,
+ NJS_VARIABLE_VAR);
+ if (njs_slow_path(var == NULL)) {
+ return NJS_ERROR;
+ }
+
+ var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL);
}
}
+ scope->items = 1;
+
return NJS_OK;
}
njs_parser_next(parser, njs_parser_generator_declaration);
} else {
- node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
+ node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_DECLARATION);
if (node == NULL) {
return NJS_ERROR;
}
array->token_line = token->line;
template = parser->node;
- index = NJS_SCOPE_CALLEE_ARGUMENTS;
+
+ index = njs_scope_temp_index(template->scope);
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) {
node = njs_parser_argument(parser, array, index);
template->right = node;
temp->right = node;
- index += sizeof(njs_value_t);
+ index = njs_scope_temp_index(template->scope);
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
} else {
template->left = array;
parent->right = node;
parent = node;
- parser->target->index += sizeof(njs_value_t);
+ parser->target->index = njs_scope_temp_index(node->scope);
+ if (njs_slow_path(parser->target->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
} else {
ret = njs_parser_array_item(parser, template->left, parser->node);
/* MethodDefinition */
} else if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
- expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
if (expr == NULL) {
return NJS_ERROR;
}
return NJS_ERROR;
}
- if (parser->target->index == 0) {
- node->index = NJS_SCOPE_CALLEE_ARGUMENTS;
-
- } else {
- node->index = parser->target->index + sizeof(njs_value_t);
+ node->index = njs_scope_temp_index(node->scope);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
}
node->token_line = token->line;
void *target;
njs_int_t ret;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
if (ret != NJS_OK) {
return NJS_ERROR;
}
njs_lexer_token_t *token, njs_queue_link_t *current)
{
njs_int_t ret;
+ njs_variable_t *var;
njs_parser_node_t *name;
ret = njs_parser_binding_pattern(parser, token, current);
return NJS_DONE;
}
- name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR);
+ name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR,
+ &var);
if (name == NULL) {
return NJS_ERROR;
}
+ if (var->self) {
+ var->type = NJS_VARIABLE_VAR;
+ var->self = 0;
+ }
+
name->token_line = token->line;
parser->node = name;
}
var = njs_parser_variable_node(parser, token->unique_id,
- NJS_VARIABLE_VAR);
+ NJS_VARIABLE_VAR, NULL);
if (var == NULL) {
return NJS_ERROR;
}
return NJS_ERROR;
}
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0);
if (ret != NJS_OK) {
return NJS_ERROR;
}
if (njs_lexer_token_is_binding_identifier(token)) {
node = njs_parser_variable_node(parser, token->unique_id,
- NJS_VARIABLE_CATCH);
+ NJS_VARIABLE_CATCH, NULL);
if (node == NULL) {
return NJS_ERROR;
}
/*
* 14.1 Function Definitions.
*/
-static njs_function_t *
-njs_parser_function_alloc(njs_parser_t *parser, njs_variable_t *var)
-{
- njs_value_t *value;
- njs_function_t *function;
- njs_function_lambda_t *lambda;
-
- lambda = njs_function_lambda_alloc(parser->vm, 1);
- if (lambda == NULL) {
- njs_memory_error(parser->vm);
- return NULL;
- }
-
- /* TODO:
- * njs_function_t is used to pass lambda to
- * njs_generate_function_declaration() and is not actually needed.
- * real njs_function_t is created by njs_vmcode_function() in runtime.
- */
-
- function = njs_function_alloc(parser->vm, lambda, NULL, 1);
- if (function == NULL) {
- return NULL;
- }
-
- njs_set_function(&var->value, function);
-
- if (var->index != NJS_INDEX_NONE
- && njs_scope_accumulative(parser->vm, parser->scope))
- {
- value = (njs_value_t *) var->index;
- *value = var->value;
- }
-
- return function;
-}
-
-
static njs_int_t
njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
njs_int_t ret;
uintptr_t unique_id;
njs_variable_t *var;
- njs_function_t *function;
- njs_parser_node_t *node, *temp;
+ njs_parser_node_t *node;
if (!njs_lexer_token_is_binding_identifier(token)) {
return njs_parser_failed(parser);
njs_lexer_consume_token(parser->lexer, 1);
- var = njs_variable_add(parser, parser->scope, unique_id,
- NJS_VARIABLE_FUNCTION);
+ var = njs_variable_function_add(parser, parser->scope, unique_id,
+ NJS_VARIABLE_FUNCTION);
if (var == NULL) {
return NJS_ERROR;
}
- ret = njs_variable_reference(parser->vm, parser->scope, node,
- unique_id, NJS_DECLARATION);
- if (ret != NJS_OK) {
- return NJS_ERROR;
- }
+ node->u.value.data.u.lambda = var->value.data.u.lambda;
- function = njs_parser_function_alloc(parser, var);
- if (function == NULL) {
- return NJS_ERROR;
- }
+ node->left = (njs_parser_node_t *) unique_id;
- temp = njs_parser_node_new(parser, 0);
- if (temp == NULL) {
+ parser->node = node;
+
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
+ if (ret != NJS_OK) {
return NJS_ERROR;
}
- temp->left = node;
- temp->u.value.data.u.lambda = function->u.lambda;
-
- node->left = (njs_parser_node_t *) function;
-
- parser->node = temp;
-
njs_parser_next(parser, njs_parser_function_parse);
- return njs_parser_after(parser, current, temp, 1,
+ return njs_parser_after(parser, current, node, 1,
njs_parser_function_declaration_after);
}
njs_parser_function_declaration_after(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
- njs_function_t *function;
+ njs_int_t ret;
+ uintptr_t unique_id;
- parser->node = parser->target->left;
+ unique_id = (uintptr_t) parser->node->left;
- function = (njs_function_t *) parser->node->left;
+ parser->node->left = NULL;
- function->args_count = function->u.lambda->nargs
- - function->u.lambda->rest_parameters;
+ njs_value_null_set(&parser->node->u.value);
- parser->node->right = parser->target->right;
- parser->node->left = NULL;
+ ret = njs_parser_variable_reference(parser, parser->scope, parser->node,
+ unique_id, NJS_DECLARATION);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
return njs_parser_stack_pop(parser);
}
njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- njs_int_t ret;
-
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
- if (ret != NJS_OK) {
- return NJS_ERROR;
- }
-
parser->target = parser->node;
parser->node = NULL;
}
+static const njs_lexer_entry_t njs_parser_anonymous_entry =
+{
+ .name = njs_str("anonymous")
+};
+
+
static njs_int_t
njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
njs_int_t ret;
+ uintptr_t unique_id;
njs_variable_t *var;
- njs_function_t *function;
njs_function_lambda_t *lambda;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_SHIM);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
if (ret != NJS_OK) {
return NJS_ERROR;
}
+ var = NULL;
+
if (njs_lexer_token_is_binding_identifier(token)) {
- var = njs_variable_add(parser, parser->scope, token->unique_id,
- NJS_VARIABLE_SHIM);
- if (var == NULL) {
- return NJS_ERROR;
- }
+ unique_id = token->unique_id;
njs_lexer_consume_token(parser->lexer, 1);
return NJS_ERROR;
}
- function = njs_parser_function_alloc(parser, var);
- if (function == NULL) {
- return NJS_ERROR;
- }
-
- lambda = function->u.lambda;
-
} else {
- /* Anonymous function. */
- lambda = njs_function_lambda_alloc(parser->vm, 1);
- if (lambda == NULL) {
- return NJS_ERROR;
- }
+ unique_id = (uintptr_t) &njs_parser_anonymous_entry;
}
if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) {
njs_lexer_consume_token(parser->lexer, 1);
+ parser->node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+ if (parser->node->left == NULL) {
+ return NJS_ERROR;
+ }
+
+ var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+ unique_id, NJS_VARIABLE_FUNCTION, 1);
+ if (var == NULL) {
+ return NJS_ERROR;
+ }
+
+ var->self = 1;
+
+ ret = njs_parser_variable_reference(parser, parser->scope,
+ parser->node->left, unique_id,
+ NJS_DECLARATION);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ lambda = njs_function_lambda_alloc(parser->vm, 1);
+ if (lambda == NULL) {
+ return NJS_ERROR;
+ }
+
parser->node->u.value.data.u.lambda = lambda;
njs_parser_next(parser, njs_parser_function_parse);
- return njs_parser_after(parser, current, NULL, 1,
+ return njs_parser_after(parser, current, var, 1,
njs_parser_function_expression_after);
}
njs_parser_function_expression_after(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
- njs_parser_scope_end(parser);
+ njs_variable_t *var;
+
+ var = (njs_variable_t *) parser->target;
+
+ var->index = njs_scope_index(var->scope->type, var->scope->items,
+ NJS_LEVEL_LOCAL);
+ var->scope->items++;
+
+ if (var->self) {
+ parser->node->u.value.data.u.lambda->self = var->index;
+ }
return njs_parser_stack_pop(parser);
}
njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- njs_variable_t *arg, *cur_arg;
+ njs_variable_t *arg;
+ njs_rbtree_node_t *rb_node;
+ njs_variable_node_t var_node;
njs_function_lambda_t *lambda;
lambda = parser->target->u.value.data.u.lambda;
default:
/* SingleNameBinding */
if (njs_lexer_token_is_binding_identifier(token)) {
- arg = njs_variable_add(parser, parser->scope,
- token->unique_id, NJS_VARIABLE_VAR);
- if (arg == NULL) {
- return NJS_ERROR;
- }
+ var_node.key = token->unique_id;
- if (arg->index > 0) {
- njs_parser_syntax_error(parser, "Duplicate parameter names");
- return NJS_DONE;
- }
+ rb_node = njs_rbtree_find(&parser->scope->variables,
+ &var_node.node);
+ if (rb_node != NULL) {
+ arg = ((njs_variable_node_t *) rb_node)->variable;
- cur_arg = (njs_variable_t *) parser->node;
-
- if (cur_arg == NULL) {
- arg->index = NJS_SCOPE_ARGUMENTS;
+ if (!arg->self) {
+ njs_parser_syntax_error(parser,
+ "Duplicate parameter names");
+ return NJS_DONE;
+ }
- /* A "this" reservation. */
- arg->index += sizeof(njs_value_t);
+ arg->self = 0;
} else {
- arg->index = cur_arg->index + sizeof(njs_value_t);
+ arg = njs_variable_add(parser, parser->scope,
+ token->unique_id, NJS_VARIABLE_VAR);
+ }
+
+ if (arg == NULL) {
+ return NJS_ERROR;
}
+ arg->argument = 1;
+
lambda->nargs++;
/* Crutch for temporary storage. */
njs_queue_link_t *current)
{
njs_int_t ret;
- njs_variable_t *arg;
- njs_parser_node_t *node;
+ uintptr_t unique_id;
+ njs_variable_t *arg, *var;
+ njs_parser_node_t *node, *name;
njs_function_lambda_t *lambda;
node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
node->token_line = token->line;
parser->node = node;
- lambda = njs_function_lambda_alloc(parser->vm, 0);
- if (lambda == NULL) {
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0);
+ if (ret != NJS_OK) {
return NJS_ERROR;
}
- node->u.value.data.u.lambda = lambda;
+ name = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+ if (name == NULL) {
+ return NJS_ERROR;
+ }
+
+ node->left = name;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
+ unique_id = (uintptr_t) &njs_parser_anonymous_entry;
+
+ var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+ unique_id, NJS_VARIABLE_FUNCTION, 1);
+ if (var == NULL) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_parser_variable_reference(parser, parser->scope, node->left,
+ unique_id, NJS_DECLARATION);
if (ret != NJS_OK) {
return NJS_ERROR;
}
+ node->left->u.reference.variable = var;
+
+ lambda = njs_function_lambda_alloc(parser->vm, 0);
+ if (lambda == NULL) {
+ return NJS_ERROR;
+ }
+
+ node->u.value.data.u.lambda = lambda;
+
parser->scope->arrow_function = 1;
if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) {
njs_parser_arrow_function_args_after);
} else if (njs_lexer_token_is_binding_identifier(token)) {
- arg = njs_variable_add(parser, parser->scope,
- token->unique_id, NJS_VARIABLE_VAR);
+ arg = njs_variable_add(parser, parser->scope, token->unique_id,
+ NJS_VARIABLE_VAR);
if (arg == NULL) {
return NJS_ERROR;
}
- arg->index = NJS_SCOPE_ARGUMENTS;
+ arg->argument = 1;
- /* A "this" reservation. */
- arg->index += sizeof(njs_value_t);
+ var->index = njs_scope_index(parser->scope->type, parser->scope->items,
+ NJS_LEVEL_LOCAL);
+ parser->scope->items++;
- lambda->nargs = 1;
+ lambda->self = var->index;
+ lambda->nargs++;
njs_lexer_consume_token(parser->lexer, 1);
njs_parser_arrow_function_args_after(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
{
+ njs_variable_t *var, **vv;
+
if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) {
return njs_parser_failed(parser);
}
njs_lexer_consume_token(parser->lexer, 1);
+ vv = &parser->target->left->u.reference.variable;
+
+ var = *vv;
+ *vv = NULL;
+
+ var->index = njs_scope_index(var->scope->type, var->scope->items,
+ NJS_LEVEL_LOCAL);
+ var->scope->items++;
+
+ parser->target->u.value.data.u.lambda->self = var->index;
+
njs_parser_next(parser, njs_parser_arrow_function_arrow);
return NJS_OK;
return njs_parser_failed(parser);
}
- expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
if (expr == NULL) {
return NJS_ERROR;
}
return njs_parser_failed(parser);
}
- expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
if (expression == NULL) {
return NJS_ERROR;
}
return njs_parser_failed(parser);
}
- expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION);
if (expression == NULL) {
return NJS_ERROR;
}
expr = parser->node;
expr->u.value.data.u.lambda = lambda;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1);
if (ret != NJS_OK) {
return NJS_ERROR;
}
return NJS_DONE;
}
- name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR);
+ name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR,
+ NULL);
if (name == NULL) {
return NJS_ERROR;
}
parser->target->right = parser->node;
parser->node = parser->target;
- parser->node->hoist = 1;
return njs_parser_stack_pop(parser);
}
+static const njs_lexer_entry_t njs_parser_module_entry =
+{
+ .name = njs_str("module")
+};
+
+
njs_int_t
njs_parser_module_lambda(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
njs_int_t ret;
+ uintptr_t unique_id;
+ njs_variable_t *var;
njs_parser_node_t *node, *parent;
njs_function_lambda_t *lambda;
return NJS_ERROR;
}
+ ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME);
+ if (node->left == NULL) {
+ return NJS_ERROR;
+ }
+
+ unique_id = (uintptr_t) &njs_parser_module_entry;
+
+ var = njs_variable_scope_add(parser, parser->scope, parser->scope,
+ unique_id, NJS_VARIABLE_FUNCTION, 1);
+ if (var == NULL) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_parser_variable_reference(parser, parser->scope, node->left,
+ unique_id, NJS_DECLARATION);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ node->left->u.reference.variable = var;
+
lambda = njs_function_lambda_alloc(parser->vm, 1);
if (lambda == NULL) {
return NJS_ERROR;
parser->node = node;
- ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION);
- if (ret != NJS_OK) {
- return NJS_ERROR;
- }
-
parser->scope->module = 1;
parent = parser->node;
njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
- njs_int_t ret;
+ njs_int_t ret;
+ njs_variable_t *var, **vv;
ret = njs_parser_export_sink(parser);
if (ret != NJS_OK) {
parser->node = parser->target;
+ vv = &parser->target->left->u.reference.variable;
+
+ var = *vv;
+ *vv = NULL;
+
+ var->index = njs_scope_index(var->scope->type, var->scope->items,
+ NJS_LEVEL_LOCAL);
+ var->scope->items++;
+
+ parser->node->u.value.data.u.lambda->self = var->index;
+
njs_parser_scope_end(parser);
return njs_parser_stack_pop(parser);
static njs_parser_node_t *
njs_parser_variable_node(njs_parser_t *parser, uintptr_t unique_id,
- njs_variable_type_t type)
+ njs_variable_type_t type, njs_variable_t **retvar)
{
njs_int_t ret;
njs_variable_t *var;
return NULL;
}
- if (njs_is_null(&var->value)) {
-
- switch (type) {
-
- case NJS_VARIABLE_VAR:
- njs_set_undefined(&var->value);
- break;
-
- default:
- break;
- }
+ if (retvar != NULL) {
+ *retvar = var;
}
node = njs_parser_node_new(parser, NJS_TOKEN_NAME);
return NULL;
}
- ret = njs_variable_reference(parser->vm, parser->scope, node, unique_id,
- NJS_DECLARATION);
+ ret = njs_parser_variable_reference(parser, parser->scope, node, unique_id,
+ NJS_DECLARATION);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
static njs_parser_node_t *
njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token)
{
- njs_int_t ret;
- njs_variable_t *var;
- njs_parser_node_t *node;
- njs_parser_scope_t *scope;
+ njs_int_t ret;
+ njs_index_t index;
+ njs_variable_t *var;
+ njs_parser_node_t *node;
+ njs_parser_scope_t *scope;
+ const njs_lexer_keyword_entry_t *keyword;
+
+ static const njs_str_t njs_undefined_str = njs_str("undefined");
node = njs_parser_node_new(parser, token->type);
if (njs_slow_path(node == NULL)) {
case NJS_TOKEN_NULL:
njs_thread_log_debug("JS: null");
-
- node->u.value = njs_value_null;
break;
case NJS_TOKEN_THIS:
njs_thread_log_debug("JS: this");
- scope = njs_function_scope(parser->scope, 0);
-
- if (scope != NULL) {
- if (scope == njs_function_scope(parser->scope, 1)) {
- node->index = NJS_INDEX_THIS;
+ scope = njs_function_scope(parser->scope);
+ if (njs_slow_path(scope == NULL)) {
+ njs_parser_syntax_error(parser,
+ "function or global scope not found");
+ return NULL;
+ }
- } else {
- node->token_type = NJS_TOKEN_NON_LOCAL_THIS;
- node->token_line = token->line;
+ if (parser->vm->options.module) {
+ keyword = njs_lexer_keyword(njs_undefined_str.start,
+ njs_undefined_str.length);
+ if (njs_slow_path(keyword == NULL)) {
+ return NULL;
+ }
- ret = njs_variable_reference(parser->vm, scope, node,
- token->unique_id, NJS_REFERENCE);
- if (njs_slow_path(ret != NJS_OK)) {
- return NULL;
- }
+ token->unique_id = (uintptr_t) keyword->value;
- var = njs_variable_add(parser, scope, token->unique_id,
- NJS_VARIABLE_VAR);
- if (njs_slow_path(var == NULL)) {
- return NULL;
- }
+ } else if (!scope->arrow_function) {
+ index = njs_scope_index(scope->type, 0, NJS_LEVEL_LOCAL);
- var->this_object = 1;
+ var = njs_variable_scope_add(parser, scope, scope, token->unique_id,
+ NJS_VARIABLE_VAR, index);
+ if (njs_slow_path(var == NULL)) {
+ return NULL;
}
-
- break;
}
- node->token_type = NJS_TOKEN_GLOBAL_OBJECT;
+ node->token_type = NJS_TOKEN_THIS;
+ node->token_line = token->line;
+
+ ret = njs_parser_variable_reference(parser, parser->scope, node,
+ token->unique_id, NJS_REFERENCE);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NULL;
+ }
break;
case NJS_TOKEN_ARGUMENTS:
njs_thread_log_debug("JS: arguments");
- scope = njs_function_scope(parser->scope, 0);
+ scope = njs_function_scope(parser->scope);
- if (scope == NULL) {
+ while (scope->arrow_function) {
+ scope = njs_function_scope(scope->parent);
+ }
+
+ if (scope == NULL || scope->type == NJS_SCOPE_GLOBAL) {
njs_parser_syntax_error(parser, "\"%V\" object in global scope",
&token->text);
return NULL;
node->token_line = token->line;
- ret = njs_variable_reference(parser->vm, scope, node, token->unique_id,
- NJS_REFERENCE);
+ ret = njs_parser_variable_reference(parser, parser->scope, node,
+ token->unique_id, NJS_REFERENCE);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
node->token_line = token->line;
- ret = njs_variable_reference(parser->vm, parser->scope, node,
- token->unique_id, NJS_REFERENCE);
+ ret = njs_parser_variable_reference(parser, parser->scope, node,
+ token->unique_id, NJS_REFERENCE);
if (njs_slow_path(ret != NJS_OK)) {
return NULL;
}
}
+njs_int_t
+njs_parser_variable_reference(njs_parser_t *parser, njs_parser_scope_t *scope,
+ njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type)
+{
+ njs_rbtree_node_t *rb_node;
+ njs_variable_reference_t *vr;
+ njs_parser_rbtree_node_t parse_node, *rb_parse_node;
+
+ vr = &node->u.reference;
+
+ vr->unique_id = unique_id;
+ vr->type = type;
+
+ parse_node.key = unique_id;
+
+ rb_node = njs_rbtree_find(&scope->references, &parse_node.node);
+ if (rb_node != NULL) {
+ return NJS_OK;
+ }
+
+ rb_parse_node = njs_mp_alloc(parser->vm->mem_pool,
+ sizeof(njs_parser_rbtree_node_t));
+ if (njs_slow_path(rb_parse_node == NULL)) {
+ return NJS_ERROR;
+ }
+
+ rb_parse_node->key = unique_id;
+ rb_parse_node->index = NJS_INDEX_NONE;
+
+ njs_rbtree_insert(&scope->references, &rb_parse_node->node);
+
+ return NJS_OK;
+}
+
+
njs_token_type_t
njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
njs_str_t *name, njs_token_type_t type)
njs_token_serialize(NJS_TOKEN_FINALLY);
njs_token_serialize(NJS_TOKEN_THROW);
njs_token_serialize(NJS_TOKEN_THIS);
- njs_token_serialize(NJS_TOKEN_GLOBAL_OBJECT);
- njs_token_serialize(NJS_TOKEN_NON_LOCAL_THIS);
njs_token_serialize(NJS_TOKEN_ARGUMENTS);
njs_token_serialize(NJS_TOKEN_EVAL);
njs_token_serialize(NJS_TOKEN_IMPORT);
struct njs_parser_scope_s {
njs_parser_node_t *top;
- njs_queue_link_t link;
- njs_queue_t nested;
-
njs_parser_scope_t *parent;
njs_rbtree_t variables;
njs_rbtree_t labels;
njs_rbtree_t references;
-#define NJS_SCOPE_INDEX_LOCAL 0
-#define NJS_SCOPE_INDEX_CLOSURE 1
+ njs_arr_t *closures;
+ njs_arr_t *declarations;
- njs_arr_t *values[2]; /* Array of njs_value_t. */
- njs_index_t next_index[2];
+ uint32_t temp;
+ uint32_t items;
njs_str_t cwd;
njs_str_t file;
njs_scope_t type:8;
- uint8_t nesting; /* 4 bits */
- uint8_t argument_closures;
uint8_t module;
uint8_t arrow_function;
};
njs_token_type_t token_type:16;
uint8_t ctor:1;
uint8_t temporary; /* 1 bit */
- uint8_t hoist; /* 1 bit */
uint32_t token_line;
union {
typedef struct {
NJS_RBTREE_NODE (node);
uintptr_t key;
- njs_parser_node_t *parser_node;
+ njs_index_t index;
} njs_parser_rbtree_node_t;
intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1,
njs_rbtree_node_t *node2);
-njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser,
- njs_rbtree_t *prev_vars);
+njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser);
njs_int_t njs_parser_module_lambda(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
njs_variable_t *njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node);
njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node);
njs_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
+njs_int_t njs_parser_variable_reference(njs_parser_t *parser,
+ njs_parser_scope_t *scope, njs_parser_node_t *node, uintptr_t unique_id,
+ njs_reference_type_t type);
njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
njs_str_t *name, njs_token_type_t type);
njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token,
|| (node)->token_type == NJS_TOKEN_PROPERTY)
-#define njs_scope_accumulative(vm, scope) \
- ((vm)->options.accumulative && (scope)->type == NJS_SCOPE_GLOBAL)
-
-
#define njs_parser_syntax_error(parser, fmt, ...) \
njs_parser_lexer_error(parser, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, \
##__VA_ARGS__)
njs_inline njs_parser_scope_t *
-njs_function_scope(njs_parser_scope_t *scope, njs_bool_t any)
+njs_function_scope(njs_parser_scope_t *scope)
{
- while (scope->type != NJS_SCOPE_GLOBAL) {
- if (scope->type == NJS_SCOPE_FUNCTION
- && (any || !scope->arrow_function))
+ do {
+ if (scope->type == NJS_SCOPE_GLOBAL
+ || scope->type == NJS_SCOPE_FUNCTION)
{
return scope;
}
scope = scope->parent;
- }
+
+ } while (scope != NULL);
return NULL;
}
--- /dev/null
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+static njs_value_t *njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src,
+ njs_uint_t runtime, njs_index_t **index);
+
+
+njs_index_t
+njs_scope_temp_index(njs_parser_scope_t *scope)
+{
+ scope = njs_function_scope(scope);
+ if (njs_slow_path(scope == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ return njs_scope_index(NJS_SCOPE_GLOBAL, scope->temp++, NJS_LEVEL_TEMP);
+}
+
+
+njs_value_t *
+njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index)
+{
+ njs_value_t *value;
+
+ value = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+ if (njs_slow_path(value == NULL)) {
+ return NULL;
+ }
+
+ njs_scope_value_set(vm, index, value);
+
+ return value;
+}
+
+
+njs_value_t **
+njs_scope_make(njs_vm_t *vm, uint32_t count)
+{
+ size_t size;
+ njs_value_t **refs, *values;
+
+ size = (count * sizeof(njs_value_t *)) + (count * sizeof(njs_value_t));
+
+ refs = njs_mp_alloc(vm->mem_pool, size);
+ if (njs_slow_path(refs == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ values = (njs_value_t *) ((u_char *) refs
+ + (count * sizeof(njs_value_t *)));
+
+ while (count != 0) {
+ count--;
+
+ refs[count] = &values[count];
+
+ njs_set_invalid(refs[count]);
+ }
+
+ return refs;
+}
+
+
+njs_index_t
+njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime)
+{
+ njs_index_t index, *retval;
+ njs_value_t **values, *value;
+
+ value = njs_scope_value_index(vm, src, runtime, &retval);
+ if (njs_slow_path(value == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ if (*retval != NJS_INDEX_ERROR) {
+ return *retval;
+ }
+
+ if (vm->scope_absolute == NULL) {
+ vm->scope_absolute = njs_arr_create(vm->mem_pool, 8,
+ sizeof(njs_value_t *));
+ if (njs_slow_path(vm->scope_absolute == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+ }
+
+ index = vm->scope_absolute->items;
+
+ values = njs_arr_add(vm->scope_absolute);
+ if (njs_slow_path(values == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
+
+ *values = value;
+
+ vm->levels[NJS_LEVEL_STATIC] = vm->scope_absolute->start;
+
+ *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_LEVEL_STATIC);
+
+ return *retval;
+}
+
+
+static njs_int_t
+njs_scope_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
+{
+ njs_str_t string;
+ njs_value_t *value;
+
+ value = data;
+
+ if (njs_is_string(value)) {
+ njs_string_get(value, &string);
+
+ } else {
+ string.start = (u_char *) value;
+ string.length = sizeof(njs_value_t);
+ }
+
+ if (lhq->key.length == string.length
+ && memcmp(lhq->key.start, string.start, string.length) == 0)
+ {
+ return NJS_OK;
+ }
+
+ return NJS_DECLINED;
+}
+
+
+static const njs_lvlhsh_proto_t njs_values_hash_proto
+ njs_aligned(64) =
+{
+ NJS_LVLHSH_DEFAULT,
+ njs_scope_values_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
+/*
+ * Constant values such as njs_value_true are copied to values_hash during
+ * code generation when they are used as operands to guarantee aligned value.
+ */
+
+static njs_value_t *
+njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime,
+ njs_index_t **index)
+{
+ u_char *start;
+ uint32_t value_size, size, length;
+ njs_int_t ret;
+ njs_str_t str;
+ njs_bool_t long_string;
+ njs_value_t *value;
+ njs_string_t *string;
+ njs_lvlhsh_t *values_hash;
+ njs_lvlhsh_query_t lhq;
+
+ long_string = 0;
+ value_size = sizeof(njs_value_t);
+
+ if (njs_is_string(src)) {
+ njs_string_get(src, &str);
+
+ size = (uint32_t) str.length;
+ start = str.start;
+
+ if (src->short_string.size == NJS_STRING_LONG) {
+ long_string = 1;
+ }
+
+ } else {
+ size = value_size;
+ start = (u_char *) src;
+ }
+
+ lhq.key_hash = njs_djb_hash(start, size);
+ lhq.key.length = size;
+ lhq.key.start = start;
+ lhq.proto = &njs_values_hash_proto;
+
+ if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
+ value = lhq.value;
+
+ *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+
+ } else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
+ value = lhq.value;
+
+ *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+
+ } else {
+ if (long_string) {
+ length = src->long_string.data->length;
+
+ if (size != length && length > NJS_STRING_MAP_STRIDE) {
+ size = njs_string_map_offset(size)
+ + njs_string_map_size(length);
+ }
+
+ value_size += sizeof(njs_string_t) + size;
+ }
+
+ value_size += sizeof(njs_index_t);
+
+ value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size);
+ if (njs_slow_path(value == NULL)) {
+ return NULL;
+ }
+
+ *value = *src;
+
+ if (long_string) {
+ string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t)
+ + sizeof(njs_index_t));
+
+ value->long_string.data = string;
+
+ string->start = (u_char *) string + sizeof(njs_string_t);
+ string->length = src->long_string.data->length;
+ string->retain = 0xffff;
+
+ memcpy(string->start, start, size);
+ }
+
+ *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t));
+ **index = NJS_INDEX_ERROR;
+
+ lhq.replace = 0;
+ lhq.value = value;
+ lhq.pool = vm->mem_pool;
+
+ values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
+
+ ret = njs_lvlhsh_insert(values_hash, &lhq);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NULL;
+ }
+ }
+
+ if (start != (u_char *) src) {
+ /*
+ * The source node value must be updated with the shared value
+ * allocated from the permanent memory pool because the node
+ * value can be used as a variable initial value.
+ */
+ *(njs_value_t *) src = *value;
+ }
+
+ return value;
+}
--- /dev/null
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_SCOPE_H_INCLUDED_
+#define _NJS_SCOPE_H_INCLUDED_
+
+
+#define NJS_SCOPE_TYPE_SIZE 4
+#define NJS_SCOPE_VALUE_OFFSET (NJS_SCOPE_TYPE_SIZE + 1)
+#define NJS_SCOPE_VALUE_MAX ((1 << (32 - NJS_SCOPE_VALUE_OFFSET)) - 1)
+#define NJS_SCOPE_TYPE_MASK ((NJS_SCOPE_VALUE_MAX) << NJS_SCOPE_TYPE_SIZE)
+
+#define NJS_INDEX_NONE ((njs_index_t) 0)
+#define NJS_INDEX_ERROR ((njs_index_t) -1)
+
+
+njs_index_t njs_scope_temp_index(njs_parser_scope_t *scope);
+njs_value_t *njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index);
+njs_value_t **njs_scope_make(njs_vm_t *vm, uint32_t count);
+njs_index_t njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src,
+ njs_uint_t runtime);
+
+
+njs_inline njs_index_t
+njs_scope_index(njs_scope_t scope, njs_index_t index, njs_level_type_t type)
+{
+ if (index > NJS_SCOPE_VALUE_MAX || type >= NJS_LEVEL_MAX
+ || (scope != NJS_SCOPE_GLOBAL && scope != NJS_SCOPE_FUNCTION))
+ {
+ return NJS_INDEX_ERROR;
+ }
+
+ if (scope == NJS_SCOPE_GLOBAL && type == NJS_LEVEL_LOCAL) {
+ type = NJS_LEVEL_GLOBAL;
+ }
+
+ return (index << NJS_SCOPE_VALUE_OFFSET) | type;
+}
+
+
+njs_inline njs_level_type_t
+njs_scope_index_type(njs_index_t index)
+{
+ return (njs_level_type_t) (index & ~NJS_SCOPE_TYPE_MASK);
+}
+
+
+njs_inline uint32_t
+njs_scope_index_value(njs_index_t index)
+{
+ return (uint32_t) (index >> NJS_SCOPE_VALUE_OFFSET);
+}
+
+
+njs_inline njs_value_t *
+njs_scope_value(njs_vm_t *vm, njs_index_t index)
+{
+ return vm->levels[njs_scope_index_type(index)]
+ [njs_scope_index_value(index)];
+}
+
+
+njs_inline njs_value_t *
+njs_scope_valid_value(njs_vm_t *vm, njs_index_t index)
+{
+ njs_value_t *value;
+
+ value = njs_scope_value(vm, index);
+
+ if (!njs_is_valid(value)) {
+ njs_set_undefined(value);
+ }
+
+ return value;
+}
+
+
+njs_inline void
+njs_scope_value_set(njs_vm_t *vm, njs_index_t index, njs_value_t *value)
+{
+ vm->levels[njs_scope_index_type(index)]
+ [njs_scope_index_value(index)] = value;
+}
+
+
+njs_inline njs_value_t *
+njs_scope_value_clone(njs_vm_t *vm, njs_index_t index, njs_value_t *value)
+{
+ njs_value_t *newval;
+
+ newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
+ if (njs_slow_path(newval == NULL)) {
+ njs_memory_error(vm);
+ return NULL;
+ }
+
+ *newval = *value;
+
+ njs_scope_value_set(vm, index, newval);
+
+ return newval;
+}
+
+
+njs_inline njs_index_t
+njs_scope_undefined_index(njs_vm_t *vm, njs_uint_t runtime)
+{
+ return njs_scope_global_index(vm, &njs_value_undefined, runtime);
+}
+
+
+njs_inline njs_index_t
+njs_scope_global_this_index()
+{
+ return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_LEVEL_LOCAL);
+}
+
+
+#endif /* _NJS_PARSER_H_INCLUDED_ */
vm_options.file.length = njs_strlen(opts.file);
vm_options.init = 1;
- vm_options.accumulative = opts.interactive;
+ vm_options.interactive = opts.interactive;
vm_options.disassemble = opts.disassemble;
vm_options.backtrace = 1;
vm_options.quiet = opts.quiet;
return;
}
- if (vm->options.accumulative) {
+ if (vm->options.interactive) {
njs_print(out.start, out.length);
njs_print("\n", 1);
}
}
-static njs_int_t
-njs_values_hash_test(njs_lvlhsh_query_t *lhq, void *data)
-{
- njs_str_t string;
- njs_value_t *value;
-
- value = data;
-
- if (njs_is_string(value)) {
- njs_string_get(value, &string);
-
- } else {
- string.start = (u_char *) value;
- string.length = sizeof(njs_value_t);
- }
-
- if (lhq->key.length == string.length
- && memcmp(lhq->key.start, string.start, string.length) == 0)
- {
- return NJS_OK;
- }
-
- return NJS_DECLINED;
-}
-
-
-static const njs_lvlhsh_proto_t njs_values_hash_proto
- njs_aligned(64) =
-{
- NJS_LVLHSH_DEFAULT,
- njs_values_hash_test,
- njs_lvlhsh_alloc,
- njs_lvlhsh_free,
-};
-
-
-/*
- * Constant values such as njs_value_true are copied to values_hash during
- * code generation when they are used as operands to guarantee aligned value.
- */
-
-njs_index_t
-njs_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime)
-{
- u_char *start;
- uint32_t value_size, size, length;
- njs_int_t ret;
- njs_str_t str;
- njs_bool_t long_string;
- njs_value_t *value;
- njs_string_t *string;
- njs_lvlhsh_t *values_hash;
- njs_lvlhsh_query_t lhq;
-
- long_string = 0;
- value_size = sizeof(njs_value_t);
-
- if (njs_is_string(src)) {
- njs_string_get(src, &str);
-
- size = (uint32_t) str.length;
- start = str.start;
-
- if (src->short_string.size == NJS_STRING_LONG) {
- long_string = 1;
- }
-
- } else {
- size = value_size;
- start = (u_char *) src;
- }
-
- lhq.key_hash = njs_djb_hash(start, size);
- lhq.key.length = size;
- lhq.key.start = start;
- lhq.proto = &njs_values_hash_proto;
-
- if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) {
- value = lhq.value;
-
- } else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) {
- value = lhq.value;
-
- } else {
- if (long_string) {
- length = src->long_string.data->length;
-
- if (size != length && length > NJS_STRING_MAP_STRIDE) {
- size = njs_string_map_offset(size)
- + njs_string_map_size(length);
- }
-
- value_size += sizeof(njs_string_t) + size;
- }
-
- value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size);
- if (njs_slow_path(value == NULL)) {
- return NJS_INDEX_NONE;
- }
-
- *value = *src;
-
- if (long_string) {
- string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t));
- value->long_string.data = string;
-
- string->start = (u_char *) string + sizeof(njs_string_t);
- string->length = src->long_string.data->length;
- string->retain = 0xffff;
-
- memcpy(string->start, start, size);
- }
-
- lhq.replace = 0;
- lhq.value = value;
- lhq.pool = vm->mem_pool;
-
- values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash;
-
- ret = njs_lvlhsh_insert(values_hash, &lhq);
-
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_INDEX_NONE;
- }
- }
-
- if (start != (u_char *) src) {
- /*
- * The source node value must be updated with the shared value
- * allocated from the permanent memory pool because the node
- * value can be used as a variable initial value.
- */
- *(njs_value_t *) src = *value;
- }
-
- return (njs_index_t) value;
-}
-
-
const njs_object_type_init_t njs_string_type_init = {
.constructor = njs_native_ctor(njs_string_constructor, 1, 0),
.constructor_props = &njs_string_constructor_init,
njs_int_t njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t component);
-njs_index_t njs_value_index(njs_vm_t *vm, const njs_value_t *src,
- njs_uint_t runtime);
-
njs_int_t njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t unused);
njs_int_t njs_string_get_substitution(njs_vm_t *vm, njs_value_t *matched,
};
-typedef struct {
- union {
- uint32_t count;
- njs_value_t values;
- } u;
-
- njs_value_t values[1];
-} njs_closure_t;
-
-
struct njs_function_s {
njs_object_t object;
uint8_t args_count:4;
- /*
- * If "closure" is true njs_closure_t[] is available right after the
- * njs_function_t and njs_function_closures() may be used to access it.
- */
-
-#define njs_function_closures(function) \
- ((njs_closure_t **) ((u_char *) function + sizeof(njs_function_t)))
-
- uint8_t closure:1;
+ uint8_t closure_copied:1;
uint8_t native:1;
uint8_t ctor:1;
uint8_t global_this:1;
+ uint8_t global:1;
uint8_t magic8;
#include <njs_main.h>
-static njs_variable_t *njs_variable_scope_add(njs_parser_t *parser,
- njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
-static njs_int_t njs_variable_reference_resolve(njs_vm_t *vm,
- njs_variable_reference_t *vr, njs_parser_scope_t *node_scope);
+static njs_value_t **njs_variable_scope_function_add(njs_parser_t *parser,
+ njs_parser_scope_t *scope);
+static njs_parser_scope_t *njs_variable_scope_find(njs_parser_t *parser,
+ njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id,
njs_variable_type_t type);
njs_variable_add(njs_parser_t *parser, njs_parser_scope_t *scope,
uintptr_t unique_id, njs_variable_type_t type)
{
- njs_variable_t *var;
+ njs_parser_scope_t *root;
- var = njs_variable_scope_add(parser, scope, unique_id, type);
- if (njs_slow_path(var == NULL)) {
+ root = njs_variable_scope_find(parser, scope, unique_id, type);
+ if (njs_slow_path(root == NULL)) {
+ njs_parser_ref_error(parser, "scope not found");
return NULL;
}
- if (type == NJS_VARIABLE_VAR && scope->type == NJS_SCOPE_BLOCK) {
- /* A "var" declaration is stored in function or global scope. */
- do {
- scope = scope->parent;
+ return njs_variable_scope_add(parser, root, scope, unique_id, type,
+ NJS_INDEX_NONE);
+}
- var = njs_variable_scope_add(parser, scope, unique_id, type);
- if (njs_slow_path(var == NULL)) {
- return NULL;
- }
- } while (scope->type == NJS_SCOPE_BLOCK);
+njs_variable_t *
+njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope,
+ uintptr_t unique_id, njs_variable_type_t type)
+{
+ njs_value_t **declr;
+ njs_variable_t *var;
+ njs_parser_scope_t *root;
+ njs_function_lambda_t *lambda;
+
+ root = njs_variable_scope_find(parser, scope, unique_id, type);
+ if (njs_slow_path(root == NULL)) {
+ njs_parser_ref_error(parser, "scope not found");
+ return NULL;
+ }
+
+ var = njs_variable_scope_add(parser, root, scope, unique_id, type,
+ NJS_INDEX_ERROR);
+ if (njs_slow_path(var == NULL)) {
+ return NULL;
}
- if (type == NJS_VARIABLE_FUNCTION) {
- var->type = type;
+ if (var->index == NJS_INDEX_ERROR || !var->function) {
+ root = njs_function_scope(scope);
+ if (njs_slow_path(scope == NULL)) {
+ return NULL;
+ }
+
+ lambda = njs_function_lambda_alloc(parser->vm, 1);
+ if (lambda == NULL) {
+ return NULL;
+ }
+
+ var->value.data.u.lambda = lambda;
+
+ declr = njs_variable_scope_function_add(parser, root);
+ if (njs_slow_path(declr == NULL)) {
+ return NULL;
+ }
+
+ *declr = &var->value;
+
+ var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL);
+ root->items++;
}
+ var->type = NJS_VARIABLE_FUNCTION;
+ var->function = 1;
+
return var;
}
+static njs_value_t **
+njs_variable_scope_function_add(njs_parser_t *parser, njs_parser_scope_t *scope)
+{
+ if (scope->declarations == NULL) {
+ scope->declarations = njs_arr_create(parser->vm->mem_pool, 1,
+ sizeof(njs_value_t *));
+ if (njs_slow_path(scope->declarations == NULL)) {
+ return NULL;
+ }
+ }
+
+ return njs_arr_add(scope->declarations);
+}
+
+
+
njs_int_t
njs_variables_copy(njs_vm_t *vm, njs_rbtree_t *variables,
njs_rbtree_t *prev_variables)
}
-static njs_variable_t *
-njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
- uintptr_t unique_id, njs_variable_type_t type)
+static njs_parser_scope_t *
+njs_variable_scope(njs_parser_scope_t *scope, uintptr_t unique_id,
+ njs_variable_t **retvar, njs_variable_type_t type)
+{
+ njs_variable_t *var;
+ njs_rbtree_node_t *node;
+ njs_variable_node_t var_node;
+
+ *retvar = NULL;
+
+ var_node.key = unique_id;
+
+ do {
+ node = njs_rbtree_find(&scope->variables, &var_node.node);
+
+ if (node != NULL) {
+ var = ((njs_variable_node_t *) node)->variable;
+
+ if (var->type != NJS_VARIABLE_CATCH || type != NJS_VARIABLE_VAR) {
+ *retvar = var;
+ return scope;
+ }
+ }
+
+ if (scope->type == NJS_SCOPE_GLOBAL
+ || scope->type == NJS_SCOPE_FUNCTION)
+ {
+ return scope;
+ }
+
+ scope = scope->parent;
+
+ } while (scope != NULL);
+
+ return NULL;
+}
+
+
+static njs_parser_scope_t *
+njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope,
+ uintptr_t unique_id, njs_variable_type_t type)
{
+ njs_bool_t module;
njs_variable_t *var;
- njs_rbtree_node_t *node;
- njs_variable_node_t var_node, *var_node_new;
+ njs_parser_scope_t *root;
const njs_lexer_entry_t *entry;
- var_node.key = unique_id;
+ if (type != NJS_VARIABLE_VAR && type != NJS_VARIABLE_FUNCTION) {
+ return scope;
+ }
- node = njs_rbtree_find(&scope->variables, &var_node.node);
+ root = njs_variable_scope(scope, unique_id, &var, type);
+ if (njs_slow_path(root == NULL)) {
+ return NULL;
+ }
- if (node != NULL) {
- var = ((njs_variable_node_t *) node)->variable;
+ if (type == NJS_VARIABLE_FUNCTION) {
+ root = scope;
+ }
- if (scope->module || scope->type == NJS_SCOPE_BLOCK) {
+ if (var == NULL) {
+ return root;
+ }
- if (type == NJS_VARIABLE_FUNCTION
- || var->type == NJS_VARIABLE_FUNCTION)
- {
- goto fail;
+ if (var->original->type == NJS_SCOPE_BLOCK) {
+ if (type == NJS_VARIABLE_FUNCTION
+ || var->type == NJS_VARIABLE_FUNCTION)
+ {
+ if (var->original == root) {
+ goto failed;
}
}
+ }
- if (scope->type == NJS_SCOPE_GLOBAL) {
+ if (type != NJS_VARIABLE_FUNCTION
+ && var->type != NJS_VARIABLE_FUNCTION)
+ {
+ return var->scope;
+ }
- if (parser->vm->options.module) {
- if (type == NJS_VARIABLE_FUNCTION
- || var->type == NJS_VARIABLE_FUNCTION)
- {
- goto fail;
- }
- }
+ if (root != scope) {
+ return root;
+ }
+
+ module = parser->vm->options.module || scope->module;
+
+ if (module) {
+ if (type == NJS_VARIABLE_FUNCTION
+ || var->type == NJS_VARIABLE_FUNCTION)
+ {
+ goto failed;
}
+ }
+
+ return root;
+
+failed:
+
+ entry = njs_lexer_entry(unique_id);
+
+ njs_parser_syntax_error(parser, "\"%V\" has already been declared",
+ &entry->name);
+ return NULL;
+}
+
+
+njs_variable_t *
+njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope,
+ njs_parser_scope_t *original, uintptr_t unique_id,
+ njs_variable_type_t type, njs_index_t index)
+{
+ njs_variable_t *var;
+ njs_rbtree_node_t *node;
+ njs_parser_scope_t *root;
+ njs_variable_node_t var_node, *var_node_new;
+
+ var_node.key = unique_id;
+
+ node = njs_rbtree_find(&scope->variables, &var_node.node);
- return var;
+ if (node != NULL) {
+ return ((njs_variable_node_t *) node)->variable;
}
var = njs_variable_alloc(parser->vm, unique_id, type);
goto memory_error;
}
+ var->scope = scope;
+ var->index = index;
+ var->original = original;
+
+ if (index == NJS_INDEX_NONE) {
+ root = njs_function_scope(scope);
+ if (njs_slow_path(scope == NULL)) {
+ return NULL;
+ }
+
+ var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL);
+ root->items++;
+ }
+
var_node_new = njs_variable_node_alloc(parser->vm, var, unique_id);
if (njs_slow_path(var_node_new == NULL)) {
goto memory_error;
njs_memory_error(parser->vm);
return NULL;
-
-fail:
-
- entry = njs_lexer_entry(unique_id);
-
- njs_parser_syntax_error(parser, "\"%V\" has already been declared",
- &entry->name);
- return NULL;
}
}
-njs_int_t
-njs_variable_reference(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type)
+static njs_bool_t
+njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope)
{
- njs_variable_reference_t *vr;
- njs_parser_rbtree_node_t *rb_node;
+ if (root == scope) {
+ return 0;
+ }
- vr = &node->u.reference;
+ do {
+ if (root->type == NJS_SCOPE_FUNCTION) {
+ return 1;
+ }
- vr->unique_id = unique_id;
- vr->type = type;
+ root = root->parent;
- rb_node = njs_mp_alloc(vm->mem_pool, sizeof(njs_parser_rbtree_node_t));
- if (njs_slow_path(rb_node == NULL)) {
- return NJS_ERROR;
- }
+ } while (root != scope);
- rb_node->key = unique_id;
- rb_node->parser_node = node;
+ return 0;
+}
- njs_rbtree_insert(&scope->references, &rb_node->node);
- return NJS_OK;
+njs_variable_t *
+njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node)
+{
+ njs_rbtree_node_t *rb_node;
+ njs_parser_scope_t *scope;
+ njs_variable_node_t var_node;
+ njs_variable_reference_t *ref;
+
+ ref = &node->u.reference;
+ scope = node->scope;
+
+ var_node.key = ref->unique_id;
+
+ do {
+ rb_node = njs_rbtree_find(&scope->variables, &var_node.node);
+
+ if (rb_node != NULL) {
+ return ((njs_variable_node_t *) rb_node)->variable;
+ }
+
+ scope = scope->parent;
+
+ } while (scope != NULL);
+
+ return NULL;
}
-static njs_int_t
-njs_variables_scope_resolve(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_bool_t closure)
+static njs_index_t
+njs_variable_closure(njs_vm_t *vm, njs_variable_t *var,
+ njs_parser_scope_t *scope)
{
- njs_int_t ret;
- njs_queue_t *nested;
- njs_queue_link_t *lnk;
+ njs_index_t index, prev_index, *idx;
+ njs_level_type_t type;
njs_rbtree_node_t *rb_node;
- njs_parser_node_t *node;
- njs_parser_rbtree_node_t *parser_rb_node;
- njs_variable_reference_t *vr;
+ njs_parser_scope_t **p, *root;
+ njs_parser_rbtree_node_t *parse_node, ref_node;
+#define NJS_VAR_MAX_DEPTH 32
+ njs_parser_scope_t *list[NJS_VAR_MAX_DEPTH];
- nested = &scope->nested;
+ ref_node.key = var->unique_id;
- for (lnk = njs_queue_first(nested);
- lnk != njs_queue_tail(nested);
- lnk = njs_queue_next(lnk))
- {
- scope = njs_queue_link_data(lnk, njs_parser_scope_t, link);
+ p = list;
- ret = njs_variables_scope_resolve(vm, scope, closure);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
+ do {
+ if (njs_slow_path(p == &list[NJS_VAR_MAX_DEPTH - 1])) {
+ njs_error(vm, "maximum depth of nested functions is reached");
+ return NJS_INDEX_ERROR;
}
- rb_node = njs_rbtree_min(&scope->references);
+ if (scope->type == NJS_SCOPE_FUNCTION) {
+ *p++ = scope;
+ }
- while (njs_rbtree_is_there_successor(&scope->references, rb_node)) {
- parser_rb_node = (njs_parser_rbtree_node_t *) rb_node;
- node = parser_rb_node->parser_node;
+ scope = scope->parent;
- if (node == NULL) {
- break;
- }
+ } while (scope != var->scope && scope->type != NJS_SCOPE_GLOBAL);
- vr = &node->u.reference;
+ prev_index = var->index;
- if (closure) {
- ret = njs_variable_reference_resolve(vm, vr, node->scope);
- if (njs_slow_path(ret != NJS_OK)) {
- goto next;
- }
+ while (p != list) {
+ p--;
- if (vr->scope_index == NJS_SCOPE_INDEX_LOCAL) {
- goto next;
- }
- }
+ scope = *p;
- (void) njs_variable_resolve(vm, node);
+ rb_node = njs_rbtree_find(&scope->references, &ref_node.node);
- next:
+ parse_node = ((njs_parser_rbtree_node_t *) rb_node);
- rb_node = njs_rbtree_node_successor(&scope->references, rb_node);
- }
- }
+ type = NJS_LEVEL_LOCAL;
- return NJS_OK;
-}
+ if (parse_node != NULL) {
+ type = njs_scope_index_type(parse_node->index);
+ if (p != list && parse_node->index != 0) {
+ prev_index = parse_node->index;
+ continue;
+ }
+ }
-njs_int_t
-njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope)
-{
- njs_int_t ret;
+ root = njs_function_scope(scope);
- /*
- * Calculating proper scope types for variables.
- * A variable is considered to be local variable if it is referenced
- * only in the local scope (reference and definition nestings are the same).
- */
+ if (type != NJS_LEVEL_CLOSURE && root == scope) {
+ /* Create new closure for scope. */
- ret = njs_variables_scope_resolve(vm, scope, 1);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
+ index = njs_scope_index(root->type, root->closures->items,
+ NJS_LEVEL_CLOSURE);
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_INDEX_ERROR;
+ }
- ret = njs_variables_scope_resolve(vm, scope, 0);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
+ idx = njs_arr_add(root->closures);
+ if (njs_slow_path(idx == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
- return NJS_OK;
-}
+ *idx = prev_index;
+ if (parse_node == NULL) {
+ /* Create new reference for closure. */
-njs_index_t
-njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node)
-{
- njs_variable_t *var;
+ parse_node = njs_mp_alloc(vm->mem_pool,
+ sizeof(njs_parser_rbtree_node_t));
+ if (njs_slow_path(parse_node == NULL)) {
+ return NJS_INDEX_ERROR;
+ }
- if (node->index != NJS_INDEX_NONE) {
- return node->index;
- }
+ parse_node->key = var->unique_id;
+
+ njs_rbtree_insert(&scope->references, &parse_node->node);
+ }
- var = njs_variable_resolve(vm, node);
+ parse_node->index = index;
+ }
- if (njs_fast_path(var != NULL)) {
- return var->index;
+ prev_index = parse_node->index;
}
- return NJS_INDEX_NONE;
+ return prev_index;
}
njs_variable_t *
-njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node)
+njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node)
{
- njs_int_t ret;
- njs_uint_t scope_index;
- njs_index_t index;
- njs_variable_t *var;
- njs_variable_reference_t *vr;
+ njs_rbtree_node_t *rb_node;
+ njs_parser_scope_t *scope;
+ njs_parser_rbtree_node_t *parse_node, ref_node;
+ njs_variable_reference_t *ref;
- vr = &node->u.reference;
+ ref = &node->u.reference;
+ scope = node->scope;
- ret = njs_variable_reference_resolve(vm, vr, node->scope);
+ if (ref->variable == NULL) {
+ ref->variable = njs_variable_resolve(vm, node);
+ if (njs_slow_path(ref->variable == NULL)) {
+ ref->not_defined = 1;
- if (njs_slow_path(ret != NJS_OK)) {
- node->u.reference.not_defined = 1;
- return NULL;
+ return NULL;
+ }
}
- scope_index = vr->scope_index;
-
- var = vr->variable;
- index = var->index;
+ ref->closure = njs_variable_closure_test(node->scope, ref->variable->scope);
+ ref->scope = node->scope;
- if (index != NJS_INDEX_NONE) {
+ ref_node.key = ref->unique_id;
- if (scope_index == NJS_SCOPE_INDEX_LOCAL
- || njs_scope_type(index) != NJS_SCOPE_ARGUMENTS)
- {
- node->index = index;
+ rb_node = njs_rbtree_find(&scope->references, &ref_node.node);
+ if (njs_slow_path(rb_node == NULL)) {
+ return NULL;
+ }
- return var;
- }
+ parse_node = ((njs_parser_rbtree_node_t *) rb_node);
- vr->scope->argument_closures++;
- index = (index >> NJS_SCOPE_SHIFT) + 1;
+ if (parse_node->index != NJS_INDEX_NONE) {
+ node->index = parse_node->index;
- if (index > 255 || vr->scope->argument_closures == 0) {
- njs_internal_error(vm, "too many argument closures");
+ return ref->variable;
+ }
- return NULL;
- }
+ if (!ref->closure) {
+ node->index = ref->variable->index;
- var->argument = index;
+ return ref->variable;
}
- index = njs_scope_next_index(vm, vr->scope, scope_index, &var->value);
-
- if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ node->index = njs_variable_closure(vm, ref->variable, scope);
+ if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
return NULL;
}
- var->index = index;
- node->index = index;
-
- return var;
+ return ref->variable;
}
}
-static njs_int_t
-njs_variable_reference_resolve(njs_vm_t *vm, njs_variable_reference_t *vr,
- njs_parser_scope_t *node_scope)
-{
- njs_rbtree_node_t *node;
- njs_parser_scope_t *scope, *previous;
- njs_variable_node_t var_node;
-
- var_node.key = vr->unique_id;
-
- scope = node_scope;
- previous = NULL;
-
- for ( ;; ) {
- node = njs_rbtree_find(&scope->variables, &var_node.node);
-
- if (node != NULL) {
- vr->variable = ((njs_variable_node_t *) node)->variable;
-
- if (scope->type == NJS_SCOPE_BLOCK
- && vr->variable->type == NJS_VARIABLE_VAR)
- {
- scope = scope->parent;
- continue;
- }
-
- if (scope->type == NJS_SCOPE_SHIM) {
- scope = previous;
-
- } else {
- /*
- * Variables declared in a block with "let" or "const"
- * keywords are actually stored in function or global scope.
- */
- while (scope->type == NJS_SCOPE_BLOCK) {
- scope = scope->parent;
- }
- }
-
- vr->scope = scope;
-
- vr->scope_index = NJS_SCOPE_INDEX_LOCAL;
-
- if (vr->scope->type > NJS_SCOPE_GLOBAL
- && node_scope->nesting != vr->scope->nesting)
- {
- vr->scope_index = NJS_SCOPE_INDEX_CLOSURE;
- }
-
- return NJS_OK;
- }
-
- if (scope->parent == NULL) {
- /* A global scope. */
- vr->scope = scope;
-
- return NJS_DECLINED;
- }
-
- previous = scope;
- scope = scope->parent;
- }
-}
-
-
-njs_index_t
-njs_scope_next_index(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_uint_t scope_index, const njs_value_t *default_value)
-{
- njs_arr_t *values;
- njs_index_t index;
- njs_value_t *value;
-
- if (njs_scope_accumulative(vm, scope)) {
- /*
- * When non-clonable VM runs in accumulative mode all
- * global variables should be allocated in absolute scope
- * to share them among consecutive VM invocations.
- */
- value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
- sizeof(njs_value_t));
- if (njs_slow_path(value == NULL)) {
- return NJS_INDEX_ERROR;
- }
-
- index = (njs_index_t) value;
-
- } else {
- values = scope->values[scope_index];
-
- if (values == NULL) {
- values = njs_arr_create(vm->mem_pool, 4, sizeof(njs_value_t));
- if (njs_slow_path(values == NULL)) {
- return NJS_INDEX_ERROR;
- }
-
- scope->values[scope_index] = values;
- }
-
- value = njs_arr_add(values);
- if (njs_slow_path(value == NULL)) {
- return NJS_INDEX_ERROR;
- }
-
- index = scope->next_index[scope_index];
- scope->next_index[scope_index] += sizeof(njs_value_t);
- }
-
- *value = *default_value;
-
- return index;
-}
-
-
static njs_variable_t *
njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id, njs_variable_type_t type)
{
NJS_VARIABLE_CONST = 0,
NJS_VARIABLE_LET,
NJS_VARIABLE_CATCH,
- NJS_VARIABLE_SHIM,
NJS_VARIABLE_VAR,
NJS_VARIABLE_FUNCTION,
} njs_variable_type_t;
uintptr_t unique_id;
njs_variable_type_t type:8; /* 3 bits */
- uint8_t argument;
- uint8_t this_object;
- uint8_t arguments_object;
+ njs_bool_t argument;
+ njs_bool_t arguments_object;
+ njs_bool_t self;
+ njs_bool_t function;
+
+ njs_parser_scope_t *scope;
+ njs_parser_scope_t *original;
njs_index_t index;
njs_value_t value;
uintptr_t unique_id;
njs_variable_t *variable;
njs_parser_scope_t *scope;
- njs_uint_t scope_index; /* NJS_SCOPE_INDEX_... */
njs_bool_t not_defined;
+ njs_bool_t closure;
} njs_variable_reference_t;
njs_variable_t *njs_variable_add(njs_parser_t *parser,
njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
+njs_variable_t *njs_variable_function_add(njs_parser_t *parser,
+ njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type);
njs_int_t njs_variables_copy(njs_vm_t *vm, njs_rbtree_t *variables,
njs_rbtree_t *prev_variables);
njs_variable_t * njs_label_add(njs_vm_t *vm, njs_parser_scope_t *scope,
uintptr_t unique_id);
njs_int_t njs_label_remove(njs_vm_t *vm, njs_parser_scope_t *scope,
uintptr_t unique_id);
-njs_int_t njs_variable_reference(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type);
-njs_int_t njs_variables_scope_reference(njs_vm_t *vm,
- njs_parser_scope_t *scope);
-njs_index_t njs_scope_next_index(njs_vm_t *vm, njs_parser_scope_t *scope,
- njs_uint_t scope_index, const njs_value_t *default_value);
+njs_variable_t *njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node);
+njs_variable_t *njs_variable_scope_add(njs_parser_t *parser,
+ njs_parser_scope_t *scope, njs_parser_scope_t *original,
+ uintptr_t unique_id, njs_variable_type_t type, njs_index_t index);
njs_int_t njs_name_copy(njs_vm_t *vm, njs_str_t *dst, const njs_str_t *src);
vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX;
+ if (njs_scope_undefined_index(vm, 0) == NJS_INDEX_ERROR) {
+ return NULL;
+ }
+
return vm;
}
njs_int_t
njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
{
- njs_int_t ret;
- njs_str_t ast;
- njs_chb_t chain;
- njs_lexer_t lexer;
- njs_parser_t parser;
- njs_vm_code_t *code;
- njs_generator_t generator;
-
- if (vm->modules != NULL && vm->options.accumulative) {
+ njs_int_t ret;
+ njs_str_t ast;
+ njs_chb_t chain;
+ njs_value_t **global, **new;
+ njs_lexer_t lexer;
+ njs_parser_t parser;
+ njs_vm_code_t *code;
+ njs_generator_t generator;
+ njs_parser_scope_t *scope;
+
+ njs_memzero(&parser, sizeof(njs_parser_t));
+
+ parser.scope = vm->global_scope;
+
+ if (parser.scope != NULL && vm->modules != NULL) {
njs_module_reset(vm);
}
return NJS_ERROR;
}
- njs_memzero(&parser, sizeof(njs_parser_t));
-
parser.lexer = &lexer;
- ret = njs_parser(vm, &parser, vm->variables_hash);
+ ret = njs_parser(vm, &parser);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
*start = lexer.start;
-
- ret = njs_variables_scope_reference(vm, parser.scope);
- if (njs_slow_path(ret != NJS_OK)) {
- return NJS_ERROR;
- }
+ scope = parser.scope;
njs_memzero(&generator, sizeof(njs_generator_t));
- code = njs_generate_scope(vm, &generator, parser.scope, &njs_entry_main);
+ code = njs_generate_scope(vm, &generator, scope, &njs_entry_main);
if (njs_slow_path(code == NULL)) {
if (!njs_is_error(&vm->retval)) {
njs_internal_error(vm, "njs_generate_scope() failed");
return NJS_ERROR;
}
+ vm->global_scope = scope;
+
+ if (scope->items > vm->global_items) {
+ global = vm->levels[NJS_LEVEL_GLOBAL];
+
+ new = njs_scope_make(vm, scope->items);
+ if (njs_slow_path(new == NULL)) {
+ return ret;
+ }
+
+ vm->levels[NJS_LEVEL_GLOBAL] = new;
+
+ if (global != NULL) {
+ while (vm->global_items != 0) {
+ vm->global_items--;
+
+ *new++ = *global++;
+ }
+
+ njs_mp_free(vm->mem_pool, global);
+ }
+ }
+
+ /* globalThis and this */
+ njs_scope_value_set(vm, njs_scope_global_this_index(), &vm->global_value);
+
vm->start = generator.code_start;
- vm->global_scope = generator.local_scope;
- vm->scope_size = generator.scope_size;
+ vm->variables_hash = &scope->variables;
+ vm->global_items = scope->items;
- vm->variables_hash = &parser.scope->variables;
+ vm->levels[NJS_LEVEL_TEMP] = NULL;
- if (vm->options.init && !vm->options.accumulative) {
- ret = njs_vm_init(vm);
- if (njs_slow_path(ret != NJS_OK)) {
+ if (scope->temp != 0) {
+ new = njs_scope_make(vm, scope->temp);
+ if (njs_slow_path(new == NULL)) {
return ret;
}
+
+ vm->levels[NJS_LEVEL_TEMP] = new;
}
if (vm->options.disassemble) {
njs_vm_t *
njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external)
{
- njs_mp_t *nmp;
- njs_vm_t *nvm;
- njs_int_t ret;
+ njs_mp_t *nmp;
+ njs_vm_t *nvm;
+ njs_int_t ret;
+ njs_value_t **global;
njs_thread_log_debug("CLONE:");
- if (vm->options.accumulative) {
+ if (vm->options.interactive) {
return NULL;
}
goto fail;
}
+ global = njs_scope_make(nvm, nvm->global_items);
+ if (njs_slow_path(global == NULL)) {
+ goto fail;
+ }
+
+ nvm->levels[NJS_LEVEL_GLOBAL] = global;
+
+ njs_set_object(&nvm->global_value, &nvm->global_object);
+
+ /* globalThis and this */
+ njs_scope_value_set(nvm, njs_scope_global_this_index(), &nvm->global_value);
+
+ nvm->levels[NJS_LEVEL_LOCAL] = NULL;
+
return nvm;
fail:
static njs_int_t
njs_vm_init(njs_vm_t *vm)
{
- size_t size, scope_size;
- u_char *values;
njs_int_t ret;
- njs_value_t *global;
njs_frame_t *frame;
- scope_size = vm->scope_size + NJS_INDEX_GLOBAL_OFFSET;
-
- size = njs_frame_size(0) + scope_size + NJS_FRAME_SPARE_SIZE;
- size = njs_align_size(size, NJS_FRAME_SPARE_SIZE);
-
- frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size);
+ frame = (njs_frame_t *) njs_function_frame_alloc(vm, NJS_FRAME_SIZE);
if (njs_slow_path(frame == NULL)) {
+ njs_memory_error(vm);
return NJS_ERROR;
}
- njs_memzero(frame, njs_frame_size(0));
+ frame->exception.catch = NULL;
+ frame->exception.next = NULL;
+ frame->previous_active_frame = NULL;
- vm->top_frame = &frame->native;
vm->active_frame = frame;
- frame->native.size = size;
- frame->native.free_size = size - (njs_frame_size(0) + scope_size);
-
- values = (u_char *) frame + njs_frame_size(0);
-
- frame->native.free = values + scope_size;
-
- vm->scopes[NJS_SCOPE_GLOBAL] = (njs_value_t *) values;
-
- memcpy(values + NJS_INDEX_GLOBAL_OFFSET, vm->global_scope, vm->scope_size);
-
ret = njs_regexp_init(vm);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
- global = (njs_value_t *) (values + NJS_INDEX_GLOBAL_OBJECT_OFFSET);
-
- ret = njs_builtin_objects_clone(vm, global);
+ ret = njs_builtin_objects_clone(vm, &vm->global_value);
if (njs_slow_path(ret != NJS_OK)) {
return NJS_ERROR;
}
njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
njs_uint_t nargs)
{
- return njs_vm_invoke(vm, function, args, nargs, (njs_index_t) &vm->retval);
+ return njs_vm_invoke(vm, function, args, nargs, &vm->retval);
}
njs_int_t
njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
- njs_uint_t nargs, njs_index_t retval)
+ njs_uint_t nargs, njs_value_t *retval)
{
njs_int_t ret;
njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *native,
njs_native_frame_t *previous)
{
- njs_uint_t n, nesting;
- njs_value_t *args;
- njs_frame_t *frame;
- njs_closure_t **closures;
- njs_function_t *function;
+ njs_frame_t *frame;
vm->top_frame = previous;
- args = previous->arguments;
- function = previous->function;
-
- if (function != NULL) {
- args += function->args_offset;
- }
-
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = args;
-
- function = native->function;
-
- if (function->native) {
+ if (native->function->native) {
return;
}
- if (function->closure) {
- /* GC: release function closures. */
- }
-
frame = (njs_frame_t *) native;
frame = frame->previous_active_frame;
vm->active_frame = frame;
-
- /* GC: arguments, local, and local block closures. */
-
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
- vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
-
- function = frame->native.function;
-
- nesting = (function != NULL) ? function->u.lambda->nesting : 0;
- closures = njs_frame_closures(frame);
-
- for (n = 0; n <= nesting; n++) {
- vm->scopes[NJS_SCOPE_CLOSURE + n] = &closures[n]->u.values;
- }
-
- while (n < NJS_MAX_NESTING) {
- vm->scopes[NJS_SCOPE_CLOSURE + n] = NULL;
- n++;
- }
}
typedef enum {
- NJS_SCOPE_ABSOLUTE = 0,
- NJS_SCOPE_GLOBAL = 1,
- NJS_SCOPE_CALLEE_ARGUMENTS = 2,
- /*
- * The argument and local VM scopes should be separated because a
- * function may be called with any number of arguments.
- */
- NJS_SCOPE_ARGUMENTS = 3,
- NJS_SCOPE_LOCAL = 4,
- NJS_SCOPE_FUNCTION = NJS_SCOPE_LOCAL,
-
- NJS_SCOPE_CLOSURE = 5,
- /*
- * The block and shim scopes are not really VM scopes.
- * They are used only on parsing phase.
- */
- NJS_SCOPE_BLOCK = 16,
- NJS_SCOPE_SHIM = 17,
+ NJS_SCOPE_GLOBAL = 0,
+ NJS_SCOPE_FUNCTION,
+ NJS_SCOPE_BLOCK
} njs_scope_t;
-/*
- * The maximum possible function nesting level is (16 - NJS_SCOPE_CLOSURE),
- * that is 11. The 8 is reasonable limit.
- */
-#define NJS_MAX_NESTING 8
-
-#define NJS_SCOPES (NJS_SCOPE_CLOSURE + NJS_MAX_NESTING)
-
-#define NJS_SCOPE_SHIFT 4
-#define NJS_SCOPE_MASK ((uintptr_t) ((1 << NJS_SCOPE_SHIFT) - 1))
-
-#define NJS_INDEX_NONE ((njs_index_t) 0)
-#define NJS_INDEX_ERROR ((njs_index_t) -1)
-#define NJS_INDEX_THIS ((njs_index_t) (0 | NJS_SCOPE_ARGUMENTS))
-
-#define njs_scope_type(index) \
- ((uintptr_t) (index) & NJS_SCOPE_MASK)
-
-#define njs_is_callee_argument_index(index) \
- (((index) & NJS_SCOPE_CALLEE_ARGUMENTS) == NJS_SCOPE_CALLEE_ARGUMENTS)
-
-
typedef enum {
NJS_OBJ_TYPE_OBJECT = 0,
NJS_OBJ_TYPE_ARRAY,
};
-#define njs_scope_index(value, type) \
- ((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | (type)))
-
-
-#define njs_global_scope_index(value) \
- (njs_scope_index(value, NJS_SCOPE_GLOBAL))
-
-
-#define NJS_INDEX_GLOBAL_OBJECT njs_global_scope_index(0)
-#define NJS_INDEX_GLOBAL_OBJECT_OFFSET njs_scope_index(0, 0)
-
-
-#define NJS_INDEX_GLOBAL_RETVAL njs_global_scope_index(1)
-#define NJS_INDEX_GLOBAL_OFFSET njs_scope_index(1, 0)
-
-
-#define njs_scope_offset(index) \
- ((uintptr_t) (index) & ~NJS_SCOPE_MASK)
-
-
-#define njs_vmcode_operand(vm, index) \
- ((njs_value_t *) \
- ((u_char *) vm->scopes[(uintptr_t) (index) & NJS_SCOPE_MASK] \
- + njs_scope_offset(index)))
-
-
enum njs_hook_e {
NJS_HOOK_EXIT = 0,
NJS_HOOK_MAX
};
+typedef enum {
+ NJS_LEVEL_LOCAL = 0,
+ NJS_LEVEL_CLOSURE,
+ NJS_LEVEL_GLOBAL,
+ NJS_LEVEL_STATIC,
+ NJS_LEVEL_TEMP,
+ NJS_LEVEL_MAX
+} njs_level_type_t;
+
+
struct njs_vm_s {
/* njs_vm_t must be aligned to njs_value_t due to scratch value. */
njs_value_t retval;
njs_arr_t *paths;
njs_arr_t *protos;
- njs_value_t *scopes[NJS_SCOPES];
+ njs_arr_t *scope_absolute;
+ njs_value_t **levels[NJS_LEVEL_MAX];
+ size_t global_items;
njs_external_ptr_t external;
njs_mp_t *mem_pool;
u_char *start;
- njs_value_t *global_scope;
- size_t scope_size;
size_t stack_size;
njs_vm_shared_t *shared;
njs_array_t *promise_reason;
+ njs_parser_scope_t *global_scope;
+
/*
* MemoryError is statically allocated immutable Error object
* with the InternalError prototype.
njs_object_t string_object;
njs_object_t global_object;
+ njs_value_t global_value;
njs_arr_t *codes; /* of njs_vm_code_t */
njs_arr_t *functions_name_cache;
njs_value_t *inlvd1, njs_value_t *inlvd2);
static njs_jump_off_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value,
njs_value_t *invld);
+static njs_jump_off_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value,
+ njs_index_t retval);
static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value,
njs_value_t *key, njs_value_t *retval);
njs_value_t *retval, u_char *pc);
static void njs_vmcode_error(njs_vm_t *vm, u_char *pc);
-/*
- * These functions are forbidden to inline to minimize JavaScript VM
- * interpreter memory footprint. The size is less than 8K on AMD64
- * and should fit in CPU L1 instruction cache.
- */
-
static njs_jump_off_t njs_string_concat(njs_vm_t *vm, njs_value_t *val1,
njs_value_t *val2);
static njs_jump_off_t njs_values_equal(njs_vm_t *vm, njs_value_t *val1,
njs_value_t *value, const njs_value_t *this, uintptr_t nargs,
njs_bool_t ctor);
-/*
- * The nJSVM is optimized for an ABIs where the first several arguments
- * are passed in registers (AMD64, ARM32/64): two pointers to the operand
- * values is passed as arguments although they are not always used.
- */
+
+#define njs_vmcode_operand(vm, index) njs_scope_valid_value(vm, index)
+
njs_int_t
njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc)
njs_value_t numeric1, numeric2, primitive1, primitive2;
njs_frame_t *frame;
njs_jump_off_t ret;
- njs_vmcode_this_t *this;
njs_native_frame_t *previous, *native;
njs_property_next_t *next;
+ njs_vmcode_finally_t *finally;
njs_vmcode_generic_t *vmcode;
+ njs_vmcode_move_arg_t *move_arg;
njs_vmcode_prop_get_t *get;
njs_vmcode_prop_set_t *set;
njs_vmcode_operation_t op;
njs_vmcode_equal_jump_t *equal;
njs_vmcode_try_return_t *try_return;
njs_vmcode_method_frame_t *method_frame;
+ njs_vmcode_function_copy_t *fcopy;
njs_vmcode_prop_accessor_t *accessor;
+ njs_vmcode_try_trampoline_t *try_trampoline;
njs_vmcode_function_frame_t *function_frame;
next:
*retval = vm->retval;
} else {
+
switch (op) {
+ case NJS_VMCODE_MOVE_ARG:
+ move_arg = (njs_vmcode_move_arg_t *) pc;
+ native = vm->top_frame;
+
+ hint = move_arg->dst;
+
+ value1 = &native->arguments_offset[hint];
+ value2 = njs_vmcode_operand(vm, move_arg->src);
+
+ *value1 = *value2;
+
+ ret = sizeof(njs_vmcode_move_arg_t);
+ break;
+
case NJS_VMCODE_STOP:
- value2 = njs_vmcode_operand(vm, value2);
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
vm->retval = *value2;
return NJS_OK;
break;
case NJS_VMCODE_RETURN:
- value2 = njs_vmcode_operand(vm, value2);
-
- frame = (njs_frame_t *) vm->top_frame;
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+ return njs_vmcode_return(vm, NULL, value2);
- if (frame->native.ctor) {
- if (njs_is_object(value2)) {
- njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
-
- } else {
- value2 = vm->scopes[NJS_SCOPE_ARGUMENTS];
- }
- }
-
- previous = njs_function_previous_frame(&frame->native);
-
- njs_vm_scopes_restore(vm, &frame->native, previous);
-
- /*
- * If a retval is in a callee arguments scope it
- * must be in the previous callee arguments scope.
- */
- retval = njs_vmcode_operand(vm, frame->native.retval);
-
- /*
- * GC: value external/internal++ depending on
- * value and retval type
- */
- *retval = *value2;
-
- njs_function_frame_free(vm, &frame->native);
-
- return NJS_OK;
+ case NJS_VMCODE_FUNCTION_COPY:
+ fcopy = (njs_vmcode_function_copy_t *) pc;
+ ret = njs_vmcode_function_copy(vm, fcopy->function,
+ fcopy->retval);
+ break;
case NJS_VMCODE_FUNCTION_FRAME:
function_frame = (njs_vmcode_function_frame_t *) pc;
case NJS_VMCODE_FUNCTION_CALL:
vm->active_frame->native.pc = pc;
- ret = njs_function_frame_invoke(vm, (njs_index_t) value2);
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
+
+ ret = njs_function_frame_invoke(vm, value2);
if (njs_slow_path(ret == NJS_ERROR)) {
goto error;
}
ret = sizeof(njs_vmcode_prop_next_t);
break;
- case NJS_VMCODE_THIS:
- frame = vm->active_frame;
- this = (njs_vmcode_this_t *) pc;
-
- retval = njs_vmcode_operand(vm, this->dst);
- *retval = frame->native.arguments[0];
-
- ret = sizeof(njs_vmcode_this_t);
- break;
-
case NJS_VMCODE_ARGUMENTS:
ret = njs_vmcode_arguments(vm, pc);
if (njs_slow_path(ret == NJS_ERROR)) {
break;
case NJS_VMCODE_THROW:
- value2 = njs_vmcode_operand(vm, value2);
+ value2 = njs_vmcode_operand(vm, (njs_index_t) value2);
vm->retval = *value2;
goto error;
case NJS_VMCODE_TRY_BREAK:
+ try_trampoline = (njs_vmcode_try_trampoline_t *) pc;
+ value1 = njs_scope_value(vm, try_trampoline->exit_value);
+
ret = njs_vmcode_try_break(vm, value1, value2);
break;
case NJS_VMCODE_TRY_CONTINUE:
+ try_trampoline = (njs_vmcode_try_trampoline_t *) pc;
+ value1 = njs_scope_value(vm, try_trampoline->exit_value);
+
ret = njs_vmcode_try_continue(vm, value1, value2);
break;
break;
case NJS_VMCODE_FINALLY:
+ finally = (njs_vmcode_finally_t *) pc;
+ value1 = njs_scope_value(vm, finally->exit_value);
+
ret = njs_vmcode_finally(vm, value1, value2, pc);
switch (ret) {
njs_vmcode_function(njs_vm_t *vm, u_char *pc)
{
njs_function_t *function;
- njs_function_lambda_t *lambda;
njs_vmcode_function_t *code;
+ njs_function_lambda_t *lambda;
code = (njs_vmcode_function_t *) pc;
lambda = code->lambda;
- function = njs_function_alloc(vm, lambda,
- njs_frame_closures(vm->active_frame), 0);
+ function = njs_function_alloc(vm, lambda);
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
+ if (njs_function_capture_closure(vm, function, lambda) != NJS_OK) {
+ return NJS_ERROR;
+ }
+
function->args_count = lambda->nargs - lambda->rest_parameters;
njs_set_function(&vm->retval, function);
.u.native = njs_string_prototype_concat
};
- value = njs_vmcode_operand(vm, retval);
+ value = njs_vmcode_operand(vm, (njs_index_t) retval);
if (!njs_is_primitive(value)) {
array = njs_array(value);
return ret;
}
- ret = njs_function_frame_invoke(vm, (njs_index_t) retval);
+ ret = njs_function_frame_invoke(vm, value);
if (njs_slow_path(ret != NJS_OK)) {
return ret;
}
}
+static njs_jump_off_t
+njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_index_t retidx)
+{
+ njs_value_t *retval;
+ njs_function_t *function;
+
+ retval = njs_scope_valid_value(vm, retidx);
+
+ if (njs_is_undefined(retval)) {
+ *retval = *value;
+
+ function = njs_function_value_copy(vm, retval);
+ if (njs_slow_path(function == NULL)) {
+ return NJS_ERROR;
+ }
+ }
+
+ return sizeof(njs_vmcode_function_copy_t);
+}
+
+
static njs_jump_off_t
njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
njs_value_t *init)
static njs_jump_off_t
njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
{
- njs_value_t *value;
njs_frame_t *frame;
njs_native_frame_t *previous;
- value = njs_vmcode_operand(vm, retval);
-
frame = (njs_frame_t *) vm->top_frame;
if (frame->native.ctor) {
- if (njs_is_object(value)) {
- njs_release(vm, vm->scopes[NJS_SCOPE_ARGUMENTS]);
+ if (njs_is_object(retval)) {
+ njs_release(vm, frame->native.local[0]);
} else {
- value = vm->scopes[NJS_SCOPE_ARGUMENTS];
+ retval = frame->native.local[0];
}
}
njs_vm_scopes_restore(vm, &frame->native, previous);
- /*
- * If a retval is in a callee arguments scope it
- * must be in the previous callee arguments scope.
- */
- retval = njs_vmcode_operand(vm, frame->native.retval);
-
- /* GC: value external/internal++ depending on value and retval type */
- *retval = *value;
+ *frame->native.retval = *retval;
njs_function_frame_free(vm, &frame->native);
njs_set_invalid(exception_value);
try_start = (njs_vmcode_try_start_t *) pc;
- exit_value = njs_vmcode_operand(vm, try_start->exit_value);
+ exit_value = njs_scope_value(vm, try_start->exit_value);
njs_set_invalid(exit_value);
njs_number(exit_value) = 0;
njs_value_t *exception_value, *exit_value;
njs_vmcode_finally_t *finally;
- exception_value = njs_vmcode_operand(vm, retval);
+ exception_value = njs_scope_value(vm, (njs_index_t) retval);
if (njs_is_valid(exception_value)) {
vm->retval = *exception_value;
}
finally = (njs_vmcode_finally_t *) pc;
- exit_value = njs_vmcode_operand(vm, finally->exit_value);
+
+ exit_value = njs_scope_value(vm, finally->exit_value);
/*
* exit_value is set by:
#define NJS_VMCODE_3OPERANDS 0
#define NJS_VMCODE_2OPERANDS 1
-#define NJS_VMCODE_1OPERAND 2
-#define NJS_VMCODE_NO_OPERAND 3
-
-#define NJS_VMCODE_NO_RETVAL 0
-#define NJS_VMCODE_RETVAL 1
-
-#define VMCODE0(n) (n)
-#define VMCODE1(n) ((n) + 128)
-
-#define NJS_VMCODE_STOP VMCODE0(0)
-#define NJS_VMCODE_JUMP VMCODE0(1)
-#define NJS_VMCODE_PROPERTY_SET VMCODE0(2)
-#define NJS_VMCODE_PROPERTY_ACCESSOR VMCODE0(3)
-#define NJS_VMCODE_IF_TRUE_JUMP VMCODE0(4)
-#define NJS_VMCODE_IF_FALSE_JUMP VMCODE0(5)
-#define NJS_VMCODE_IF_EQUAL_JUMP VMCODE0(6)
-#define NJS_VMCODE_PROPERTY_INIT VMCODE0(7)
-#define NJS_VMCODE_RETURN VMCODE0(8)
-#define NJS_VMCODE_FUNCTION_FRAME VMCODE0(9)
-#define NJS_VMCODE_METHOD_FRAME VMCODE0(10)
-#define NJS_VMCODE_FUNCTION_CALL VMCODE0(11)
-#define NJS_VMCODE_PROPERTY_NEXT VMCODE0(16)
-#define NJS_VMCODE_THIS VMCODE0(17)
-#define NJS_VMCODE_ARGUMENTS VMCODE0(18)
-#define NJS_VMCODE_PROTO_INIT VMCODE0(19)
-
-#define NJS_VMCODE_TRY_START VMCODE0(32)
-#define NJS_VMCODE_THROW VMCODE0(33)
-#define NJS_VMCODE_TRY_BREAK VMCODE0(34)
-#define NJS_VMCODE_TRY_CONTINUE VMCODE0(35)
-#define NJS_VMCODE_TRY_END VMCODE0(37)
-#define NJS_VMCODE_CATCH VMCODE0(38)
-#define NJS_VMCODE_FINALLY VMCODE0(39)
-#define NJS_VMCODE_ERROR VMCODE0(40)
-
-#define NJS_VMCODE_NORET 127
-
-#define NJS_VMCODE_MOVE VMCODE1(0)
-#define NJS_VMCODE_PROPERTY_GET VMCODE1(1)
-#define NJS_VMCODE_INCREMENT VMCODE1(2)
-#define NJS_VMCODE_POST_INCREMENT VMCODE1(3)
-#define NJS_VMCODE_DECREMENT VMCODE1(4)
-#define NJS_VMCODE_POST_DECREMENT VMCODE1(5)
-#define NJS_VMCODE_TRY_RETURN VMCODE1(6)
-#define NJS_VMCODE_GLOBAL_GET VMCODE1(7)
-
-#define NJS_VMCODE_LESS VMCODE1(8)
-#define NJS_VMCODE_GREATER VMCODE1(9)
-#define NJS_VMCODE_LESS_OR_EQUAL VMCODE1(10)
-#define NJS_VMCODE_GREATER_OR_EQUAL VMCODE1(11)
-#define NJS_VMCODE_ADDITION VMCODE1(12)
-#define NJS_VMCODE_EQUAL VMCODE1(13)
-#define NJS_VMCODE_NOT_EQUAL VMCODE1(14)
-
-#define NJS_VMCODE_SUBSTRACTION VMCODE1(16)
-#define NJS_VMCODE_MULTIPLICATION VMCODE1(17)
-#define NJS_VMCODE_EXPONENTIATION VMCODE1(18)
-#define NJS_VMCODE_DIVISION VMCODE1(19)
-#define NJS_VMCODE_REMAINDER VMCODE1(20)
-#define NJS_VMCODE_BITWISE_AND VMCODE1(21)
-#define NJS_VMCODE_BITWISE_OR VMCODE1(22)
-#define NJS_VMCODE_BITWISE_XOR VMCODE1(23)
-#define NJS_VMCODE_LEFT_SHIFT VMCODE1(24)
-#define NJS_VMCODE_RIGHT_SHIFT VMCODE1(25)
-#define NJS_VMCODE_UNSIGNED_RIGHT_SHIFT VMCODE1(26)
-#define NJS_VMCODE_OBJECT_COPY VMCODE1(27)
-#define NJS_VMCODE_TEMPLATE_LITERAL VMCODE1(28)
-#define NJS_VMCODE_PROPERTY_IN VMCODE1(29)
-#define NJS_VMCODE_PROPERTY_DELETE VMCODE1(30)
-#define NJS_VMCODE_PROPERTY_FOREACH VMCODE1(31)
-
-#define NJS_VMCODE_STRICT_EQUAL VMCODE1(32)
-#define NJS_VMCODE_STRICT_NOT_EQUAL VMCODE1(33)
-
-#define NJS_VMCODE_TEST_IF_TRUE VMCODE1(34)
-#define NJS_VMCODE_TEST_IF_FALSE VMCODE1(35)
-
-#define NJS_VMCODE_COALESCE VMCODE1(36)
-
-#define NJS_VMCODE_UNARY_PLUS VMCODE1(37)
-#define NJS_VMCODE_UNARY_NEGATION VMCODE1(38)
-#define NJS_VMCODE_BITWISE_NOT VMCODE1(39)
-#define NJS_VMCODE_LOGICAL_NOT VMCODE1(40)
-#define NJS_VMCODE_OBJECT VMCODE1(41)
-#define NJS_VMCODE_ARRAY VMCODE1(42)
-#define NJS_VMCODE_FUNCTION VMCODE1(43)
-#define NJS_VMCODE_REGEXP VMCODE1(44)
-
-#define NJS_VMCODE_INSTANCE_OF VMCODE1(45)
-#define NJS_VMCODE_TYPEOF VMCODE1(46)
-#define NJS_VMCODE_VOID VMCODE1(47)
-#define NJS_VMCODE_DELETE VMCODE1(48)
-
-#define NJS_VMCODE_NOP 255
+
+
+enum {
+ NJS_VMCODE_MOVE_ARG = 0,
+ NJS_VMCODE_STOP,
+ NJS_VMCODE_JUMP,
+ NJS_VMCODE_PROPERTY_SET,
+ NJS_VMCODE_PROPERTY_ACCESSOR,
+ NJS_VMCODE_IF_TRUE_JUMP,
+ NJS_VMCODE_IF_FALSE_JUMP,
+ NJS_VMCODE_IF_EQUAL_JUMP,
+ NJS_VMCODE_PROPERTY_INIT,
+ NJS_VMCODE_RETURN,
+ NJS_VMCODE_FUNCTION_COPY,
+ NJS_VMCODE_FUNCTION_FRAME,
+ NJS_VMCODE_METHOD_FRAME,
+ NJS_VMCODE_FUNCTION_CALL,
+ NJS_VMCODE_PROPERTY_NEXT,
+ NJS_VMCODE_THIS,
+ NJS_VMCODE_ARGUMENTS,
+ NJS_VMCODE_PROTO_INIT,
+
+ NJS_VMCODE_TRY_START,
+ NJS_VMCODE_THROW,
+ NJS_VMCODE_TRY_BREAK,
+ NJS_VMCODE_TRY_CONTINUE,
+ NJS_VMCODE_TRY_END,
+ NJS_VMCODE_CATCH,
+ NJS_VMCODE_FINALLY,
+ NJS_VMCODE_ERROR,
+
+ NJS_VMCODE_NORET = 127
+};
+
+
+enum {
+ NJS_VMCODE_MOVE = NJS_VMCODE_NORET + 1,
+ NJS_VMCODE_PROPERTY_GET,
+ NJS_VMCODE_INCREMENT,
+ NJS_VMCODE_POST_INCREMENT,
+ NJS_VMCODE_DECREMENT,
+ NJS_VMCODE_POST_DECREMENT,
+ NJS_VMCODE_TRY_RETURN,
+ NJS_VMCODE_GLOBAL_GET,
+
+ NJS_VMCODE_LESS,
+ NJS_VMCODE_GREATER,
+ NJS_VMCODE_LESS_OR_EQUAL,
+ NJS_VMCODE_GREATER_OR_EQUAL,
+ NJS_VMCODE_ADDITION,
+ NJS_VMCODE_EQUAL,
+ NJS_VMCODE_NOT_EQUAL,
+
+ NJS_VMCODE_SUBSTRACTION,
+ NJS_VMCODE_MULTIPLICATION,
+ NJS_VMCODE_EXPONENTIATION,
+ NJS_VMCODE_DIVISION,
+ NJS_VMCODE_REMAINDER,
+ NJS_VMCODE_BITWISE_AND,
+ NJS_VMCODE_BITWISE_OR,
+ NJS_VMCODE_BITWISE_XOR,
+ NJS_VMCODE_LEFT_SHIFT,
+ NJS_VMCODE_RIGHT_SHIFT,
+ NJS_VMCODE_UNSIGNED_RIGHT_SHIFT,
+ NJS_VMCODE_OBJECT_COPY,
+ NJS_VMCODE_TEMPLATE_LITERAL,
+ NJS_VMCODE_PROPERTY_IN,
+ NJS_VMCODE_PROPERTY_DELETE,
+ NJS_VMCODE_PROPERTY_FOREACH,
+
+ NJS_VMCODE_STRICT_EQUAL,
+ NJS_VMCODE_STRICT_NOT_EQUAL,
+
+ NJS_VMCODE_TEST_IF_TRUE,
+ NJS_VMCODE_TEST_IF_FALSE,
+
+ NJS_VMCODE_COALESCE,
+
+ NJS_VMCODE_UNARY_PLUS,
+ NJS_VMCODE_UNARY_NEGATION,
+ NJS_VMCODE_BITWISE_NOT,
+ NJS_VMCODE_LOGICAL_NOT,
+ NJS_VMCODE_OBJECT,
+ NJS_VMCODE_ARRAY,
+ NJS_VMCODE_FUNCTION,
+ NJS_VMCODE_REGEXP,
+
+ NJS_VMCODE_INSTANCE_OF,
+ NJS_VMCODE_TYPEOF,
+ NJS_VMCODE_VOID,
+ NJS_VMCODE_DELETE,
+
+ NJS_VMCODE_NOP = 255
+};
typedef struct {
} njs_vmcode_error_t;
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t src;
+ njs_uint_t dst;
+} njs_vmcode_move_arg_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_value_t *function;
+ njs_index_t retval;
+} njs_vmcode_function_copy_t;
+
+
njs_int_t njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc);
njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor);
{ njs_str("var f = 1; function f() {}; f"),
njs_str("1") },
+ { njs_str("var f = 1; function f() {}; f"),
+ njs_str("1") },
+
+ { njs_str("function f(a) {return function (x) {return a(x)}} f(1)(0)"),
+ njs_str("TypeError: number is not a function") },
+
+ { njs_str("var x = 0;"
+ ""
+ "function f1() {"
+ " function f2() {"
+ " return x;"
+ " };"
+ ""
+ " return f2();"
+ ""
+ " var x = 1;"
+ "}"
+ ""
+ "f1() === undefined"),
+ njs_str("true") },
+
+ { njs_str("var fn = function fn() {return fn.test}; fn.test = 'test'; fn()"),
+ njs_str("test") },
+
+ { njs_str("var body;"
+ "var n = 'outside';"
+ "var before = function() {return n};"
+ ""
+ "var func = function n() {"
+ " var n;"
+ " body = function() {return n};"
+ "};"
+ ""
+ "func();"
+ ""
+ "[before(), body()]"),
+ njs_str("outside,") },
+
+ { njs_str("var func = function x(x) {return x}; func()"),
+ njs_str("undefined") },
+
#if 0 /* TODO */
{ njs_str("var a; Object.getOwnPropertyDescriptor(this, 'a').value"),
njs_str("undefined") },
{ njs_str("delete undefined"),
njs_str("SyntaxError: Delete of an unqualified identifier in 1") },
+ { njs_str("delete this !== true"),
+ njs_str("false") },
+
/* Object shorthand methods. */
{ njs_str("var o = {m(){}}; new o.m();"),
"})"
"})"
"})"),
- njs_str("SyntaxError: The maximum function nesting level is \"8\" in 1") },
+ njs_str("[object Function]") },
{ njs_str("Function.prototype.toString = function () {return 'X'};"
"eval"),
{ njs_str("(function (Object, Array, Boolean){ return Object + Array + Boolean})('x', 'y', 'z')"),
njs_str("xyz") },
+ { njs_str("var n = 11, res;"
+ "function a() {return b}"
+ "res = a()(2);"
+ "function b(k) {var x = b; return 1 + k + n} res"),
+ njs_str("14") },
+
+ { njs_str("var y = 9, res;"
+ "function a(n) {function b() {return c(n + 2)} return b()}"
+ "res = a(1);"
+ "function c(m) {var x = c; return m + 3 + y} res"),
+ njs_str("15") },
+
+ { njs_str("var res;"
+ "closure();"
+ "res = globalThis.funcall(1);"
+ "function closure() {"
+ " var y = 9, res;"
+ " globalThis.funcall = a;"
+ " function a(n) { function b() {return c(2)} return b() }"
+ " function c(m) {var x = c; return m + 3 + y}"
+ "} res"),
+ njs_str("14") },
+
+ { njs_str("function a() {"
+ " var x = 1;"
+ " function b() {var n = x; x = undefined; return n}"
+ " return b;"
+ "}"
+ "[a()(), a()()];"),
+ njs_str("1,1") },
+
+ { njs_str("function a(obj, name) {!Object.prototype.hasOwnProperty.call(obj, name)}"
+ "a(this, 'b');"
+ "function b() {}"),
+ njs_str("undefined") },
+
+ { njs_str("function abc() {"
+ "function x() {var a = x.arr; x.arr = 123; return a}"
+ "return x;"
+ "} [abc()(),abc()()]"),
+ njs_str(",") },
+
+ { njs_str("function x() {var a = x.arr; x.arr = 123; return a} [x(),x()]"),
+ njs_str(",123") },
+
+ { njs_str("var obj;"
+ "function make(desc) {obj = {'a': 123}}"
+ "function a(desc) {make()}"
+ "a(); obj.a"),
+ njs_str("123") },
+
+ { njs_str("var res;"
+ "function cls() {"
+ " var obj = {'a': 123};"
+ " Object.defineProperty(obj, \"length\", {"
+ " get: function() {res = obj}"
+ " });"
+ " return obj;"
+ "}"
+ "var obj = cls();"
+ "[].includes.call(obj); res.a"),
+ njs_str("123") },
+
+ { njs_str("function f(){} typeof(f)"),
+ njs_str("function") },
+
/* Recursive factorial. */
{ njs_str("function f(a) {"
{ njs_str("function f(){}; (function(){try {f(f((new RegExp('a**'))))} catch (e) { return 1}})()"),
njs_str("1") },
+ { njs_str("var before, during, after;"
+ ""
+ "try {"
+ " throw 'exception';"
+ "} catch (err) {"
+ " before = err;"
+ ""
+ " for (var err in { name: null }) {"
+ " during = err;"
+ " }"
+ ""
+ " after = err;"
+ "}"
+ ""
+ "[before === 'exception', during === 'name', after === 'name']"),
+ njs_str("true,true,true") },
+
+ { njs_str("var arr = [];"
+ "foo = \"outside\";"
+ ""
+ "try {"
+ " throw new Error();"
+ "}"
+ "catch (foo) {"
+ " var foo = \"inside\";"
+ " arr.push(foo);"
+ "}"
+ ""
+ "arr.push(foo);"
+ "arr"),
+ njs_str("inside,outside") },
+
{ njs_str("var o = { valueOf: function() { return '3' } }; --o"),
njs_str("2") },
{ njs_str("/abc/i.test('ABC')" ENTER),
njs_str("true") },
- /* Accumulative mode. */
+ /* Interactive mode. */
{ njs_str("var a = 1" ENTER
"a" ENTER),
njs_vm_opt_init(&options);
options.init = 1;
- options.accumulative = 1;
+ options.interactive = 1;
options.backtrace = 1;
vm = njs_vm_create(&options);