nxt_uint_t nargs, njs_index_t retval)
{
/* Skip retval update. */
- vm->frame->skip = 1;
+ vm->top_frame->skip = 1;
return NXT_OK;
}
value = njs_is_true(&args[1]) ? &njs_value_true : &njs_value_false;
}
- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {
object = njs_object_value_alloc(vm, value, value->type);
if (nxt_slow_path(object == NULL)) {
return NXT_ERROR;
njs_date_t *date;
struct tm tm;
- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {
if (nargs == 1) {
time = njs_gettime();
nxt_uint_t nargs, njs_index_t retval)
{
/* Skip retval update. */
- vm->frame->skip = 1;
+ vm->top_frame->skip = 1;
return NXT_OK;
}
njs_function_t *
njs_function_value_copy(njs_vm_t *vm, njs_value_t *value)
{
- njs_function_t *function;
+ size_t size;
+ nxt_uint_t n, nesting;
+ njs_function_t *function, *copy;
function = value->data.u.function;
return function;
}
- function = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_function_t));
+ nesting = (function->native) ? 0 : function->u.lambda->nesting;
- if (nxt_fast_path(function != NULL)) {
- *function = *value->data.u.function;
- function->object.__proto__ =
- &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
- function->object.shared = 0;
- value->data.u.function = function;
+ size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
+
+ copy = nxt_mem_cache_alloc(vm->mem_cache_pool, size);
+ if (nxt_slow_path(copy == NULL)) {
+ return copy;
}
- return function;
+ value->data.u.function = copy;
+
+ *copy = *function;
+ copy->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
+ copy->object.shared = 0;
+
+ if (nesting == 0) {
+ return copy;
+ }
+
+ copy->closure = 1;
+
+ n = 0;
+
+ do {
+ /* GC: retain closure. */
+ copy->closures[n] = vm->active_frame->closures[n];
+ n++;
+ } while (n < nesting);
+
+ return copy;
}
frame->ctor = ctor;
value = (njs_value_t *) (njs_continuation(frame) + reserve);
+ frame->arguments = value;
bound = function->bound;
} while (n != 0);
}
- frame->arguments = value;
vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
if (args != NULL) {
const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs,
nxt_bool_t ctor)
{
- size_t size;
- nxt_uint_t n, max_args;
- njs_value_t *value, *bound;
- njs_frame_t *frame;
- njs_native_frame_t *native_frame;
+ size_t size;
+ nxt_uint_t n, max_args, closures;;
+ njs_value_t *value, *bound;
+ njs_frame_t *frame;
+ njs_native_frame_t *native_frame;
+ njs_function_lambda_t *lambda;
+
+ lambda = function->u.lambda;
- max_args = nxt_max(nargs, function->u.lambda->nargs);
+ max_args = nxt_max(nargs, lambda->nargs);
+
+ closures = lambda->nesting + lambda->block_closures;
size = NJS_FRAME_SIZE
+ (function->args_offset + max_args) * sizeof(njs_value_t)
- + function->u.lambda->local_size;
+ + lambda->local_size
+ + closures * sizeof(njs_closure_t *);
native_frame = njs_function_frame_alloc(vm, size);
if (nxt_slow_path(native_frame == NULL)) {
native_frame->nargs = nargs;
native_frame->ctor = ctor;
+ /* Function arguments. */
+
value = (njs_value_t *) ((u_char *) native_frame + NJS_FRAME_SIZE);
+ native_frame->arguments = value;
bound = function->bound;
} while (n != 0);
}
- native_frame->arguments = value;
vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = value;
if (args != NULL) {
frame = (njs_frame_t *) native_frame;
frame->local = value;
- memcpy(frame->local, function->u.lambda->local_scope,
- function->u.lambda->local_size);
-
return NXT_OK;
}
size_t spare_size, chunk_size;
njs_native_frame_t *frame;
- spare_size = vm->frame->free_size;
+ spare_size = vm->top_frame->free_size;
if (nxt_fast_path(size <= spare_size)) {
- frame = (njs_native_frame_t *) vm->frame->free;
+ frame = (njs_native_frame_t *) vm->top_frame->free;
chunk_size = 0;
} else {
frame->free_size = spare_size - size;
frame->free = (u_char *) frame + size;
- frame->previous = vm->frame;
- vm->frame = frame;
+ frame->previous = vm->top_frame;
+ vm->top_frame = frame;
return frame;
}
nxt_noinline njs_ret_t
njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance)
{
- njs_frame_t *frame;
- njs_function_t *function;
+ size_t size;
+ nxt_uint_t n, nesting;
+ njs_frame_t *frame;
+ njs_value_t *value;
+ njs_closure_t *closure, **closures;
+ njs_function_t *function;
+ njs_function_lambda_t *lambda;
- frame = (njs_frame_t *) vm->frame;
+ frame = (njs_frame_t *) vm->top_frame;
frame->retval = retval;
function = frame->native.function;
frame->return_address = vm->current + advance;
- vm->current = function->u.lambda->u.start;
- frame->prev_arguments = vm->scopes[NJS_SCOPE_ARGUMENTS];
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments
- - function->args_offset;
+ lambda = function->u.lambda;
+ vm->current = lambda->u.start;
+
#if (NXT_DEBUG)
vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = NULL;
#endif
- frame->prev_local = vm->scopes[NJS_SCOPE_FUNCTION];
- vm->scopes[NJS_SCOPE_FUNCTION] = frame->local;
+
+ vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->native.arguments;
+
+ /* Function local variables and temporary values. */
+
+ vm->scopes[NJS_SCOPE_LOCAL] = frame->local;
+
+ memcpy(frame->local, lambda->local_scope, lambda->local_size);
+
+ /* Parent closures values. */
+
+ n = 0;
+ nesting = lambda->nesting;
+
+ if (nesting != 0) {
+ closures = (function->closure) ? function->closures
+ : vm->active_frame->closures;
+ do {
+ closure = *closures++;
+
+ frame->closures[n] = closure;
+ vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
+
+ n++;
+ } while (n < nesting);
+ }
+
+ /* Function closure values. */
+
+ if (lambda->block_closures > 0) {
+ closure = NULL;
+
+ size = lambda->closure_size;
+
+ if (size != 0) {
+ closure = nxt_mem_cache_align(vm->mem_cache_pool,
+ sizeof(njs_value_t), size);
+ if (nxt_slow_path(closure == NULL)) {
+ return NXT_ERROR;
+ }
+
+ /* TODO: copy initialzed values. */
+
+ size -= sizeof(njs_value_t);
+ closure->u.count = 0;
+ value = closure->values;
+
+ do {
+ *value++ = njs_value_void;
+ size -= sizeof(njs_value_t);
+ } while (size != 0);
+ }
+
+ frame->closures[n] = closure;
+ vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values;
+ }
+
+ frame->previous_active_frame = vm->active_frame;
+ vm->active_frame = frame;
return NJS_APPLIED;
}
}
/* Skip the "call/apply" method frame. */
- vm->frame->previous->skip = 1;
+ vm->top_frame->previous->skip = 1;
cont = njs_vm_continuation(vm);
}
/* Skip the "call/apply" method frame. */
- vm->frame->previous->skip = 1;
+ vm->top_frame->previous->skip = 1;
return njs_function_call(vm, retval, sizeof(njs_vmcode_function_call_t));
}
struct njs_function_lambda_s {
uint32_t nargs;
uint32_t local_size;
+ uint32_t closure_size;
+
+ /* Function nesting level. */
+ uint8_t nesting; /* 4 bits */
+
+ /* Function internal block closures levels. */
+ uint8_t block_closures; /* 4 bits */
/* Initial values of local scope. */
njs_value_t *local_scope;
#define njs_vm_continuation(vm) \
- (void *) njs_continuation((vm)->frame)
+ (void *) njs_continuation((vm)->top_frame)
#define njs_continuation(frame) \
((u_char *) frame + NJS_NATIVE_FRAME_SIZE)
#define njs_vm_trap_value(vm, val) \
- (vm)->frame->trap_scratch.data.u.value = val
+ (vm)->top_frame->trap_scratch.data.u.value = val
};
-typedef struct {
+struct njs_frame_s {
njs_native_frame_t native;
+ njs_index_t retval;
+
u_char *return_address;
- njs_value_t *prev_arguments;
- njs_value_t *prev_local;
- njs_value_t *local;
- njs_value_t *closure;
+ njs_frame_t *previous_active_frame;
- njs_index_t retval;
-} njs_frame_t;
+ njs_value_t *local;
+#if (NXT_SUNC)
+ njs_closure_t *closures[1];
+#else
+ njs_closure_t *closures[];
+#endif
+};
njs_function_t *njs_function_alloc(njs_vm_t *vm);
njs_parser_t *parser, njs_parser_node_t *node);
static nxt_int_t njs_generate_function_scope(njs_vm_t *vm,
njs_function_lambda_t *lambda, njs_parser_node_t *node);
+static void njs_generate_argument_closures(njs_parser_t *parser,
+ njs_parser_node_t *node);
static nxt_int_t njs_generate_return_statement(njs_vm_t *vm,
njs_parser_t *parser, njs_parser_node_t *node);
static nxt_int_t njs_generate_function_call(njs_vm_t *vm, njs_parser_t *parser,
njs_variable_t *var;
njs_vmcode_object_copy_t *copy;
- var = njs_variable_get(vm, node, NJS_NAME_REFERENCE);
+ var = njs_variable_get(vm, node);
if (nxt_slow_path(var == NULL)) {
return NXT_ERROR;
}
njs_index_t index;
njs_vmcode_object_copy_t *copy;
- index = njs_variable_index(vm, node, NJS_NAME_REFERENCE);
+ index = njs_variable_index(vm, node);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
{
njs_index_t index;
- index = njs_variable_index(vm, node, NJS_NAME_REFERENCE);
+ index = njs_variable_index(vm, node);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
lvalue = node->left;
- index = njs_variable_index(vm, lvalue, NJS_NAME_DECLARATION);
+ index = njs_variable_index(vm, lvalue);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
if (lvalue->token == NJS_TOKEN_NAME) {
- index = njs_variable_index(vm, lvalue, NJS_NAME_REFERENCE);
+ index = njs_variable_index(vm, lvalue);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
expr = node->left;
if (expr->token == NJS_TOKEN_NAME) {
- index = njs_variable_index(vm, expr, NJS_NAME_TYPEOF);
+ index = njs_variable_typeof(vm, expr);
if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
njs_variable_t *var;
njs_function_lambda_t *lambda;
- var = njs_variable_get(vm, node, NJS_NAME_DECLARATION);
+ var = njs_variable_get(vm, node);
if (nxt_slow_path(var == NULL)) {
return NXT_ERROR;
}
njs_generate_function_scope(njs_vm_t *vm, njs_function_lambda_t *lambda,
njs_parser_node_t *node)
{
- nxt_int_t ret;
+ size_t size;
+ nxt_int_t ret;
+ nxt_array_t *closure;
+ njs_parser_t *parser;
+
+ parser = lambda->u.parser;
+ node = node->right;
+
+ parser->code_size += node->scope->argument_closures
+ * sizeof(njs_vmcode_move_t);
- ret = njs_generate_scope(vm, lambda->u.parser, node->right);
+ ret = njs_generate_scope(vm, parser, node);
if (nxt_fast_path(ret == NXT_OK)) {
- lambda->local_size = lambda->u.parser->scope_size;
- lambda->local_scope = lambda->u.parser->local_scope;
- lambda->u.start = lambda->u.parser->code_start;
+ size = 0;
+ closure = node->scope->values[1];
+
+ if (closure != NULL) {
+ lambda->block_closures = 1;
+ size = (1 + closure->items) * sizeof(njs_value_t);
+ }
+
+ lambda->nesting = node->scope->nesting;
+ lambda->closure_size = size;
+
+ lambda->local_size = parser->scope_size;
+ lambda->local_scope = parser->local_scope;
+ lambda->u.start = parser->code_start;
}
return ret;
njs_vm_code_t *code;
njs_parser_scope_t *scope;
+ scope = node->scope;
+
p = nxt_mem_cache_alloc(vm->mem_cache_pool, parser->code_size);
if (nxt_slow_path(p == NULL)) {
return NXT_ERROR;
parser->code_start = p;
parser->code_end = p;
+ njs_generate_argument_closures(parser, node);
+
if (nxt_slow_path(njs_generator(vm, parser, node) != NXT_OK)) {
return NXT_ERROR;
}
- scope = node->scope;
-
code_size = parser->code_end - parser->code_start;
nxt_thread_log_debug("SCOPE CODE SIZE: %uz %uz",
return NXT_ERROR;
}
- scope_size = njs_offset(scope->next_index);
+ scope_size = njs_scope_offset(scope->next_index[0]);
if (scope->type == NJS_SCOPE_GLOBAL) {
scope_size -= NJS_INDEX_GLOBAL_OFFSET;
parser->scope_size = scope_size;
- size = scope->values->items * sizeof(njs_value_t);
+ size = scope->values[0]->items * sizeof(njs_value_t);
nxt_thread_log_debug("SCOPE SIZE: %uz %uz", size, scope_size);
- p = memcpy(parser->local_scope, scope->values->start, size);
+ p = memcpy(parser->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)) {
}
+static void
+njs_generate_argument_closures(njs_parser_t *parser, njs_parser_node_t *node)
+{
+ nxt_uint_t n;
+ njs_index_t index;
+ njs_variable_t *var;
+ njs_vmcode_move_t *move;
+ nxt_lvlhsh_each_t lhe;
+
+ n = node->scope->argument_closures;
+
+ if (n == 0) {
+ return;
+ }
+
+ memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t));
+ lhe.proto = &njs_variables_hash_proto;
+
+ do {
+ var = nxt_lvlhsh_each(&node->scope->variables, &lhe);
+
+ if (var->argument != 0) {
+ index = njs_scope_index((var->argument - 1), NJS_SCOPE_ARGUMENTS);
+
+ njs_generate_code(parser, njs_vmcode_move_t, move);
+ move->code.operation = njs_vmcode_move;
+ move->code.operands = NJS_VMCODE_2OPERANDS;
+ move->code.retval = NJS_VMCODE_RETVAL;
+ move->dst = var->index;
+ move->src = index;
+
+ n--;
+ }
+
+ } while(n != 0);
+}
+
+
static nxt_int_t
njs_generate_return_statement(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node)
if (node->token == NJS_TOKEN_CATCH) {
/* A "try/catch" case. */
- catch_index = njs_variable_index(vm, node->left, NJS_NAME_DECLARATION);
+ catch_index = njs_variable_index(vm, node->left);
if (nxt_slow_path(catch_index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
if (node->left != NULL) {
/* A try/catch/finally case. */
- catch_index = njs_variable_index(vm, node->left->left,
- NJS_NAME_DECLARATION);
+ catch_index = njs_variable_index(vm, node->left->left);
if (nxt_slow_path(catch_index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
scope = scope->parent;
}
- value = nxt_array_add(scope->values, &njs_array_mem_proto,
+ value = nxt_array_add(scope->values[0], &njs_array_mem_proto,
vm->mem_cache_pool);
if (nxt_slow_path(value == NULL)) {
return NJS_INDEX_ERROR;
*value = njs_value_invalid;
- index = scope->next_index;
- scope->next_index += sizeof(njs_value_t);
+ index = scope->next_index[0];
+ scope->next_index[0] += sizeof(njs_value_t);
return index;
}
value = &args[1];
}
- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {
object = njs_object_value_alloc(vm, value, value->type);
if (nxt_slow_path(object == NULL)) {
return NXT_ERROR;
static njs_ret_t
njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type)
{
+ nxt_uint_t nesting;
nxt_array_t *values;
njs_parser_scope_t *scope, *parent;
+ nesting = 0;
+
+ if (type == NJS_SCOPE_FUNCTION) {
+
+ for (scope = parser->scope; scope != NULL; scope = scope->parent) {
+
+ if (scope->type == NJS_SCOPE_FUNCTION) {
+ nesting = scope->nesting + 1;
+
+ if (nesting <= NJS_MAX_NESTING) {
+ break;
+ }
+
+ nxt_alert(&vm->trace, NXT_LEVEL_ERROR, "SyntaxError: "
+ "The maximum function nesting level is \"%d\"",
+ NJS_MAX_NESTING);
+
+ return NXT_ERROR;
+ }
+ }
+ }
+
scope = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_scope_t));
if (nxt_slow_path(scope == NULL)) {
return NXT_ERROR;
scope->type = type;
- if (type == NJS_SCOPE_GLOBAL) {
- type += NJS_INDEX_GLOBAL_OFFSET;
+ 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->next_index = type;
+ scope->nesting = nesting;
+ scope->argument_closures = 0;
- scope->inclusive = 0;
+ nxt_queue_init(&scope->nested);
nxt_lvlhsh_init(&scope->variables);
+ nxt_lvlhsh_init(&scope->references);
values = NULL;
}
}
- scope->values = values;
+ scope->values[0] = values;
+ scope->values[1] = NULL;
parent = parser->scope;
+ scope->parent = parent;
+ parser->scope = scope;
if (parent != NULL) {
- parent->inclusive++;
+ nxt_queue_insert_tail(&parent->nested, &scope->link);
}
- scope->parent = parent;
- parser->scope = scope;
-
return NXT_OK;
}
scope = parser->scope;
parent = scope->parent;
-
-#if 0
- if (scope->inclusive == 0
- && scope->type == NJS_SCOPE_BLOCK
- && nxt_lvlhsh_is_empty(&scope->variables))
- {
- parent->inclusive--;
-
- nxt_mem_cache_free(vm->mem_cache_pool, scope);
- }
-#endif
-
parser->scope = parent;
}
return NJS_TOKEN_ERROR;
}
- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
}
}
- lambda->nargs = njs_index_size(index) / sizeof(njs_value_t) - 1;
+ lambda->nargs = njs_scope_offset(index) / sizeof(njs_value_t) - 1;
token = njs_parser_token(parser);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
name->token = NJS_TOKEN_NAME;
- ret = njs_variable_reference(vm, parser, name);
+ ret = njs_variable_reference(vm, parser, name, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
name->token = NJS_TOKEN_NAME;
- ret = njs_variable_reference(vm, parser, name);
+ ret = njs_variable_reference(vm, parser, name, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
return NJS_TOKEN_ERROR;
}
- var = njs_variable_add(vm, parser, NJS_VARIABLE_LET);
+ var = njs_variable_add(vm, parser, NJS_VARIABLE_CATCH);
if (nxt_slow_path(var == NULL)) {
return NJS_TOKEN_ERROR;
}
node->token = NJS_TOKEN_NAME;
- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 0);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
break;
}
- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 1);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
var->value.type = NJS_OBJECT;
var->value.data.truth = 1;
- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 1);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
var->value.type = NJS_FUNCTION;
var->value.data.truth = 1;
- ret = njs_variable_reference(vm, parser, node);
+ ret = njs_variable_reference(vm, parser, node, 1);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
((node)->token == NJS_TOKEN_NAME || (node)->token == NJS_TOKEN_PROPERTY)
-typedef struct njs_parser_scope_s njs_parser_scope_t;
-
struct njs_parser_scope_s {
- nxt_array_t *values; /* Array of njs_value_t. */
+ nxt_queue_link_t link;
+ nxt_queue_t nested;
- nxt_lvlhsh_t variables;
njs_parser_scope_t *parent;
- njs_index_t next_index;
- uint32_t inclusive;
+ nxt_lvlhsh_t variables;
+ nxt_lvlhsh_t references;
+
+ nxt_array_t *values[2]; /* Array of njs_value_t. */
+ njs_index_t next_index[2];
+
njs_scope_t type:8;
+ uint8_t nesting; /* 4 bits */
+ uint8_t argument_closures;
};
njs_token_t token:16;
uint8_t ctor:1; /* 1 bit */
uint8_t temporary; /* 1 bit */
+ uint8_t reference; /* 1 bit */
uint32_t token_line;
uint32_t variable_name_hash;
njs_token_t njs_parser_token(njs_parser_t *parser);
nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value);
njs_ret_t njs_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
- njs_parser_node_t *node);
-njs_variable_t *njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node,
- njs_name_reference_t reference);
-njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node,
- njs_name_reference_t reference);
+ njs_parser_node_t *node, nxt_bool_t reference);
+njs_variable_t *njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node);
+njs_index_t njs_variable_typeof(njs_vm_t *vm, njs_parser_node_t *node);
+njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node);
nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
u_char *njs_parser_trace_handler(nxt_trace_t *trace, nxt_trace_data_t *td,
u_char *start);
value = &args[1];
}
- if (vm->frame->ctor) {
+ if (vm->top_frame->ctor) {
object = njs_object_value_alloc(vm, value, value->type);
if (nxt_slow_path(object == NULL)) {
return NXT_ERROR;
#include <string.h>
+typedef struct {
+ nxt_lvlhsh_query_t lhq;
+ njs_variable_t *variable;
+ njs_parser_scope_t *scope;
+} njs_variable_scope_t;
+
+
+static njs_ret_t njs_variable_find(njs_vm_t *vm, njs_parser_node_t *node,
+ njs_variable_scope_t *vs);
static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, nxt_str_t *name,
njs_variable_type_t type);
}
-static const nxt_lvlhsh_proto_t njs_variables_hash_proto
+const nxt_lvlhsh_proto_t njs_variables_hash_proto
nxt_aligned(64) =
{
NXT_LVLHSH_DEFAULT,
}
+static nxt_int_t
+njs_reference_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ njs_parser_node_t *node;
+
+ node = data;
+
+ if (nxt_strstr_eq(&lhq->key, &node->u.variable_name)) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+const nxt_lvlhsh_proto_t njs_reference_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ 0,
+ njs_reference_hash_test,
+ njs_lvlhsh_alloc,
+ njs_lvlhsh_free,
+};
+
+
njs_ret_t
njs_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
- njs_parser_node_t *node)
+ njs_parser_node_t *node, nxt_bool_t reference)
{
- njs_ret_t ret;
+ njs_ret_t ret;
+ nxt_lvlhsh_query_t lhq;
ret = njs_name_copy(vm, &node->u.variable_name, &parser->lexer->text);
if (nxt_fast_path(ret == NXT_OK)) {
node->variable_name_hash = parser->lexer->key_hash;
node->scope = parser->scope;
+ node->reference = reference;
+
+ lhq.key_hash = node->variable_name_hash;
+ lhq.key = node->u.variable_name;
+ lhq.proto = &njs_reference_hash_proto;
+ lhq.replace = 0;
+ lhq.value = node;
+ lhq.pool = vm->mem_cache_pool;
+
+ ret = nxt_lvlhsh_insert(&parser->scope->references, &lhq);
+
+ if (nxt_slow_path(ret != NXT_ERROR)) {
+ ret = NXT_OK;
+ }
}
return ret;
}
-njs_variable_t *
-njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node,
- njs_name_reference_t reference)
+njs_ret_t
+njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope)
{
- nxt_array_t *values;
- njs_index_t index;
- njs_value_t *value;
+ njs_ret_t ret;
+ nxt_queue_t *nested;
njs_variable_t *var;
- njs_parser_scope_t *scope, *parent, *inclusive;
- nxt_lvlhsh_query_t lhq;
+ nxt_queue_link_t *lnk;
+ njs_parser_node_t *node;
+ nxt_lvlhsh_each_t lhe;
- lhq.key_hash = node->variable_name_hash;
- lhq.key = node->u.variable_name;
- lhq.proto = &njs_variables_hash_proto;
+ nested = &scope->nested;
- inclusive = NULL;
- scope = node->scope;
+ for (lnk = nxt_queue_first(nested);
+ lnk != nxt_queue_tail(nested);
+ lnk = nxt_queue_next(lnk))
+ {
+ scope = nxt_queue_link_data(lnk, njs_parser_scope_t, link);
- for ( ;; ) {
- if (nxt_lvlhsh_find(&scope->variables, &lhq) == NXT_OK) {
- var = lhq.value;
+ ret = njs_variables_scope_reference(vm, scope);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
- if (scope->type == NJS_SCOPE_SHIM) {
- scope = inclusive;
+ memset(&lhe, 0, sizeof(nxt_lvlhsh_each_t));
+ lhe.proto = &njs_variables_hash_proto;
- } 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;
- }
+ for ( ;; ) {
+ node = nxt_lvlhsh_each(&scope->references, &lhe);
+
+ if (node == NULL) {
+ break;
}
- goto found;
+ var = njs_variable_get(vm, node);
+ if (nxt_slow_path(var == NULL)) {
+ return NXT_ERROR;
+ }
}
+ }
- parent = scope->parent;
+ return NXT_OK;
+}
- if (parent == NULL) {
- /* A global scope. */
- break;
- }
- inclusive = scope;
- scope = parent;
+njs_index_t
+njs_variable_typeof(njs_vm_t *vm, njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ njs_variable_scope_t vs;
+
+ if (node->index != NJS_INDEX_NONE) {
+ return node->index;
}
- if (reference != NJS_NAME_TYPEOF) {
- goto not_found;
+ ret = njs_variable_find(vm, node, &vs);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return vs.variable->index;
}
- var = njs_variable_alloc(vm, &lhq.key, NJS_VARIABLE_TYPEOF);
- if (nxt_slow_path(var == NULL)) {
- return NULL;
+ return NJS_INDEX_NONE;
+}
+
+
+njs_index_t
+njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node)
+{
+ njs_variable_t *var;
+
+ if (node->index != NJS_INDEX_NONE) {
+ return node->index;
}
- var->index = NJS_INDEX_NONE;
+ var = njs_variable_get(vm, node);
- return var;
+ if (nxt_fast_path(var != NULL)) {
+ return var->index;
+ }
+
+ return NJS_INDEX_ERROR;
+}
+
+
+njs_variable_t *
+njs_variable_get(njs_vm_t *vm, njs_parser_node_t *node)
+{
+ nxt_int_t ret;
+ nxt_uint_t n;
+ nxt_array_t *values;
+ njs_index_t index;
+ njs_value_t *value;
+ njs_variable_t *var;
+ njs_variable_scope_t vs;
-found:
+ ret = njs_variable_find(vm, node, &vs);
- if (reference == NJS_NAME_REFERENCE && var->type == NJS_VARIABLE_TYPEOF) {
+ if (nxt_slow_path(ret != NXT_OK)) {
goto not_found;
}
+ n = (node->scope->nesting != vs.scope->nesting);
+
+ var = vs.variable;
index = var->index;
if (index != NJS_INDEX_NONE) {
- node->index = index;
- return var;
+
+ if (n == 0 || njs_scope_type(index) != NJS_SCOPE_ARGUMENTS) {
+ node->index = index;
+
+ return var;
+ }
+
+ vs.scope->argument_closures++;
+ index = (index >> NJS_SCOPE_SHIFT) + 1;
+
+ if (index > 255 || vs.scope->argument_closures == 0) {
+ nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
+ "InternalError: too many argument closures");
+
+ return NULL;
+ }
+
+ var->argument = index;
}
- if (reference == NJS_NAME_REFERENCE && var->type <= NJS_VARIABLE_LET) {
+ if (node->reference && var->type <= NJS_VARIABLE_LET) {
goto not_found;
}
- values = scope->values;
+ values = vs.scope->values[n];
if (values == NULL) {
values = nxt_array_create(4, sizeof(njs_value_t), &njs_array_mem_proto,
return NULL;
}
- scope->values = values;
+ vs.scope->values[n] = values;
}
value = nxt_array_add(values, &njs_array_mem_proto, vm->mem_cache_pool);
*value = njs_value_void;
}
- index = scope->next_index;
- scope->next_index += sizeof(njs_value_t);
+ index = vs.scope->next_index[n];
+ vs.scope->next_index[n] += sizeof(njs_value_t);
var->index = index;
node->index = index;
nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
"ReferenceError: \"%.*s\" is not defined",
- (int) lhq.key.length, lhq.key.start);
+ (int) vs.lhq.key.length, vs.lhq.key.start);
return NULL;
}
-njs_index_t
-njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node,
- njs_name_reference_t reference)
+static njs_ret_t
+njs_variable_find(njs_vm_t *vm, njs_parser_node_t *node,
+ njs_variable_scope_t *vs)
{
- njs_variable_t *var;
+ njs_parser_scope_t *scope, *parent, *previous;
- var = njs_variable_get(vm, node, reference);
+ vs->lhq.key_hash = node->variable_name_hash;
+ vs->lhq.key = node->u.variable_name;
+ vs->lhq.proto = &njs_variables_hash_proto;
- if (nxt_fast_path(var != NULL)) {
- return var->index;
- }
+ previous = NULL;
+ scope = node->scope;
- return NJS_INDEX_ERROR;
+ for ( ;; ) {
+ if (nxt_lvlhsh_find(&scope->variables, &vs->lhq) == NXT_OK) {
+ vs->variable = vs->lhq.value;
+
+ 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;
+ }
+ }
+
+ vs->scope = scope;
+
+ return NXT_OK;
+ }
+
+ parent = scope->parent;
+
+ if (parent == NULL) {
+ /* A global scope. */
+ vs->scope = scope;
+
+ return NXT_DECLINED;
+ }
+
+ previous = scope;
+ scope = parent;
+ }
}
typedef enum {
NJS_VARIABLE_CONST = 0,
NJS_VARIABLE_LET,
- NJS_VARIABLE_TYPEOF,
+ NJS_VARIABLE_CATCH,
NJS_VARIABLE_SHIM,
NJS_VARIABLE_VAR,
NJS_VARIABLE_FUNCTION,
} njs_variable_type_t;
-typedef enum {
- NJS_NAME_REFERENCE = 0,
- NJS_NAME_DECLARATION,
- NJS_NAME_TYPEOF,
-} njs_name_reference_t;
-
-
typedef struct {
nxt_str_t name;
+
njs_variable_type_t type:8; /* 3 bits */
+ uint8_t closure; /* 1 bit */
+ uint8_t argument;
njs_index_t index;
njs_value_t value;
#define njs_global_variable_value(vm, var) \
(njs_value_t *) ((u_char *) vm->global_scope \
- + njs_offset((var)->index) - NJS_INDEX_GLOBAL_OFFSET)
+ + njs_scope_offset((var)->index) - NJS_INDEX_GLOBAL_OFFSET)
njs_variable_t *njs_builtin_add(njs_vm_t *vm, njs_parser_t *parser);
njs_variable_t *njs_variable_add(njs_vm_t *vm, njs_parser_t *parser,
njs_variable_type_t type);
+njs_ret_t njs_variables_scope_reference(njs_vm_t *vm,
+ njs_parser_scope_t *scope);
njs_ret_t njs_name_copy(njs_vm_t *vm, nxt_str_t *dst, nxt_str_t *src);
nxt_str_t *njs_vm_export_functions(njs_vm_t *vm);
+extern const nxt_lvlhsh_proto_t njs_variables_hash_proto;
+
#endif /* _NJS_VARIABLE_H_INCLUDED_ */
static njs_ret_t njs_function_frame_create(njs_vm_t *vm, njs_value_t *value,
const njs_value_t *this, uintptr_t nargs, nxt_bool_t ctor);
static njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *value);
+static void njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
+ njs_native_frame_t *previous);
static njs_ret_t njs_vmcode_continuation(njs_vm_t *vm, njs_value_t *invld1,
njs_value_t *invld2);
static njs_native_frame_t *
if (ret == NXT_ERROR) {
for ( ;; ) {
- frame = (njs_frame_t *) vm->frame;
+ frame = (njs_frame_t *) vm->top_frame;
catch = frame->native.exception.catch;
if (catch != NULL) {
return NXT_ERROR;
}
- vm->frame = previous;
-
- /* GC: NJS_SCOPE_ARGUMENTS and NJS_SCOPE_FUNCTION. */
-
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
- vm->scopes[NJS_SCOPE_FUNCTION] = frame->prev_local;
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
+ njs_vm_scopes_restore(vm, frame, previous);
if (frame->native.size != 0) {
vm->stack_size -= frame->native.size;
njs_ret_t
njs_vmcode_function(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2)
{
+ size_t size;
+ nxt_uint_t n, nesting;
njs_function_t *function;
+ njs_function_lambda_t *lambda;
njs_vmcode_function_t *code;
- function = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_function_t));
+ code = (njs_vmcode_function_t *) vm->current;
+ lambda = code->lambda;
+ nesting = lambda->nesting;
- if (nxt_fast_path(function != NULL)) {
- function->object.shared_hash = vm->shared->function_prototype_hash;
- function->object.__proto__ =
- &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
- function->args_offset = 1;
+ size = sizeof(njs_function_t) + nesting * sizeof(njs_closure_t *);
- code = (njs_vmcode_function_t *) vm->current;
- function->u.lambda = code->lambda;
+ function = nxt_mem_cache_zalloc(vm->mem_cache_pool, size);
+ if (nxt_slow_path(function == NULL)) {
+ return NXT_ERROR;
+ }
- vm->retval.data.u.function = function;
- vm->retval.type = NJS_FUNCTION;
- vm->retval.data.truth = 1;
+ function->u.lambda = lambda;
+ function->object.shared_hash = vm->shared->function_prototype_hash;
+ function->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_FUNCTION].object;
+ function->args_offset = 1;
- return sizeof(njs_vmcode_function_t);
+ if (nesting != 0) {
+ function->closure = 1;
+
+ n = 0;
+
+ do {
+ /* GC: retain closure. */
+ function->closures[n] = vm->active_frame->closures[n];
+ n++;
+ } while (n < nesting);
}
- return NXT_ERROR;
+ vm->retval.data.u.function = function;
+ vm->retval.type = NJS_FUNCTION;
+ vm->retval.data.truth = 1;
+
+ return sizeof(njs_vmcode_function_t);
}
njs_continuation_t *cont;
njs_native_frame_t *frame;
- function = vm->frame->function;
+ frame = vm->top_frame;
+ function = frame->function;
if (!function->native) {
- (void) njs_function_call(vm, (njs_index_t) retval,
- sizeof(njs_vmcode_function_call_t));
- return 0;
+ ret = njs_function_call(vm, (njs_index_t) retval,
+ sizeof(njs_vmcode_function_call_t));
+
+ if (nxt_fast_path(ret != NJS_ERROR)) {
+ return 0;
+ }
+
+ return ret;
}
- args = vm->frame->arguments - function->args_offset;
- nargs = vm->frame->nargs;
+ args = frame->arguments;
+ nargs = frame->nargs;
ret = njs_normalize_args(vm, args, function->args_types, nargs);
if (ret != NJS_OK) {
* for NJS_APPLIED and NXT_AGAIN cases.
*/
if (ret == NXT_OK) {
- frame = vm->frame;
+ frame = vm->top_frame;
- vm->frame = njs_function_previous_frame(frame);
+ vm->top_frame = njs_function_previous_frame(frame);
(void) njs_function_frame_free(vm, frame);
/*
* If a retval is in a callee arguments scope it
* must be in the previous callee arguments scope.
*/
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = vm->frame->arguments;
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] =
+ vm->top_frame->arguments + function->args_offset;
retval = njs_vmcode_operand(vm, retval);
/*
{
njs_value_t *value;
njs_frame_t *frame;
- njs_value_t *args;
njs_native_frame_t *previous;
value = njs_vmcode_operand(vm, retval);
- frame = (njs_frame_t *) vm->frame;
+ frame = (njs_frame_t *) vm->top_frame;
if (frame->native.ctor) {
if (njs_is_object(value)) {
}
previous = njs_function_previous_frame(&frame->native);
- vm->frame = previous;
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = previous->arguments;
- vm->scopes[NJS_SCOPE_FUNCTION] = frame->prev_local;
- args = vm->scopes[NJS_SCOPE_ARGUMENTS];
- vm->scopes[NJS_SCOPE_ARGUMENTS] = frame->prev_arguments;
+ njs_vm_scopes_restore(vm, frame, previous);
/*
* If a retval is in a callee arguments scope it
vm->current = frame->return_address;
- /* GC: arguments and local. */
+ return njs_function_frame_free(vm, &frame->native);
+}
- njs_release(vm, &args[0]);
- return njs_function_frame_free(vm, &frame->native);
+static void
+njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
+ njs_native_frame_t *previous)
+{
+ nxt_uint_t n, nesting;
+ njs_value_t *args;
+ njs_function_t *function;
+
+ 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 = frame->native.function;
+
+ if (function->native) {
+ return;
+ }
+
+ if (function->closure) {
+ /* GC: release function closures. */
+ }
+
+ 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;
+
+ for (n = 0; n <= nesting; n++) {
+ vm->scopes[NJS_SCOPE_CLOSURE + n] = &frame->closures[n]->u.values;
+ }
+
+ while (n < NJS_MAX_NESTING) {
+ vm->scopes[NJS_SCOPE_CLOSURE + n] = NULL;
+ n++;
+ }
}
njs_ret_t ret;
nxt_bool_t skip;
njs_value_t *args, *retval;
+ njs_function_t *function;
njs_native_frame_t *frame;
njs_continuation_t *cont;
cont = njs_vm_continuation(vm);
- frame = vm->frame;
- args = frame->arguments - frame->function->args_offset;
+ frame = vm->top_frame;
+ args = frame->arguments;
if (cont->args_types != NULL) {
ret = njs_normalize_args(vm, args, cont->args_types, frame->nargs);
case NXT_OK:
- frame = vm->frame;
+ frame = vm->top_frame;
skip = frame->skip;
- vm->frame = njs_function_previous_frame(frame);
+ vm->top_frame = njs_function_previous_frame(frame);
/*
* If a retval is in a callee arguments scope it
* must be in the previous callee arguments scope.
*/
- vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = vm->frame->arguments;
+ args = vm->top_frame->arguments;
+ function = vm->top_frame->function;
+
+ if (function != NULL) {
+ args += function->args_offset;
+ }
+
+ vm->scopes[NJS_SCOPE_CALLEE_ARGUMENTS] = args;
if (!skip) {
retval = njs_vmcode_operand(vm, cont->retval);
{
njs_exception_t *e;
- if (vm->frame->exception.catch != NULL) {
+ if (vm->top_frame->exception.catch != NULL) {
e = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_exception_t));
if (nxt_slow_path(e == NULL)) {
return NXT_ERROR;
}
- *e = vm->frame->exception;
- vm->frame->exception.next = e;
+ *e = vm->top_frame->exception;
+ vm->top_frame->exception.next = e;
}
- vm->frame->exception.catch = vm->current + (njs_ret_t) offset;
+ vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
njs_set_invalid(value);
{
njs_exception_t *e;
- e = vm->frame->exception.next;
+ e = vm->top_frame->exception.next;
if (e == NULL) {
- vm->frame->exception.catch = NULL;
+ vm->top_frame->exception.catch = NULL;
} else {
- vm->frame->exception = *e;
+ vm->top_frame->exception = *e;
nxt_mem_cache_free(vm->mem_cache_pool, e);
}
return njs_vmcode_try_end(vm, exception, offset);
}
- vm->frame->exception.catch = vm->current + (njs_ret_t) offset;
+ vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
return sizeof(njs_vmcode_catch_t);
}
{
njs_native_frame_t *frame;
- frame = vm->frame;
+ frame = vm->top_frame;
/*
* The trap_scratch value is for results of "valueOf" and "toString"
njs_value_t *value;
njs_native_frame_t *frame;
- frame = vm->frame;
+ frame = vm->top_frame;
value = frame->trap_scratch.data.u.value;
njs_set_invalid(&frame->trap_scratch);
njs_ret_t ret;
njs_value_t *value;
- value = &vm->frame->trap_values[(uintptr_t) narg];
+ value = &vm->top_frame->trap_values[(uintptr_t) narg];
ret = njs_primitive_value(vm, value, 0);
njs_ret_t ret;
njs_value_t *value;
- value = &vm->frame->trap_values[(uintptr_t) narg];
+ value = &vm->top_frame->trap_values[(uintptr_t) narg];
ret = njs_primitive_value(vm, value, 1);
njs_ret_t ret;
njs_value_t *value;
- value = &vm->frame->trap_values[0];
+ value = &vm->top_frame->trap_values[0];
ret = njs_primitive_value(vm, value, 0);
njs_number_set(value, num);
}
- *vm->frame->trap_values[1].data.u.value = *value;
+ *vm->top_frame->trap_values[1].data.u.value = *value;
- vm->current = vm->frame->trap_restart;
- vm->frame->trap_restart = NULL;
+ vm->current = vm->top_frame->trap_restart;
+ vm->top_frame->trap_restart = NULL;
return 0;
}
njs_ret_t ret;
njs_value_t *value;
- value = &vm->frame->trap_values[0];
+ value = &vm->top_frame->trap_values[0];
ret = njs_primitive_value(vm, value, 1);
ret = njs_primitive_value_to_string(vm, value, value);
if (nxt_fast_path(ret == NXT_OK)) {
- *vm->frame->trap_values[1].data.u.value = *value;
+ *vm->top_frame->trap_values[1].data.u.value = *value;
- vm->current = vm->frame->trap_restart;
- vm->frame->trap_restart = NULL;
+ vm->current = vm->top_frame->trap_restart;
+ vm->top_frame->trap_restart = NULL;
}
}
};
if (!njs_is_primitive(value)) {
- retval = &vm->frame->trap_scratch;
+ retval = &vm->top_frame->trap_scratch;
if (!njs_is_primitive(retval)) {
vm->exception = &njs_exception_type_error;
ret = NXT_ERROR;
- if (njs_is_object(value) && vm->frame->trap_tries < 2) {
- hint ^= vm->frame->trap_tries++;
+ if (njs_is_object(value) && vm->top_frame->trap_tries < 2) {
+ hint ^= vm->top_frame->trap_tries++;
lhq.key_hash = hashes[hint];
lhq.key = names[hint];
njs_set_invalid(retval);
}
- vm->frame->trap_tries = 0;
+ vm->top_frame->trap_tries = 0;
return 1;
}
njs_native_frame_t *frame;
njs_vmcode_generic_t *vmcode;
- frame = vm->frame;
+ frame = vm->top_frame;
restart = frame->trap_restart;
frame->trap_restart = NULL;
vm->current = restart;
current = vm->current;
vm->current = (u_char *) value_to_string;
- njs_set_invalid(&vm->frame->trap_scratch);
- vm->frame->trap_values[0] = *value;
+ njs_set_invalid(&vm->top_frame->trap_scratch);
+ vm->top_frame->trap_values[0] = *value;
ret = njs_vmcode_interpreter(vm);
if (ret == NJS_STOP) {
ret = NXT_OK;
- *value = vm->frame->trap_values[0];
+ *value = vm->top_frame->trap_values[0];
}
vm->current = current;
{
njs_ret_t ret;
- ret = njs_primitive_value(vm, &vm->frame->trap_values[0], 1);
+ ret = njs_primitive_value(vm, &vm->top_frame->trap_values[0], 1);
if (nxt_fast_path(ret > 0)) {
return NJS_STOP;
#include <nxt_trace.h>
+#include <nxt_queue.h>
#include <nxt_regex.h>
typedef struct njs_regexp_pattern_s njs_regexp_pattern_t;
typedef struct njs_date_s njs_date_t;
typedef struct njs_extern_s njs_extern_t;
+typedef struct njs_frame_s njs_frame_t;
typedef struct njs_native_frame_s njs_native_frame_t;
typedef struct njs_property_next_s njs_property_next_t;
+typedef struct njs_parser_scope_s njs_parser_scope_t;
union njs_value_s {
};
+typedef struct {
+ union {
+ uint32_t count;
+ njs_value_t values;
+ } u;
+
+ njs_value_t values[1];
+} njs_closure_t;
+
+
#define NJS_ARGS_TYPES_MAX 5
struct njs_function_s {
uint8_t args_offset;
uint8_t continuation_size;
+ /* Function is a closure. */
+ uint8_t closure:1;
+
uint8_t native:1;
uint8_t ctor:1;
} u;
njs_value_t *bound;
+#if (NXT_SUNC)
+ njs_closure_t *closures[1];
+#else
+ njs_closure_t *closures[];
+#endif
};
typedef enum {
NJS_SCOPE_ABSOLUTE = 0,
- NJS_SCOPE_GLOBAL,
- NJS_SCOPE_FUNCTION,
- NJS_SCOPE_CALLEE_ARGUMENTS,
- NJS_SCOPE_ARGUMENTS,
- NJS_SCOPE_CLOSURE,
- NJS_SCOPE_PARENT_LOCAL,
- NJS_SCOPE_PARENT_ARGUMENTS,
- NJS_SCOPE_PARENT_CLOSURE,
+ NJS_SCOPE_GLOBAL = 1,
+ NJS_SCOPE_CALLEE_ARGUMENTS = 2,
+ /*
+ * The argument and local VM scopes should separate 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 used only on parsing phase.
} njs_scope_t;
-#define NJS_SCOPES (NJS_SCOPE_PARENT_CLOSURE + 1)
+/*
+ * The maximum possible function nesting level is (16 - NJS_SCOPE_CLOSURE),
+ * that is 11. The 5 is reasonable limit.
+ */
+#define NJS_MAX_NESTING 5
+
+#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_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)
};
-#define njs_scope_index(value) \
- ((njs_index_t) ((value) << NJS_SCOPE_SHIFT))
+#define njs_scope_index(value, type) \
+ ((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | (type)))
#define njs_global_scope_index(value) \
((njs_index_t) (((value) << NJS_SCOPE_SHIFT) | NJS_SCOPE_GLOBAL))
#define NJS_INDEX_DATE njs_global_scope_index(NJS_CONSTRUCTOR_DATE)
#define NJS_INDEX_GLOBAL_RETVAL njs_global_scope_index(NJS_CONSTRUCTOR_MAX)
-#define NJS_INDEX_GLOBAL_OFFSET njs_scope_index(NJS_CONSTRUCTOR_MAX + 1)
+#define NJS_INDEX_GLOBAL_OFFSET njs_scope_index(NJS_CONSTRUCTOR_MAX + 1, 0)
-#define njs_offset(index) \
+#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_offset(index)))
-
-
-#define njs_index_size(index) \
- njs_offset(index)
+ + njs_scope_offset(index)))
typedef struct {
void **external;
- njs_native_frame_t *frame;
+ njs_native_frame_t *top_frame;
+ njs_frame_t *active_frame;
const njs_value_t *exception;
return NJS_ERROR;
}
+ ret = njs_variables_scope_reference(vm, parser->scope);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NJS_ERROR;
+ }
+
*start = parser->lexer->start;
ret = njs_generate_scope(vm, parser, node);
memset(frame, 0, NJS_GLOBAL_FRAME_SIZE);
- nvm->frame = &frame->native;
+ nvm->top_frame = &frame->native;
+ nvm->active_frame = frame;
frame->native.size = size;
frame->native.free_size = size - (NJS_GLOBAL_FRAME_SIZE + scope_size);
ret = njs_function_frame(vm, function, this,
(njs_value_t *) args, nargs, 0);
-
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
current = vm->current;
vm->current = (u_char *) stop;
- (void) njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0);
+ ret = njs_function_call(vm, NJS_INDEX_GLOBAL_RETVAL, 0);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return ret;
+ }
ret = njs_vmcode_interpreter(vm);
{ nxt_string("(function f(a) { return (a > 1) ? a * f(a - 1) : 1 })(10)"),
nxt_string("3628800") },
+ /* Nested functions and closures. */
+
+ { nxt_string("function f() { var x = 4; "
+ "function g() { return x }; return g(); } f()"),
+ nxt_string("4") },
+
+ { nxt_string("function f(a) { function g(b) { return a + b } return g }"
+ "var k = f('a'); k('b')"),
+ nxt_string("ab") },
+
+ { nxt_string("function f(a) { return function(b) { return a + b } }"
+ "var k = f('a'); k('b')"),
+ nxt_string("ab") },
+
+ { nxt_string("function f(a) { return function(b) { return a + b } }"
+ "var k = f('a'), m = f('b'); k('c') + m('d')"),
+ nxt_string("acbd") },
+
+ { nxt_string("function f(a) { return "
+ "function(b) { return function(c) { return a + b + c } } }"
+ "var g = f('a'), k = g('b'), m = g('c'); k('d') + m('e')"),
+ nxt_string("abdace") },
+
+ { nxt_string("function f(a) {"
+ "function g() { return a }; return g; }"
+ "var y = f(4); y()"),
+ nxt_string("4") },
+
+ { nxt_string("function f() { var x = 4; "
+ "return function() { return x } }"
+ "var y = f(); y()"),
+ nxt_string("4") },
+
/* Recursive fibonacci. */
{ nxt_string("function fibo(n) {"
nxt_string("[object Function]1") },
#endif
-#if 0
{ nxt_string("function f() {}; function g() { return f }; g()"),
nxt_string("[object Function]") },
-#endif
{ nxt_string("function f(a) { return this+a }; var a = f; a.call('0', 1)"),
nxt_string("01") },