in loops.
njs_parser_node_t *node);
static nxt_int_t njs_generate_for_in_statement(njs_vm_t *vm,
njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_noinline nxt_int_t njs_generate_start_block(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_block_type_t type, const nxt_str_t *label);
+static nxt_noinline void njs_generate_patch_loop_continuation(njs_vm_t *vm,
+ njs_parser_t *parser);
+static nxt_noinline void njs_generate_patch_block_exit(njs_vm_t *vm,
+ njs_parser_t *parser);
+static nxt_int_t njs_generate_continue_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_break_statement(njs_vm_t *vm,
+ njs_parser_t *parser, njs_parser_node_t *node);
static nxt_int_t njs_generate_statement(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node);
static nxt_int_t njs_generate_children(njs_vm_t *vm, njs_parser_t *parser,
nxt_inline nxt_bool_t njs_generator_is_constant(njs_parser_node_t *node);
+static const nxt_str_t no_label = { 0, NULL };
+
+
static nxt_int_t
njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
{
case NJS_TOKEN_FOR_IN:
return njs_generate_for_in_statement(vm, parser, node);
+ case NJS_TOKEN_CONTINUE:
+ return njs_generate_continue_statement(vm, parser, node);
+
+ case NJS_TOKEN_BREAK:
+ return njs_generate_break_statement(vm, parser, node);
+
case NJS_TOKEN_STATEMENT:
return njs_generate_statement(vm, parser, node);
/* The loop body. */
+ ret = njs_generate_start_block(vm, parser, NJS_PARSER_LOOP, &no_label);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
loop = parser->code_end;
ret = njs_generator(vm, parser, node->left);
/* The loop condition. */
+ njs_generate_patch_loop_continuation(vm, parser);
+
jump->offset = parser->code_end - (u_char *) jump;
condition = node->right;
cond_jump->offset = loop - (u_char *) cond_jump;
cond_jump->cond = condition->index;
+ njs_generate_patch_block_exit(vm, parser);
+
return njs_generator_node_index_release(vm, parser, condition);
}
/* The loop body. */
+ ret = njs_generate_start_block(vm, parser, NJS_PARSER_LOOP, &no_label);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
loop = parser->code_end;
ret = njs_generator(vm, parser, node->left);
/* The loop condition. */
+ njs_generate_patch_loop_continuation(vm, parser);
+
condition = node->right;
ret = njs_generator(vm, parser, condition);
cond_jump->offset = loop - (u_char *) cond_jump;
cond_jump->cond = condition->index;
+ njs_generate_patch_block_exit(vm, parser);
+
return njs_generator_node_index_release(vm, parser, condition);
}
njs_vmcode_jump_t *jump;
njs_vmcode_cond_jump_t *cond_jump;
+ ret = njs_generate_start_block(vm, parser, NJS_PARSER_LOOP, &no_label);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
jump = NULL;
/* The loop initialization. */
/* The loop update. */
+ njs_generate_patch_loop_continuation(vm, parser);
+
update = node->right;
ret = njs_generator(vm, parser, update);
cond_jump->offset = loop - (u_char *) cond_jump;
cond_jump->cond = condition->index;
+ njs_generate_patch_block_exit(vm, parser);
+
return njs_generator_node_index_release(vm, parser, condition);
}
jump->code.retval = NJS_VMCODE_NO_RETVAL;
jump->offset = loop - (u_char *) jump;
+ njs_generate_patch_block_exit(vm, parser);
+
return NXT_OK;
}
njs_vmcode_prop_next_t *prop_next;
njs_vmcode_prop_foreach_t *prop_foreach;
+ ret = njs_generate_start_block(vm, parser, NJS_PARSER_LOOP, &no_label);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
/* The object. */
foreach = node->left;
/* The loop iterator. */
+ njs_generate_patch_loop_continuation(vm, parser);
+
prop_foreach->offset = parser->code_end - (u_char *) prop_foreach;
ret = njs_generator(vm, parser, node->left->left);
prop_next->next = index;
prop_next->offset = loop - (u_char *) prop_next;
+ njs_generate_patch_block_exit(vm, parser);
+
/*
* Release object and iterator indexes: an object can be a function result
* or a property of another object and an iterator can be given with "let".
}
+static nxt_noinline nxt_int_t
+njs_generate_start_block(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_block_type_t type, const nxt_str_t *label)
+{
+ njs_parser_block_t *block;
+
+ block = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_block_t));
+
+ if (nxt_fast_path(block != NULL)) {
+ block->next = parser->block;
+ parser->block = block;
+
+ block->type = type;
+ block->label = *label;
+ block->continuation = NULL;
+ block->exit = NULL;
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_noinline void
+njs_generate_patch_loop_continuation(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_parser_block_t *block;
+ njs_parser_patch_t *patch, *next;
+
+ block = parser->block;
+
+ for (patch = block->continuation; patch != NULL; patch = next) {
+ *patch->address += parser->code_end - patch->address;
+ next = patch->next;
+
+ nxt_mem_cache_free(vm->mem_cache_pool, patch);
+ }
+}
+
+
+static nxt_noinline void
+njs_generate_patch_block_exit(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_parser_block_t *block;
+ njs_parser_patch_t *patch, *next;
+
+ block = parser->block;
+ parser->block = block->next;
+
+ for (patch = block->exit; patch != NULL; patch = next) {
+ *patch->address += parser->code_end - patch->address;
+ next = patch->next;
+
+ nxt_mem_cache_free(vm->mem_cache_pool, patch);
+ }
+
+ nxt_mem_cache_free(vm->mem_cache_pool, block);
+}
+
+
+static nxt_int_t
+njs_generate_continue_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ njs_vmcode_jump_t *jump;
+ njs_parser_patch_t *patch;
+
+ if (parser->block == NULL) {
+ vm->exception = &njs_exception_syntax_error;
+ return NXT_ERROR;
+ }
+
+ /* TODO: LABEL */
+
+ if (parser->block->type != NJS_PARSER_LOOP) {
+ vm->exception = &njs_exception_syntax_error;
+ return NXT_ERROR;
+ }
+
+ patch = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_patch_t));
+
+ if (nxt_fast_path(patch != NULL)) {
+ patch->next = parser->block->continuation;
+ parser->block->continuation = patch;
+
+ njs_generate_code(parser, njs_vmcode_jump_t, jump);
+ jump->code.operation = njs_vmcode_jump;
+ jump->code.operands = NJS_VMCODE_NO_OPERAND;
+ jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ jump->offset = offsetof(njs_vmcode_jump_t, offset);
+
+ patch->address = (u_char *) &jump->offset;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_int_t
+njs_generate_break_statement(njs_vm_t *vm, njs_parser_t *parser,
+ njs_parser_node_t *node)
+{
+ njs_vmcode_jump_t *jump;
+ njs_parser_patch_t *patch;
+
+ if (parser->block == NULL) {
+ vm->exception = &njs_exception_syntax_error;
+ return NXT_ERROR;
+ }
+
+ /* TODO: LABEL: loop and switch may have label, block must have label. */
+
+ patch = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_patch_t));
+
+ if (nxt_fast_path(patch != NULL)) {
+ patch->next = parser->block->exit;
+ parser->block->exit = patch;
+
+ njs_generate_code(parser, njs_vmcode_jump_t, jump);
+ jump->code.operation = njs_vmcode_jump;
+ jump->code.operands = NJS_VMCODE_NO_OPERAND;
+ jump->code.retval = NJS_VMCODE_NO_RETVAL;
+ jump->offset = offsetof(njs_vmcode_jump_t, offset);
+
+ patch->address = (u_char *) &jump->offset;
+ }
+
+ return NXT_OK;
+}
+
+
static nxt_int_t
njs_generate_statement(njs_vm_t *vm, njs_parser_t *parser,
njs_parser_node_t *node)
* is treated as a single expiression.
*/
-static njs_token_t njs_parser_statement_link(njs_vm_t *vm, njs_parser_t *parser,
- njs_token_t token);
+static njs_token_t njs_parser_statement_chain(njs_vm_t *vm,
+ njs_parser_t *parser, njs_token_t token);
static njs_token_t njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token);
static njs_token_t njs_parser_block(njs_vm_t *vm, njs_parser_t *parser);
static njs_token_t njs_parser_for_statement(njs_vm_t *vm, njs_parser_t *parser);
static njs_token_t njs_parser_for_in_statement(njs_vm_t *vm,
njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_continue_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
+static njs_token_t njs_parser_break_statement(njs_vm_t *vm,
+ njs_parser_t *parser);
static njs_token_t njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser);
static njs_token_t njs_parser_try_block(njs_vm_t *vm, njs_parser_t *parser);
static njs_token_t njs_parser_throw_statement(njs_vm_t *vm,
while (token != NJS_TOKEN_END) {
- token = njs_parser_statement_link(vm, parser, token);
+ token = njs_parser_statement_chain(vm, parser, token);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
return NULL;
}
static njs_token_t
-njs_parser_statement_link(njs_vm_t *vm, njs_parser_t *parser,
+njs_parser_statement_chain(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token)
{
njs_parser_node_t *node, *last;
case NJS_TOKEN_FOR:
return njs_parser_for_statement(vm, parser);
+ case NJS_TOKEN_CONTINUE:
+ return njs_parser_continue_statement(vm, parser);
+
+ case NJS_TOKEN_BREAK:
+ return njs_parser_break_statement(vm, parser);
+
case NJS_TOKEN_TRY:
return njs_parser_try_statement(vm, parser);
parser->node = NULL;
while (token != NJS_TOKEN_CLOSE_BRACE) {
- token = njs_parser_statement_link(vm, parser, token);
+ token = njs_parser_statement_chain(vm, parser, token);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
return token;
}
}
+static njs_token_t
+njs_parser_continue_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_CONTINUE;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_jump_t);
+
+ token = njs_lexer_token(parser->lexer);
+
+ switch (token) {
+
+ case NJS_TOKEN_SEMICOLON:
+ case NJS_TOKEN_LINE_END:
+ return njs_parser_token(parser);
+
+ case NJS_TOKEN_CLOSE_BRACE:
+ case NJS_TOKEN_END:
+ return token;
+
+ default:
+ /* TODO: LABEL */
+ return NJS_TOKEN_ILLEGAL;
+ }
+}
+
+
+static njs_token_t
+njs_parser_break_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+ njs_token_t token;
+ njs_parser_node_t *node;
+
+ node = njs_parser_node_alloc(vm);
+ if (nxt_slow_path(node == NULL)) {
+ return NJS_TOKEN_ERROR;
+ }
+
+ node->token = NJS_TOKEN_BREAK;
+ parser->node = node;
+ parser->code_size += sizeof(njs_vmcode_jump_t);
+
+ token = njs_lexer_token(parser->lexer);
+
+ switch (token) {
+
+ case NJS_TOKEN_SEMICOLON:
+ case NJS_TOKEN_LINE_END:
+ return njs_parser_token(parser);
+
+ case NJS_TOKEN_CLOSE_BRACE:
+ case NJS_TOKEN_END:
+ return token;
+
+ default:
+ /* TODO: LABEL */
+ return NJS_TOKEN_ILLEGAL;
+ }
+}
+
+
static njs_token_t
njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser)
{
nxt_mem_cache_zalloc((vm)->mem_cache_pool, sizeof(njs_parser_node_t))
+typedef struct njs_parser_patch_s njs_parser_patch_t;
+
+struct njs_parser_patch_s {
+ u_char *address;
+ njs_parser_patch_t *next;
+};
+
+
+typedef enum {
+ NJS_PARSER_BLOCK = 0,
+ NJS_PARSER_LOOP,
+ NJS_PARSER_SWITCH,
+} njs_parser_block_type_t;
+
+typedef struct njs_parser_block_s njs_parser_block_t;
+
+struct njs_parser_block_s {
+ njs_parser_block_type_t type; /* 2 bits */
+ nxt_str_t label;
+ njs_parser_patch_t *continuation;
+ njs_parser_patch_t *exit;
+ njs_parser_block_t *next;
+};
+
+
struct njs_parser_s {
njs_lexer_t *lexer;
njs_parser_node_t *node;
/* Vector of njs_variable_t. */
nxt_array_t *arguments;
+ njs_parser_block_t *block;
nxt_lvlhsh_t variables_hash;
{ nxt_string("a = 3; if (true) if (false); else; a = 2; a"),
nxt_string("2") },
+ /* continue. */
+
+ { nxt_string("continue"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("do continue while (false)"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("do continue; while (false)"),
+ nxt_string("undefined") },
+
+ { nxt_string("do { continue } while (false)"),
+ nxt_string("undefined") },
+
+ { nxt_string("var i = 0; do if (i++ > 9) continue; while (i < 100); i"),
+ nxt_string("100") },
+
+ { nxt_string("while (false) continue"),
+ nxt_string("undefined") },
+
+ { nxt_string("while (false) continue;"),
+ nxt_string("undefined") },
+
+ { nxt_string("while (false) { continue }"),
+ nxt_string("undefined") },
+
+ { nxt_string("var i = 0; while (i < 100) if (i++ > 9) continue; i"),
+ nxt_string("100") },
+
+ { nxt_string("for ( ;null; ) continue"),
+ nxt_string("undefined") },
+
+ { nxt_string("for ( ;null; ) continue;"),
+ nxt_string("undefined") },
+
+ { nxt_string("for ( ;null; ) { continue }"),
+ nxt_string("undefined") },
+
+ { nxt_string("for (i = 0; i < 100; i++) if (i > 9) continue; i"),
+ nxt_string("100") },
+
+ { nxt_string("var a = []; for (i in a) continue"),
+ nxt_string("undefined") },
+
+ { nxt_string("var a = []; for (i in a) continue;"),
+ nxt_string("undefined") },
+
+ { nxt_string("var a = []; for (i in a) { continue }"),
+ nxt_string("undefined") },
+
+ { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+ "for (i in a) { if (a[i] > 4) continue; else s += a[i] } s"),
+ nxt_string("10") },
+
+ { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+ "for (i in a) { if (a[i] > 4) continue; s += a[i] } s"),
+ nxt_string("10") },
+
+ /* break. */
+
+ { nxt_string("break"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("do break while (true)"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("do break; while (true)"),
+ nxt_string("undefined") },
+
+ { nxt_string("do { break } while (true)"),
+ nxt_string("undefined") },
+
+ { nxt_string("var i = 0; do if (i++ > 9) break; while (i < 100); i"),
+ nxt_string("11") },
+
+ { nxt_string("while (true) break"),
+ nxt_string("undefined") },
+
+ { nxt_string("while (true) break;"),
+ nxt_string("undefined") },
+
+ { nxt_string("while (true) { break }"),
+ nxt_string("undefined") },
+
+ { nxt_string("var i = 0; while (i < 100) if (i++ > 9) break; i"),
+ nxt_string("11") },
+
+ { nxt_string("for ( ;; ) break"),
+ nxt_string("undefined") },
+
+ { nxt_string("for ( ;; ) break;"),
+ nxt_string("undefined") },
+
+ { nxt_string("for ( ;; ) { break }"),
+ nxt_string("undefined") },
+
+ { nxt_string("for (i = 0; i < 100; i++) if (i > 9) break; i"),
+ nxt_string("10") },
+
+ { nxt_string("var a = []; for (i in a) break"),
+ nxt_string("undefined") },
+
+ { nxt_string("var a = []; for (i in a) break;"),
+ nxt_string("undefined") },
+
+ { nxt_string("var a = []; for (i in a) { break }"),
+ nxt_string("undefined") },
+
+ { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+ "for (i in a) { if (a[i] > 4) break; else s += a[i] } s"),
+ nxt_string("10") },
+
+ { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+ "for (i in a) { if (a[i] > 4) break; s += a[i] } s"),
+ nxt_string("10") },
+
+#if 0
+ { nxt_string("var a = [1,2,3,4,5]; var s = 0;"
+ "for (i in a) if (a[i] > 4) break; s += a[i] } s"),
+ nxt_string("segfault") },
+#endif
+
+ /**/
+
{ nxt_string("for (i = 0; i < 10; i++) { i += 1 } i"),
nxt_string("10") },