From 4de9298c33203236a5bc2243f6065f2af7c8dee8 Mon Sep 17 00:00:00 2001 From: Vadim Zhestikov Date: Tue, 25 Oct 2022 06:43:10 -0700 Subject: [PATCH] Computed goto support added to vmcode. --- auto/clang | 19 + auto/computed_goto | 25 + auto/help | 4 + auto/options | 4 + auto/summary | 5 + configure | 1 + src/njs_clang.h | 8 + src/njs_disassembler.c | 2 - src/njs_parser.c | 2 +- src/njs_vmcode.c | 2221 ++++++++++++++++++++++++++-------------- src/njs_vmcode.h | 29 +- 11 files changed, 1542 insertions(+), 778 deletions(-) create mode 100644 auto/computed_goto diff --git a/auto/clang b/auto/clang index 4f4d1fe9..270d6e59 100644 --- a/auto/clang +++ b/auto/clang @@ -142,6 +142,25 @@ njs_feature_test="struct __attribute__((packed)) s { char v; }; . auto/feature +njs_feature="GCC __attribute__ fallthrough" +njs_feature_name=NJS_HAVE_GCC_ATTRIBUTE_FALLTHROUGH +njs_feature_run=no +njs_feature_path= +njs_feature_libs= +njs_feature_test="int main(int argc, char *argv[]) { + switch (argc) { + case 0: + argc++; + __attribute__((fallthrough)); + default: + argc++; + } + + return argc; + }" +. auto/feature + + njs_feature="Address sanitizer" njs_feature_name=NJS_HAVE_ADDRESS_SANITIZER njs_feature_run=no diff --git a/auto/computed_goto b/auto/computed_goto new file mode 100644 index 00000000..e68fdc50 --- /dev/null +++ b/auto/computed_goto @@ -0,0 +1,25 @@ + +# Copyright (C) Vadim Zhestikov +# Copyright (C) NGINX, Inc. + + +NJS_HAVE_COMPUTED_GOTO=NO + + +if [ $NJS_TRY_GOTO = YES ]; then + + njs_feature="Computed goto" + njs_feature_name=NJS_HAVE_COMPUTED_GOTO + njs_feature_run=no + njs_feature_incs= + njs_feature_libs= + njs_feature_test="int main(void) { + void *ptr; + ptr = &&label; + goto *ptr; + label: + return 0; + }" + . auto/feature + +fi diff --git a/auto/help b/auto/help index fcaa354b..9e248a1b 100644 --- a/auto/help +++ b/auto/help @@ -27,6 +27,10 @@ default: "$NJS_LD_OPT" When this option is enabled only PCRE library is discovered. + --no-goto disables computed goto discovery. + When this option is enabled 'switch' statement + will be always used in instead of computed goto. + --no-openssl disables OpenSSL discovery. When this option is enabled OpenSSL dependant code is not built as a part of libnjs.a. diff --git a/auto/options b/auto/options index 386fd0ac..3f6ae650 100644 --- a/auto/options +++ b/auto/options @@ -20,6 +20,8 @@ NJS_OPENSSL=YES NJS_PCRE=YES NJS_TRY_PCRE2=YES +NJS_TRY_GOTO=YES + NJS_CONFIGURE_OPTIONS= for njs_option @@ -50,6 +52,8 @@ do --no-pcre) NJS_PCRE=NO ;; --no-pcre2) NJS_TRY_PCRE2=NO ;; + --no-goto) NJS_TRY_GOTO=NO ;; + --help) . auto/help exit 0 diff --git a/auto/summary b/auto/summary index 8d17807b..46c9deb7 100644 --- a/auto/summary +++ b/auto/summary @@ -22,6 +22,11 @@ if [ $NJS_HAVE_OPENSSL = YES ]; then echo " + using OpenSSL library: $NJS_OPENSSL_LIB" fi +if [ $NJS_HAVE_COMPUTED_GOTO = YES ]; then + echo " + using computed goto" +fi + + echo echo " njs build dir: $NJS_BUILD_DIR" echo " njs CLI: $NJS_BUILD_DIR/njs" diff --git a/configure b/configure index 63583d17..8fc71f33 100755 --- a/configure +++ b/configure @@ -46,6 +46,7 @@ NJS_LIB_AUX_LIBS= . auto/memalign . auto/getrandom . auto/stat +. auto/computed_goto . auto/explicit_bzero . auto/pcre . auto/readline diff --git a/src/njs_clang.h b/src/njs_clang.h index a13e13dd..614b5090 100644 --- a/src/njs_clang.h +++ b/src/njs_clang.h @@ -146,6 +146,14 @@ njs_leading_zeros64(uint64_t x) #endif +#if (NJS_HAVE_GCC_ATTRIBUTE_FALLTHROUGH) +#define NJS_FALLTHROUGH __attribute__((fallthrough)) + +#else +#define NJS_FALLTHROUGH +#endif + + #if (NJS_HAVE_GCC_ATTRIBUTE_MALLOC) #define NJS_MALLOC_LIKE __attribute__((__malloc__)) diff --git a/src/njs_disassembler.c b/src/njs_disassembler.c index 03a83e8c..25d30679 100644 --- a/src/njs_disassembler.c +++ b/src/njs_disassembler.c @@ -23,8 +23,6 @@ static njs_code_name_t code_names[] = { njs_str("OBJECT ") }, { NJS_VMCODE_FUNCTION, sizeof(njs_vmcode_function_t), njs_str("FUNCTION ") }, - { NJS_VMCODE_THIS, sizeof(njs_vmcode_this_t), - njs_str("THIS ") }, { NJS_VMCODE_ARGUMENTS, sizeof(njs_vmcode_arguments_t), njs_str("ARGUMENTS ") }, { NJS_VMCODE_REGEXP, sizeof(njs_vmcode_regexp_t), diff --git a/src/njs_parser.c b/src/njs_parser.c index 2b94d3d4..66d7c7c8 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -4527,7 +4527,7 @@ njs_parser_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_COMMA, - NJS_VMCODE_NOP, + 0, njs_parser_expression_comma); } diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index cab230f5..033177f4 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -108,7 +108,6 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, void *promise_cap, njs_vmcode_variable_t *var; njs_vmcode_prop_get_t *get; njs_vmcode_prop_set_t *set; - njs_vmcode_operation_t op; njs_vmcode_prop_next_t *pnext; njs_vmcode_test_jump_t *test_jump; njs_vmcode_equal_jump_t *equal; @@ -121,980 +120,1694 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, void *promise_cap, njs_vmcode_debug(vm, pc, "ENTER"); -next: +#if !defined(NJS_HAVE_COMPUTED_GOTO) + #define SWITCH(op) switch (op) + #define CASE(op) case op + #define BREAK pc += ret; NEXT + + #define NEXT vmcode = (njs_vmcode_generic_t *) pc; \ + goto next + + #define NEXT_LBL next: + #define FALLTHROUGH NJS_FALLTHROUGH + +#else + #define SWITCH(op) goto *switch_tbl[(uint8_t) op]; + #define CASE(op) case_ ## op + #define BREAK pc += ret; NEXT + + #define NEXT vmcode = (njs_vmcode_generic_t *) pc; \ + SWITCH (vmcode->code.operation) + + #define NEXT_LBL + #define FALLTHROUGH + + #define NJS_GOTO_ROW(name) [ (uint8_t) name ] = &&case_ ## name + + static const void * const switch_tbl[NJS_VMCODES] = { + + NJS_GOTO_ROW(NJS_VMCODE_PUT_ARG), + NJS_GOTO_ROW(NJS_VMCODE_STOP), + NJS_GOTO_ROW(NJS_VMCODE_JUMP), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_SET), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_ACCESSOR), + NJS_GOTO_ROW(NJS_VMCODE_IF_TRUE_JUMP), + NJS_GOTO_ROW(NJS_VMCODE_IF_FALSE_JUMP), + NJS_GOTO_ROW(NJS_VMCODE_IF_EQUAL_JUMP), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_INIT), + NJS_GOTO_ROW(NJS_VMCODE_RETURN), + NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_COPY), + NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_FRAME), + NJS_GOTO_ROW(NJS_VMCODE_METHOD_FRAME), + NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_CALL), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_NEXT), + NJS_GOTO_ROW(NJS_VMCODE_ARGUMENTS), + NJS_GOTO_ROW(NJS_VMCODE_PROTO_INIT), + NJS_GOTO_ROW(NJS_VMCODE_TO_PROPERTY_KEY), + NJS_GOTO_ROW(NJS_VMCODE_TO_PROPERTY_KEY_CHK), + NJS_GOTO_ROW(NJS_VMCODE_SET_FUNCTION_NAME), + NJS_GOTO_ROW(NJS_VMCODE_IMPORT), + NJS_GOTO_ROW(NJS_VMCODE_AWAIT), + NJS_GOTO_ROW(NJS_VMCODE_TRY_START), + NJS_GOTO_ROW(NJS_VMCODE_THROW), + NJS_GOTO_ROW(NJS_VMCODE_TRY_BREAK), + NJS_GOTO_ROW(NJS_VMCODE_TRY_CONTINUE), + NJS_GOTO_ROW(NJS_VMCODE_TRY_END), + NJS_GOTO_ROW(NJS_VMCODE_CATCH), + NJS_GOTO_ROW(NJS_VMCODE_FINALLY), + NJS_GOTO_ROW(NJS_VMCODE_LET), + NJS_GOTO_ROW(NJS_VMCODE_LET_UPDATE), + NJS_GOTO_ROW(NJS_VMCODE_INITIALIZATION_TEST), + NJS_GOTO_ROW(NJS_VMCODE_NOT_INITIALIZED), + NJS_GOTO_ROW(NJS_VMCODE_ASSIGNMENT_ERROR), + NJS_GOTO_ROW(NJS_VMCODE_ERROR), + NJS_GOTO_ROW(NJS_VMCODE_MOVE), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_GET), + NJS_GOTO_ROW(NJS_VMCODE_INCREMENT), + NJS_GOTO_ROW(NJS_VMCODE_POST_INCREMENT), + NJS_GOTO_ROW(NJS_VMCODE_DECREMENT), + NJS_GOTO_ROW(NJS_VMCODE_POST_DECREMENT), + NJS_GOTO_ROW(NJS_VMCODE_TRY_RETURN), + NJS_GOTO_ROW(NJS_VMCODE_GLOBAL_GET), + NJS_GOTO_ROW(NJS_VMCODE_LESS), + NJS_GOTO_ROW(NJS_VMCODE_GREATER), + NJS_GOTO_ROW(NJS_VMCODE_LESS_OR_EQUAL), + NJS_GOTO_ROW(NJS_VMCODE_GREATER_OR_EQUAL), + NJS_GOTO_ROW(NJS_VMCODE_ADDITION), + NJS_GOTO_ROW(NJS_VMCODE_EQUAL), + NJS_GOTO_ROW(NJS_VMCODE_NOT_EQUAL), + NJS_GOTO_ROW(NJS_VMCODE_SUBSTRACTION), + NJS_GOTO_ROW(NJS_VMCODE_MULTIPLICATION), + NJS_GOTO_ROW(NJS_VMCODE_EXPONENTIATION), + NJS_GOTO_ROW(NJS_VMCODE_DIVISION), + NJS_GOTO_ROW(NJS_VMCODE_REMAINDER), + NJS_GOTO_ROW(NJS_VMCODE_BITWISE_AND), + NJS_GOTO_ROW(NJS_VMCODE_BITWISE_OR), + NJS_GOTO_ROW(NJS_VMCODE_BITWISE_XOR), + NJS_GOTO_ROW(NJS_VMCODE_LEFT_SHIFT), + NJS_GOTO_ROW(NJS_VMCODE_RIGHT_SHIFT), + NJS_GOTO_ROW(NJS_VMCODE_UNSIGNED_RIGHT_SHIFT), + NJS_GOTO_ROW(NJS_VMCODE_OBJECT_COPY), + NJS_GOTO_ROW(NJS_VMCODE_TEMPLATE_LITERAL), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_IN), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_DELETE), + NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_FOREACH), + NJS_GOTO_ROW(NJS_VMCODE_STRICT_EQUAL), + NJS_GOTO_ROW(NJS_VMCODE_STRICT_NOT_EQUAL), + NJS_GOTO_ROW(NJS_VMCODE_TEST_IF_TRUE), + NJS_GOTO_ROW(NJS_VMCODE_TEST_IF_FALSE), + NJS_GOTO_ROW(NJS_VMCODE_COALESCE), + NJS_GOTO_ROW(NJS_VMCODE_UNARY_PLUS), + NJS_GOTO_ROW(NJS_VMCODE_UNARY_NEGATION), + NJS_GOTO_ROW(NJS_VMCODE_BITWISE_NOT), + NJS_GOTO_ROW(NJS_VMCODE_LOGICAL_NOT), + NJS_GOTO_ROW(NJS_VMCODE_OBJECT), + NJS_GOTO_ROW(NJS_VMCODE_ARRAY), + NJS_GOTO_ROW(NJS_VMCODE_FUNCTION), + NJS_GOTO_ROW(NJS_VMCODE_REGEXP), + NJS_GOTO_ROW(NJS_VMCODE_INSTANCE_OF), + NJS_GOTO_ROW(NJS_VMCODE_TYPEOF), + NJS_GOTO_ROW(NJS_VMCODE_VOID), + NJS_GOTO_ROW(NJS_VMCODE_DELETE), + NJS_GOTO_ROW(NJS_VMCODE_DEBUGGER), + }; + +#endif + + vmcode = (njs_vmcode_generic_t *) pc; + +NEXT_LBL; + + SWITCH (vmcode->code.operation) { + + CASE (NJS_VMCODE_MOVE): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand2, value1); + njs_vmcode_operand(vm, vmcode->operand1, retval); + *retval = *value1; + + pc += sizeof(njs_vmcode_move_t); + NEXT; + + CASE (NJS_VMCODE_PROPERTY_GET): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + get = (njs_vmcode_prop_get_t *) pc; + njs_vmcode_operand(vm, get->value, retval); + + if (njs_slow_path(!njs_is_index_or_key(value2))) { + if (njs_slow_path(njs_is_null_or_undefined(value1))) { + (void) njs_throw_cannot_property(vm, value1, value2, "get"); + goto error; + } + + ret = njs_value_to_key(vm, &primitive1, value2); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + value2 = &primitive1; + } + + ret = njs_value_property(vm, value1, value2, retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } + + pc += sizeof(njs_vmcode_prop_get_t); + NEXT; + + CASE (NJS_VMCODE_INCREMENT): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_numeric(value2))) { + ret = njs_value_to_numeric(vm, value2, &numeric1); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + num = njs_number(&numeric1); + + } else { + num = njs_number(value2); + } + + njs_set_number(value1, num + 1); + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + *retval = *value1; + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_POST_INCREMENT): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_numeric(value2))) { + ret = njs_value_to_numeric(vm, value2, &numeric1); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + num = njs_number(&numeric1); + + } else { + num = njs_number(value2); + } + + njs_set_number(value1, num + 1); + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + njs_set_number(retval, num); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_DECREMENT): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_numeric(value2))) { + ret = njs_value_to_numeric(vm, value2, &numeric1); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + num = njs_number(&numeric1); + + } else { + num = njs_number(value2); + } + + njs_set_number(value1, num - 1); + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + *retval = *value1; + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_POST_DECREMENT): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_numeric(value2))) { + ret = njs_value_to_numeric(vm, value2, &numeric1); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + num = njs_number(&numeric1); + + } else { + num = njs_number(value2); + } + + njs_set_number(value1, num - 1); + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + njs_set_number(retval, num); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_GLOBAL_GET): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + get = (njs_vmcode_prop_get_t *) pc; + njs_vmcode_operand(vm, get->value, retval); + + ret = njs_value_property(vm, value1, value2, retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } + + pc += sizeof(njs_vmcode_prop_get_t); + + if (ret == NJS_OK) { + pc += sizeof(njs_vmcode_error_t); + } + + NEXT; + + /* + * njs_vmcode_try_return() saves a return value to use it later by + * njs_vmcode_finally(), and jumps to the nearest try_break block. + */ + CASE (NJS_VMCODE_TRY_RETURN): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand2, value1); + + njs_vmcode_operand(vm, vmcode->operand1, retval); + *retval = *value1; + + try_return = (njs_vmcode_try_return_t *) pc; + pc += try_return->offset; + NEXT; + + CASE (NJS_VMCODE_LESS): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_primitive(value1))) { + ret = njs_value_to_primitive(vm, &primitive1, value1, 0); + if (ret != NJS_OK) { + goto error; + } + + value1 = &primitive1; + } + + if (njs_slow_path(!njs_is_primitive(value2))) { + ret = njs_value_to_primitive(vm, &primitive2, value2, 0); + if (ret != NJS_OK) { + goto error; + } + + value2 = &primitive2; + } + + if (njs_slow_path(njs_is_symbol(value1) + || njs_is_symbol(value2))) + { + njs_symbol_conversion_failed(vm, 0); + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + njs_set_boolean(retval, + njs_primitive_values_compare(vm, value1, value2) > 0); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_GREATER): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_primitive(value1))) { + ret = njs_value_to_primitive(vm, &primitive1, value1, 0); + if (ret != NJS_OK) { + goto error; + } + + value1 = &primitive1; + } + + if (njs_slow_path(!njs_is_primitive(value2))) { + ret = njs_value_to_primitive(vm, &primitive2, value2, 0); + if (ret != NJS_OK) { + goto error; + } + + value2 = &primitive2; + } + + if (njs_slow_path(njs_is_symbol(value1) + || njs_is_symbol(value2))) + { + njs_symbol_conversion_failed(vm, 0); + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + njs_set_boolean(retval, + njs_primitive_values_compare(vm, value2, value1) > 0); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_LESS_OR_EQUAL): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_primitive(value1))) { + ret = njs_value_to_primitive(vm, &primitive1, value1, 0); + if (ret != NJS_OK) { + goto error; + } + + value1 = &primitive1; + } + + if (njs_slow_path(!njs_is_primitive(value2))) { + ret = njs_value_to_primitive(vm, &primitive2, value2, 0); + if (ret != NJS_OK) { + goto error; + } + + value2 = &primitive2; + } + + if (njs_slow_path(njs_is_symbol(value1) + || njs_is_symbol(value2))) + { + njs_symbol_conversion_failed(vm, 0); + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + njs_set_boolean(retval, + njs_primitive_values_compare(vm, value2, value1) == 0); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_GREATER_OR_EQUAL): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_primitive(value1))) { + ret = njs_value_to_primitive(vm, &primitive1, value1, 0); + if (ret != NJS_OK) { + goto error; + } + + value1 = &primitive1; + } + + if (njs_slow_path(!njs_is_primitive(value2))) { + ret = njs_value_to_primitive(vm, &primitive2, value2, 0); + if (ret != NJS_OK) { + goto error; + } + + value2 = &primitive2; + } + + if (njs_slow_path(njs_is_symbol(value1) + || njs_is_symbol(value2))) + { + njs_symbol_conversion_failed(vm, 0); + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + njs_set_boolean(retval, + njs_primitive_values_compare(vm, value1, value2) == 0); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_ADDITION): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_slow_path(!njs_is_primitive(value1))) { + hint = njs_is_date(value1); + ret = njs_value_to_primitive(vm, &primitive1, value1, hint); + if (ret != NJS_OK) { + goto error; + } + + value1 = &primitive1; + } + + if (njs_slow_path(!njs_is_primitive(value2))) { + hint = njs_is_date(value2); + ret = njs_value_to_primitive(vm, &primitive2, value2, hint); + if (ret != NJS_OK) { + goto error; + } + + value2 = &primitive2; + } + + if (njs_slow_path(njs_is_symbol(value1) + || njs_is_symbol(value2))) + { + njs_symbol_conversion_failed(vm, + (njs_is_string(value1) || njs_is_string(value2))); + + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + + if (njs_fast_path(njs_is_numeric(value1) + && njs_is_numeric(value2))) + { + njs_set_number(retval, njs_number(value1) + + njs_number(value2)); + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + } + + if (njs_is_string(value1)) { + s1 = value1; + s2 = &dst; + src = value2; + + } else { + s1 = &dst; + s2 = value2; + src = value1; + } + + ret = njs_primitive_value_to_string(vm, &dst, src); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + ret = njs_string_concat(vm, s1, s2); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } + + *retval = vm->retval; + + BREAK; + + CASE (NJS_VMCODE_EQUAL): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_values_equal(vm, value1, value2); + if (njs_slow_path(ret < 0)) { + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_set_boolean(retval, ret); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + + CASE (NJS_VMCODE_NOT_EQUAL): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_values_equal(vm, value1, value2); + if (njs_slow_path(ret < 0)) { + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_set_boolean(retval, !ret); + + pc += sizeof(njs_vmcode_3addr_t); + NEXT; + +#define NJS_PRE_NUMERIC \ + \ + if (njs_slow_path(!njs_is_numeric(value1))) { \ + ret = njs_value_to_numeric(vm, value1, &numeric1); \ + if (njs_slow_path(ret != NJS_OK)) { \ + goto error; \ + } \ + \ + value1 = &numeric1; \ + } \ + \ + if (njs_slow_path(!njs_is_numeric(value2))) { \ + ret = njs_value_to_numeric(vm, value2, &numeric2); \ + if (njs_slow_path(ret != NJS_OK)) { \ + goto error; \ + } \ + \ + value2 = &numeric2; \ + } \ + \ + num = njs_number(value1); \ + \ + njs_vmcode_operand(vm, vmcode->operand1, retval); \ + pc += sizeof(njs_vmcode_3addr_t) + + CASE (NJS_VMCODE_SUBSTRACTION): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + num -= njs_number(value2); + + njs_set_number(retval, num); + NEXT; + + CASE (NJS_VMCODE_MULTIPLICATION): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + num *= njs_number(value2); + + njs_set_number(retval, num); + NEXT; + + CASE (NJS_VMCODE_EXPONENTIATION): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + exponent = njs_number(value2); + /* + * According to ES7: + * 1. If exponent is NaN, the result should be NaN; + * 2. The result of +/-1 ** +/-Infinity should be NaN. + */ + valid = njs_expect(1, fabs(num) != 1 + || (!isnan(exponent) + && !isinf(exponent))); + + num = valid ? pow(num, exponent) : NAN; + + njs_set_number(retval, num); + NEXT; + + CASE (NJS_VMCODE_DIVISION): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + num /= njs_number(value2); + + njs_set_number(retval, num); + NEXT; + + CASE (NJS_VMCODE_REMAINDER): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + num = fmod(num, njs_number(value2)); + + njs_set_number(retval, num); + NEXT; + + CASE (NJS_VMCODE_BITWISE_AND): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + i32 = njs_number_to_int32(njs_number(value2)); + i32 &= njs_number_to_int32(num); + + njs_set_int32(retval, i32); + NEXT; + + CASE (NJS_VMCODE_BITWISE_OR): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + i32 = njs_number_to_int32(njs_number(value2)); + i32 |= njs_number_to_int32(num); + + njs_set_int32(retval, i32); + NEXT; + + CASE (NJS_VMCODE_BITWISE_XOR): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + i32 = njs_number_to_int32(njs_number(value2)); + i32 ^= njs_number_to_int32(num); + + njs_set_int32(retval, i32); + NEXT; + + CASE (NJS_VMCODE_LEFT_SHIFT): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + u32 = njs_number_to_uint32(njs_number(value2)) & 0x1f; + i32 = njs_number_to_int32(num); + + /* Shifting of negative numbers is undefined. */ + i32 = (uint32_t) i32 << u32; + + njs_set_int32(retval, i32); + NEXT; + + CASE (NJS_VMCODE_RIGHT_SHIFT): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; - for ( ;; ) { + u32 = njs_number_to_uint32(njs_number(value2)) & 0x1f; + i32 = njs_number_to_int32(num); - vmcode = (njs_vmcode_generic_t *) pc; + i32 >>= u32; + + njs_set_int32(retval, i32); + NEXT; + + CASE (NJS_VMCODE_UNSIGNED_RIGHT_SHIFT): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + NJS_PRE_NUMERIC; + + u32 = njs_number_to_uint32(njs_number(value2)) & 0x1f; + njs_set_uint32(retval, njs_number_to_uint32(num) >> u32); + NEXT; + + CASE (NJS_VMCODE_OBJECT_COPY): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_vmcode_object_copy(vm, value1, NULL); + + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; + + BREAK; + + CASE (NJS_VMCODE_TEMPLATE_LITERAL): + njs_vmcode_debug_opcode(); - /* - * The first operand is passed as is in value2 to - * NJS_VMCODE_JUMP, - * NJS_VMCODE_IF_TRUE_JUMP, - * NJS_VMCODE_IF_FALSE_JUMP, - * NJS_VMCODE_FUNCTION_FRAME, - * NJS_VMCODE_FUNCTION_CALL, - * NJS_VMCODE_RETURN, - * NJS_VMCODE_TRY_START, - * NJS_VMCODE_TRY_CONTINUE, - * NJS_VMCODE_TRY_BREAK, - * NJS_VMCODE_TRY_END, - * NJS_VMCODE_CATCH, - * NJS_VMCODE_THROW, - * NJS_VMCODE_STOP. - */ value2 = (njs_value_t *) vmcode->operand1; - value1 = NULL; - switch (vmcode->code.operands) { + ret = njs_vmcode_template_literal(vm, NULL, value2); - case NJS_VMCODE_3OPERANDS: - njs_vmcode_operand(vm, vmcode->operand3, value2); + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; + + BREAK; + + CASE (NJS_VMCODE_PROPERTY_IN): + njs_vmcode_debug_opcode(); - /* Fall through. */ + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); - case NJS_VMCODE_2OPERANDS: - njs_vmcode_operand(vm, vmcode->operand2, value1); + ret = njs_vmcode_property_in(vm, value1, value2); + + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; } - op = vmcode->code.operation; + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; - /* - * On success an operation returns size of the bytecode, - * a jump offset or zero after the call or return operations. - * Jumps can return a negative offset. Compilers can generate - * (ret < 0 && ret >= NJS_PREEMPT) - * as a single unsigned comparision. - */ + BREAK; + + CASE (NJS_VMCODE_PROPERTY_DELETE): + njs_vmcode_debug_opcode(); -#ifdef NJS_DEBUG_OPCODE - if (vm->options.opcode_debug) { - njs_disassemble(pc, NULL, 1, NULL); + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_value_property_delete(vm, value1, value2, NULL, 1); + if (njs_fast_path(ret != NJS_ERROR)) { + vm->retval = njs_value_true; + + ret = sizeof(njs_vmcode_3addr_t); } -#endif - if (op > NJS_VMCODE_NORET) { + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } - if (op == NJS_VMCODE_MOVE) { - njs_vmcode_operand(vm, vmcode->operand1, retval); - *retval = *value1; + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; - pc += sizeof(njs_vmcode_move_t); - goto next; - } + BREAK; - if (op == NJS_VMCODE_PROPERTY_GET) { - get = (njs_vmcode_prop_get_t *) pc; - njs_vmcode_operand(vm, get->value, retval); + CASE (NJS_VMCODE_PROPERTY_FOREACH): + njs_vmcode_debug_opcode(); - if (njs_slow_path(!njs_is_index_or_key(value2))) { - if (njs_slow_path(njs_is_null_or_undefined(value1))) { - (void) njs_throw_cannot_property(vm, value1, value2, - "get"); - goto error; - } + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); - ret = njs_value_to_key(vm, &primitive1, value2); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + ret = njs_vmcode_property_foreach(vm, value1, value2, pc); - value2 = &primitive1; - } + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } - ret = njs_value_property(vm, value1, value2, retval); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; - pc += sizeof(njs_vmcode_prop_get_t); - goto next; - } + BREAK; - switch (op) { - case NJS_VMCODE_INCREMENT: - case NJS_VMCODE_POST_INCREMENT: - case NJS_VMCODE_DECREMENT: - case NJS_VMCODE_POST_DECREMENT: - if (njs_slow_path(!njs_is_numeric(value2))) { - ret = njs_value_to_numeric(vm, value2, &numeric1); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } - - num = njs_number(&numeric1); - - } else { - num = njs_number(value2); - } + CASE (NJS_VMCODE_STRICT_EQUAL): + njs_vmcode_debug_opcode(); - njs_set_number(value1, - num + (1 - 2 * ((op - NJS_VMCODE_INCREMENT) >> 1))); + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); - njs_vmcode_operand(vm, vmcode->operand1, retval); + ret = njs_values_strict_equal(value1, value2); - if (op & 1) { - njs_set_number(retval, num); + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_set_boolean(retval, ret); - } else { - *retval = *value1; - } + pc += sizeof(njs_vmcode_3addr_t); + NEXT; - pc += sizeof(njs_vmcode_3addr_t); - goto next; + CASE (NJS_VMCODE_STRICT_NOT_EQUAL): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_GLOBAL_GET: - get = (njs_vmcode_prop_get_t *) pc; - njs_vmcode_operand(vm, get->value, retval); + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); - ret = njs_value_property(vm, value1, value2, retval); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + ret = njs_values_strict_equal(value1, value2); - pc += sizeof(njs_vmcode_prop_get_t); + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_set_boolean(retval, !ret); - if (ret == NJS_OK) { - pc += sizeof(njs_vmcode_error_t); - } + pc += sizeof(njs_vmcode_3addr_t); + NEXT; - goto next; - - /* - * njs_vmcode_try_return() saves a return value to use it later by - * njs_vmcode_finally(), and jumps to the nearest try_break block. - */ - case NJS_VMCODE_TRY_RETURN: - njs_vmcode_operand(vm, vmcode->operand1, retval); - *retval = *value1; - - try_return = (njs_vmcode_try_return_t *) pc; - pc += try_return->offset; - goto next; - - case NJS_VMCODE_LESS: - case NJS_VMCODE_GREATER: - case NJS_VMCODE_LESS_OR_EQUAL: - case NJS_VMCODE_GREATER_OR_EQUAL: - case NJS_VMCODE_ADDITION: - if (njs_slow_path(!njs_is_primitive(value1))) { - hint = (op == NJS_VMCODE_ADDITION) && njs_is_date(value1); - ret = njs_value_to_primitive(vm, &primitive1, value1, hint); - if (ret != NJS_OK) { - goto error; - } - - value1 = &primitive1; - } + CASE (NJS_VMCODE_TEST_IF_TRUE): + njs_vmcode_debug_opcode(); - if (njs_slow_path(!njs_is_primitive(value2))) { - hint = (op == NJS_VMCODE_ADDITION) && njs_is_date(value2); - ret = njs_value_to_primitive(vm, &primitive2, value2, hint); - if (ret != NJS_OK) { - goto error; - } + njs_vmcode_operand(vm, vmcode->operand2, value1); - value2 = &primitive2; - } + ret = njs_is_true(value1); - if (njs_slow_path(njs_is_symbol(value1) - || njs_is_symbol(value2))) - { - njs_symbol_conversion_failed(vm, - (op == NJS_VMCODE_ADDITION) && - (njs_is_string(value1) || njs_is_string(value2))); + if (ret) { + test_jump = (njs_vmcode_test_jump_t *) pc; + ret = test_jump->offset; - goto error; - } + } else { + ret = sizeof(njs_vmcode_3addr_t); + } - njs_vmcode_operand(vm, vmcode->operand1, retval); - - if (op == NJS_VMCODE_ADDITION) { - if (njs_fast_path(njs_is_numeric(value1) - && njs_is_numeric(value2))) - { - njs_set_number(retval, njs_number(value1) - + njs_number(value2)); - pc += sizeof(njs_vmcode_3addr_t); - goto next; - } - - if (njs_is_string(value1)) { - s1 = value1; - s2 = &dst; - src = value2; - - } else { - s1 = &dst; - s2 = value2; - src = value1; - } - - ret = njs_primitive_value_to_string(vm, &dst, src); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } - - ret = njs_string_concat(vm, s1, s2); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } - - *retval = vm->retval; - - pc += ret; - goto next; - } + njs_vmcode_operand(vm, vmcode->operand1, retval); + *retval = *value1; - if ((uint8_t) (op - NJS_VMCODE_GREATER) < 2) { - /* NJS_VMCODE_GREATER, NJS_VMCODE_LESS_OR_EQUAL */ - src = value1; - value1 = value2; - value2 = src; - } + BREAK; - ret = njs_primitive_values_compare(vm, value1, value2); + CASE (NJS_VMCODE_TEST_IF_FALSE): + njs_vmcode_debug_opcode(); - if (op < NJS_VMCODE_LESS_OR_EQUAL) { - ret = ret > 0; + njs_vmcode_operand(vm, vmcode->operand2, value1); - } else { - ret = ret == 0; - } + ret = !njs_is_true(value1); - njs_set_boolean(retval, ret); + if (ret) { + test_jump = (njs_vmcode_test_jump_t *) pc; + ret = test_jump->offset; - pc += sizeof(njs_vmcode_3addr_t); - goto next; + } else { + ret = sizeof(njs_vmcode_3addr_t); + } - case NJS_VMCODE_EQUAL: - case NJS_VMCODE_NOT_EQUAL: - ret = njs_values_equal(vm, value1, value2); - if (njs_slow_path(ret < 0)) { - goto error; - } + njs_vmcode_operand(vm, vmcode->operand1, retval); + *retval = *value1; - ret ^= op - NJS_VMCODE_EQUAL; - - njs_vmcode_operand(vm, vmcode->operand1, retval); - njs_set_boolean(retval, ret); - - pc += sizeof(njs_vmcode_3addr_t); - goto next; - - case NJS_VMCODE_SUBSTRACTION: - case NJS_VMCODE_MULTIPLICATION: - case NJS_VMCODE_EXPONENTIATION: - case NJS_VMCODE_DIVISION: - case NJS_VMCODE_REMAINDER: - case NJS_VMCODE_BITWISE_AND: - case NJS_VMCODE_BITWISE_OR: - case NJS_VMCODE_BITWISE_XOR: - case NJS_VMCODE_LEFT_SHIFT: - case NJS_VMCODE_RIGHT_SHIFT: - case NJS_VMCODE_UNSIGNED_RIGHT_SHIFT: - if (njs_slow_path(!njs_is_numeric(value1))) { - ret = njs_value_to_numeric(vm, value1, &numeric1); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } - - value1 = &numeric1; - } + BREAK; - if (njs_slow_path(!njs_is_numeric(value2))) { - ret = njs_value_to_numeric(vm, value2, &numeric2); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + CASE (NJS_VMCODE_COALESCE): + njs_vmcode_debug_opcode(); - value2 = &numeric2; - } + njs_vmcode_operand(vm, vmcode->operand2, value1); - num = njs_number(value1); + ret = !njs_is_null_or_undefined(value1); - njs_vmcode_operand(vm, vmcode->operand1, retval); - pc += sizeof(njs_vmcode_3addr_t); + if (ret) { + test_jump = (njs_vmcode_test_jump_t *) pc; + ret = test_jump->offset; - switch (op) { - case NJS_VMCODE_SUBSTRACTION: - num -= njs_number(value2); - break; + } else { + ret = sizeof(njs_vmcode_3addr_t); + } - case NJS_VMCODE_MULTIPLICATION: - num *= njs_number(value2); - break; + njs_vmcode_operand(vm, vmcode->operand1, retval); + *retval = *value1; - case NJS_VMCODE_EXPONENTIATION: - exponent = njs_number(value2); + BREAK; - /* - * According to ES7: - * 1. If exponent is NaN, the result should be NaN; - * 2. The result of +/-1 ** +/-Infinity should be NaN. - */ - valid = njs_expect(1, fabs(num) != 1 - || (!isnan(exponent) - && !isinf(exponent))); +#define NJS_PRE_UNARY \ + if (njs_slow_path(!njs_is_numeric(value1))) { \ + ret = njs_value_to_numeric(vm, value1, &numeric1); \ + if (njs_slow_path(ret != NJS_OK)) { \ + goto error; \ + } \ + \ + value1 = &numeric1; \ + } \ + \ + num = njs_number(value1); \ + njs_vmcode_operand(vm, vmcode->operand1, retval); - num = valid ? pow(num, exponent) : NAN; - break; + CASE (NJS_VMCODE_UNARY_NEGATION): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_DIVISION: - num /= njs_number(value2); - break; + njs_vmcode_operand(vm, vmcode->operand2, value1); - case NJS_VMCODE_REMAINDER: - num = fmod(num, njs_number(value2)); - break; + NJS_PRE_UNARY; - case NJS_VMCODE_BITWISE_AND: - case NJS_VMCODE_BITWISE_OR: - case NJS_VMCODE_BITWISE_XOR: - i32 = njs_number_to_int32(njs_number(value2)); + num = -num; - switch (op) { - case NJS_VMCODE_BITWISE_AND: - i32 &= njs_number_to_int32(num); - break; + njs_set_number(retval, num); - case NJS_VMCODE_BITWISE_OR: - i32 |= njs_number_to_int32(num); - break; + pc += sizeof(njs_vmcode_2addr_t); + NEXT; - case NJS_VMCODE_BITWISE_XOR: - i32 ^= njs_number_to_int32(num); - break; - } + CASE (NJS_VMCODE_UNARY_PLUS): + njs_vmcode_debug_opcode(); - njs_set_int32(retval, i32); - goto next; + njs_vmcode_operand(vm, vmcode->operand2, value1); - default: - u32 = njs_number_to_uint32(njs_number(value2)) & 0x1f; + NJS_PRE_UNARY; - switch (op) { - case NJS_VMCODE_LEFT_SHIFT: - case NJS_VMCODE_RIGHT_SHIFT: - i32 = njs_number_to_int32(num); + njs_set_number(retval, num); - if (op == NJS_VMCODE_LEFT_SHIFT) { - /* Shifting of negative numbers is undefined. */ - i32 = (uint32_t) i32 << u32; - } else { - i32 >>= u32; - } + pc += sizeof(njs_vmcode_2addr_t); + NEXT; - njs_set_int32(retval, i32); - break; + CASE (NJS_VMCODE_BITWISE_NOT): + njs_vmcode_debug_opcode(); - default: /* NJS_VMCODE_UNSIGNED_RIGHT_SHIFT */ - njs_set_uint32(retval, - njs_number_to_uint32(num) >> u32); - } + njs_vmcode_operand(vm, vmcode->operand2, value1); - goto next; - } + NJS_PRE_UNARY; - njs_set_number(retval, num); - goto next; + njs_set_int32(retval, ~njs_number_to_uint32(num)); - case NJS_VMCODE_OBJECT_COPY: - ret = njs_vmcode_object_copy(vm, value1, value2); - break; + pc += sizeof(njs_vmcode_2addr_t); + NEXT; - case NJS_VMCODE_TEMPLATE_LITERAL: - ret = njs_vmcode_template_literal(vm, value1, value2); - break; + CASE (NJS_VMCODE_LOGICAL_NOT): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_PROPERTY_IN: - ret = njs_vmcode_property_in(vm, value1, value2); - break; + njs_vmcode_operand(vm, vmcode->operand2, value1); + njs_vmcode_operand(vm, vmcode->operand1, retval); - case NJS_VMCODE_PROPERTY_DELETE: - ret = njs_value_property_delete(vm, value1, value2, NULL, 1); - if (njs_fast_path(ret != NJS_ERROR)) { - vm->retval = njs_value_true; + njs_set_boolean(retval, !njs_is_true(value1)); - ret = sizeof(njs_vmcode_3addr_t); - } + pc += sizeof(njs_vmcode_2addr_t); + NEXT; - break; + CASE (NJS_VMCODE_OBJECT): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_PROPERTY_FOREACH: - ret = njs_vmcode_property_foreach(vm, value1, value2, pc); - break; + ret = njs_vmcode_object(vm); - case NJS_VMCODE_STRICT_EQUAL: - case NJS_VMCODE_STRICT_NOT_EQUAL: - ret = njs_values_strict_equal(value1, value2); + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } - ret ^= op - NJS_VMCODE_STRICT_EQUAL; + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; - njs_vmcode_operand(vm, vmcode->operand1, retval); - njs_set_boolean(retval, ret); + BREAK; - pc += sizeof(njs_vmcode_3addr_t); - goto next; + CASE (NJS_VMCODE_ARRAY): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_TEST_IF_TRUE: - case NJS_VMCODE_TEST_IF_FALSE: - case NJS_VMCODE_COALESCE: - if (op == NJS_VMCODE_COALESCE) { - ret = !njs_is_null_or_undefined(value1); + ret = njs_vmcode_array(vm, pc); - } else { - ret = njs_is_true(value1); - ret ^= op - NJS_VMCODE_TEST_IF_TRUE; - } + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } - if (ret) { - test_jump = (njs_vmcode_test_jump_t *) pc; - ret = test_jump->offset; + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; - } else { - ret = sizeof(njs_vmcode_3addr_t); - } + BREAK; - njs_vmcode_operand(vm, vmcode->operand1, retval); - *retval = *value1; + CASE (NJS_VMCODE_FUNCTION): + njs_vmcode_debug_opcode(); - pc += ret; - goto next; + ret = njs_vmcode_function(vm, pc); - case NJS_VMCODE_UNARY_PLUS: - case NJS_VMCODE_UNARY_NEGATION: - case NJS_VMCODE_BITWISE_NOT: - if (njs_slow_path(!njs_is_numeric(value1))) { - ret = njs_value_to_numeric(vm, value1, &numeric1); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } - value1 = &numeric1; - } + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; - num = njs_number(value1); - njs_vmcode_operand(vm, vmcode->operand1, retval); + BREAK; - switch (op) { - case NJS_VMCODE_UNARY_NEGATION: - num = -num; + CASE (NJS_VMCODE_REGEXP): + njs_vmcode_debug_opcode(); - /* Fall through. */ - case NJS_VMCODE_UNARY_PLUS: - njs_set_number(retval, num); - break; + ret = njs_vmcode_regexp(vm, pc); - case NJS_VMCODE_BITWISE_NOT: - njs_set_int32(retval, ~njs_number_to_uint32(num)); - } + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; + + BREAK; + + CASE (NJS_VMCODE_INSTANCE_OF): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_vmcode_instance_of(vm, value1, value2); + + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; + + BREAK; + + CASE (NJS_VMCODE_TYPEOF): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_vmcode_typeof(vm, value1, NULL); + + if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { + goto error; + } + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; + + BREAK; + + CASE (NJS_VMCODE_VOID): + njs_vmcode_debug_opcode(); + + njs_set_undefined(&vm->retval); + + ret = sizeof(njs_vmcode_2addr_t); + + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; + + BREAK; + + CASE (NJS_VMCODE_DELETE): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand2, value1); + + njs_release(vm, value1); + vm->retval = njs_value_true; + + ret = sizeof(njs_vmcode_2addr_t); + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; + + BREAK; - pc += sizeof(njs_vmcode_2addr_t); - goto next; + CASE (NJS_VMCODE_DEBUGGER): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_LOGICAL_NOT: - njs_vmcode_operand(vm, vmcode->operand1, retval); - njs_set_boolean(retval, !njs_is_true(value1)); + ret = njs_vmcode_debugger(vm); - pc += sizeof(njs_vmcode_2addr_t); - goto next; + njs_vmcode_operand(vm, vmcode->operand1, retval); + njs_release(vm, retval); + *retval = vm->retval; - case NJS_VMCODE_OBJECT: - ret = njs_vmcode_object(vm); - break; + BREAK; - case NJS_VMCODE_ARRAY: - ret = njs_vmcode_array(vm, pc); - break; + CASE (NJS_VMCODE_PUT_ARG): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_FUNCTION: - ret = njs_vmcode_function(vm, pc); - break; + put_arg = (njs_vmcode_1addr_t *) pc; + native = vm->top_frame; + + value1 = &native->arguments[native->put_args++]; + njs_vmcode_operand(vm, put_arg->index, value2); + + njs_value_assign(value1, value2); + + ret = sizeof(njs_vmcode_1addr_t); + BREAK; - case NJS_VMCODE_REGEXP: - ret = njs_vmcode_regexp(vm, pc); - break; + CASE (NJS_VMCODE_STOP): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_INSTANCE_OF: - ret = njs_vmcode_instance_of(vm, value1, value2); - break; + njs_vmcode_operand(vm, vmcode->operand1, value2); + vm->retval = *value2; - case NJS_VMCODE_TYPEOF: - ret = njs_vmcode_typeof(vm, value1, value2); - break; + njs_vmcode_debug(vm, pc, "EXIT STOP"); - case NJS_VMCODE_VOID: - njs_set_undefined(&vm->retval); + return NJS_OK; - ret = sizeof(njs_vmcode_2addr_t); - break; + CASE (NJS_VMCODE_JUMP): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_DELETE: - njs_release(vm, value1); - vm->retval = njs_value_true; + ret = (njs_jump_off_t) vmcode->operand1; + BREAK; - ret = sizeof(njs_vmcode_2addr_t); - break; + CASE (NJS_VMCODE_PROPERTY_SET): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_DEBUGGER: - ret = njs_vmcode_debugger(vm); - break; + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + njs_vmcode_operand(vm, vmcode->operand1, retval); - default: - njs_internal_error(vm, "%d has retval", op); + if (njs_slow_path(!njs_is_index_or_key(value2))) { + if (njs_slow_path(njs_is_null_or_undefined(value1))) { + (void) njs_throw_cannot_property(vm, value1, value2, "set"); goto error; } - if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { - break; + njs_value_assign(&primitive1, value1); + ret = njs_value_to_key(vm, &primitive2, value2); + if (njs_slow_path(ret != NJS_OK)) { + goto error; } - njs_vmcode_operand(vm, vmcode->operand1, retval); - njs_release(vm, retval); - *retval = vm->retval; + value1 = &primitive1; + value2 = &primitive2; + } + + ret = njs_value_property_set(vm, value1, value2, retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } + + ret = sizeof(njs_vmcode_prop_set_t); + BREAK; + + CASE (NJS_VMCODE_PROPERTY_ACCESSOR): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + accessor = (njs_vmcode_prop_accessor_t *) pc; + njs_vmcode_operand(vm, accessor->value, function); + + ret = njs_value_to_key(vm, &name, value2); + if (njs_slow_path(ret != NJS_OK)) { + njs_internal_error(vm, "failed conversion of type \"%s\" " + "to string while property define", + njs_type_string(value2->type)); + goto error; + } + + ret = njs_object_prop_define(vm, value1, &name, function, + accessor->type); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } + + ret = sizeof(njs_vmcode_prop_accessor_t); + BREAK; + + CASE (NJS_VMCODE_IF_TRUE_JUMP): + njs_vmcode_debug_opcode(); + + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_is_true(value1); + + ret = ret ? (njs_jump_off_t) value2 + : (njs_jump_off_t) sizeof(njs_vmcode_cond_jump_t); + + BREAK; + + CASE (NJS_VMCODE_IF_FALSE_JUMP): + njs_vmcode_debug_opcode(); + + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); + + ret = njs_is_true(value1); + + ret = !ret ? (njs_jump_off_t) value2 + : (njs_jump_off_t) sizeof(njs_vmcode_cond_jump_t); + + BREAK; + + CASE (NJS_VMCODE_IF_EQUAL_JUMP): + njs_vmcode_debug_opcode(); + + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); + + if (njs_values_strict_equal(value1, value2)) { + equal = (njs_vmcode_equal_jump_t *) pc; + ret = equal->offset; } else { + ret = sizeof(njs_vmcode_3addr_t); + } + + BREAK; - switch (op) { - case NJS_VMCODE_PUT_ARG: - put_arg = (njs_vmcode_1addr_t *) pc; - native = vm->top_frame; + CASE (NJS_VMCODE_PROPERTY_INIT): + njs_vmcode_debug_opcode(); - value1 = &native->arguments[native->put_args++]; - njs_vmcode_operand(vm, put_arg->index, value2); + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); - njs_value_assign(value1, value2); + set = (njs_vmcode_prop_set_t *) pc; + njs_vmcode_operand(vm, set->value, retval); + ret = njs_vmcode_property_init(vm, value1, value2, retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - ret = sizeof(njs_vmcode_1addr_t); - break; + BREAK; - case NJS_VMCODE_STOP: - njs_vmcode_operand(vm, (njs_index_t) value2, value2); - vm->retval = *value2; + CASE (NJS_VMCODE_RETURN): + njs_vmcode_debug_opcode(); - njs_vmcode_debug(vm, pc, "EXIT STOP"); + value2 = (njs_value_t *) vmcode->operand1; - return NJS_OK; + njs_vmcode_operand(vm, (njs_index_t) value2, value2); - case NJS_VMCODE_JUMP: - ret = (njs_jump_off_t) value2; - break; + njs_vmcode_debug(vm, pc, "EXIT RETURN"); - case NJS_VMCODE_PROPERTY_SET: - set = (njs_vmcode_prop_set_t *) pc; - njs_vmcode_operand(vm, set->value, retval); + return njs_vmcode_return(vm, NULL, value2); - if (njs_slow_path(!njs_is_index_or_key(value2))) { - if (njs_slow_path(njs_is_null_or_undefined(value1))) { - (void) njs_throw_cannot_property(vm, value1, value2, - "set"); - goto error; - } + CASE (NJS_VMCODE_FUNCTION_COPY): + njs_vmcode_debug_opcode(); - njs_value_assign(&primitive1, value1); - ret = njs_value_to_key(vm, &primitive2, value2); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + fcopy = (njs_vmcode_function_copy_t *) pc; + ret = njs_vmcode_function_copy(vm, fcopy->function, fcopy->retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - value1 = &primitive1; - value2 = &primitive2; - } + BREAK; - ret = njs_value_property_set(vm, value1, value2, retval); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + CASE (NJS_VMCODE_FUNCTION_FRAME): + njs_vmcode_debug_opcode(); - ret = sizeof(njs_vmcode_prop_set_t); - break; + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); - case NJS_VMCODE_PROPERTY_ACCESSOR: - accessor = (njs_vmcode_prop_accessor_t *) pc; - njs_vmcode_operand(vm, accessor->value, function); + function_frame = (njs_vmcode_function_frame_t *) pc; - ret = njs_value_to_key(vm, &name, value2); - if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "failed conversion of type \"%s\" " - "to string while property define", - njs_type_string(value2->type)); - goto error; - } + ret = njs_function_frame_create(vm, value1, &njs_value_undefined, + (uintptr_t) value2, + function_frame->ctor); - ret = njs_object_prop_define(vm, value1, &name, function, - accessor->type); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } - ret = sizeof(njs_vmcode_prop_accessor_t); - break; + ret = sizeof(njs_vmcode_function_frame_t); + BREAK; - case NJS_VMCODE_IF_TRUE_JUMP: - case NJS_VMCODE_IF_FALSE_JUMP: - ret = njs_is_true(value1); + CASE (NJS_VMCODE_METHOD_FRAME): + njs_vmcode_debug_opcode(); - ret ^= op - NJS_VMCODE_IF_TRUE_JUMP; + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); - ret = ret ? (njs_jump_off_t) value2 - : (njs_jump_off_t) sizeof(njs_vmcode_cond_jump_t); + method_frame = (njs_vmcode_method_frame_t *) pc; - break; + if (njs_slow_path(!njs_is_key(value2))) { + if (njs_slow_path(njs_is_null_or_undefined(value1))) { + (void) njs_throw_cannot_property(vm, value1, value2, "get"); + goto error; + } - case NJS_VMCODE_IF_EQUAL_JUMP: - if (njs_values_strict_equal(value1, value2)) { - equal = (njs_vmcode_equal_jump_t *) pc; - ret = equal->offset; + ret = njs_value_to_key(vm, &primitive1, value2); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } - } else { - ret = sizeof(njs_vmcode_3addr_t); - } + value2 = &primitive1; + } - break; + ret = njs_value_property(vm, value1, value2, &dst); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - case NJS_VMCODE_PROPERTY_INIT: - set = (njs_vmcode_prop_set_t *) pc; - njs_vmcode_operand(vm, set->value, retval); - ret = njs_vmcode_property_init(vm, value1, value2, retval); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + if (njs_slow_path(!njs_is_function(&dst))) { + ret = njs_value_to_key(vm, &dst, value2); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } - break; + njs_key_string_get(vm, &dst, &string); + njs_type_error(vm, + "(intermediate value)[\"%V\"] is not a function", + &string); + goto error; + } - case NJS_VMCODE_RETURN: - njs_vmcode_operand(vm, (njs_index_t) value2, value2); + ret = njs_function_frame_create(vm, &dst, value1, method_frame->nargs, + method_frame->ctor); - njs_vmcode_debug(vm, pc, "EXIT RETURN"); + if (njs_slow_path(ret != NJS_OK)) { + goto error; + } - return njs_vmcode_return(vm, NULL, value2); + ret = sizeof(njs_vmcode_method_frame_t); + BREAK; - case NJS_VMCODE_FUNCTION_COPY: - fcopy = (njs_vmcode_function_copy_t *) pc; - ret = njs_vmcode_function_copy(vm, fcopy->function, - fcopy->retval); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + CASE (NJS_VMCODE_FUNCTION_CALL): + njs_vmcode_debug_opcode(); - break; + value2 = (njs_value_t *) vmcode->operand1; - case NJS_VMCODE_FUNCTION_FRAME: - function_frame = (njs_vmcode_function_frame_t *) pc; + vm->active_frame->native.pc = pc; - /* TODO: external object instead of void this. */ + njs_vmcode_operand(vm, (njs_index_t) value2, value2); - ret = njs_function_frame_create(vm, value1, - &njs_value_undefined, - (uintptr_t) value2, - function_frame->ctor); + ret = njs_function_frame_invoke(vm, value2); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + njs_vmcode_debug(vm, pc, "RESUME"); - ret = sizeof(njs_vmcode_function_frame_t); - break; + ret = sizeof(njs_vmcode_function_call_t); + BREAK; - case NJS_VMCODE_METHOD_FRAME: - method_frame = (njs_vmcode_method_frame_t *) pc; + CASE (NJS_VMCODE_PROPERTY_NEXT): + njs_vmcode_debug_opcode(); - if (njs_slow_path(!njs_is_key(value2))) { - if (njs_slow_path(njs_is_null_or_undefined(value1))) { - (void) njs_throw_cannot_property(vm, value1, value2, - "get"); - goto error; - } + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); - ret = njs_value_to_key(vm, &primitive1, value2); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + pnext = (njs_vmcode_prop_next_t *) pc; + retval = njs_scope_value(vm, pnext->retval); - value2 = &primitive1; - } + njs_assert(njs_is_data(value2, NJS_DATA_TAG_FOREACH_NEXT)); + next = njs_data(value2); - ret = njs_value_property(vm, value1, value2, &dst); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + if (next->index < next->array->length) { + *retval = next->array->start[next->index++]; - if (njs_slow_path(!njs_is_function(&dst))) { - ret = njs_value_to_key(vm, &dst, value2); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } - - njs_key_string_get(vm, &dst, &string); - njs_type_error(vm, - "(intermediate value)[\"%V\"] is not a function", - &string); - goto error; - } + ret = pnext->offset; + BREAK; + } - ret = njs_function_frame_create(vm, &dst, value1, - method_frame->nargs, - method_frame->ctor); + njs_mp_free(vm->mem_pool, next); - if (njs_slow_path(ret != NJS_OK)) { - goto error; - } + ret = sizeof(njs_vmcode_prop_next_t); + BREAK; - ret = sizeof(njs_vmcode_method_frame_t); - break; + CASE (NJS_VMCODE_ARGUMENTS): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_FUNCTION_CALL: - vm->active_frame->native.pc = pc; + ret = njs_vmcode_arguments(vm, pc); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - njs_vmcode_operand(vm, (njs_index_t) value2, value2); + BREAK; - ret = njs_function_frame_invoke(vm, value2); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + CASE (NJS_VMCODE_TO_PROPERTY_KEY): + njs_vmcode_debug_opcode(); - njs_vmcode_debug(vm, pc, "RESUME"); + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); - ret = sizeof(njs_vmcode_function_call_t); - break; + njs_vmcode_operand(vm, (njs_index_t) value2, retval); - case NJS_VMCODE_PROPERTY_NEXT: - pnext = (njs_vmcode_prop_next_t *) pc; - retval = njs_scope_value(vm, pnext->retval); + ret = njs_value_to_key(vm, retval, value1); + if (njs_fast_path(ret == NJS_ERROR)) { + goto error; + } - njs_assert(njs_is_data(value2, NJS_DATA_TAG_FOREACH_NEXT)); - next = njs_data(value2); + ret = sizeof(njs_vmcode_2addr_t); + BREAK; - if (next->index < next->array->length) { - *retval = next->array->start[next->index++]; + CASE (NJS_VMCODE_TO_PROPERTY_KEY_CHK): + njs_vmcode_debug_opcode(); - ret = pnext->offset; - break; - } + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); - njs_mp_free(vm->mem_pool, next); + njs_vmcode_operand(vm, (njs_index_t) value2, retval); + njs_vmcode_operand(vm, vmcode->operand3, value2); - ret = sizeof(njs_vmcode_prop_next_t); - break; + if (njs_slow_path(njs_is_null_or_undefined(value2))) { + (void) njs_throw_cannot_property(vm, value2, value1, "get"); + goto error; + } - case NJS_VMCODE_ARGUMENTS: - ret = njs_vmcode_arguments(vm, pc); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + ret = njs_value_to_key(vm, retval, value1); + if (njs_fast_path(ret == NJS_ERROR)) { + goto error; + } - break; + ret = sizeof(njs_vmcode_3addr_t); + BREAK; - case NJS_VMCODE_TO_PROPERTY_KEY: - case NJS_VMCODE_TO_PROPERTY_KEY_CHK: - njs_vmcode_operand(vm, (njs_index_t) value2, retval); + CASE (NJS_VMCODE_SET_FUNCTION_NAME): + njs_vmcode_debug_opcode(); - if (op == NJS_VMCODE_TO_PROPERTY_KEY_CHK) { - njs_vmcode_operand(vm, vmcode->operand3, value2); + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); - if (njs_slow_path(njs_is_null_or_undefined(value2))) { - (void) njs_throw_cannot_property(vm, value2, value1, - "get"); - goto error; - } - } + njs_vmcode_operand(vm, (njs_index_t) value2, value2); - ret = njs_value_to_key(vm, retval, value1); - if (njs_fast_path(ret == NJS_ERROR)) { - goto error; - } + njs_assert(njs_is_function(value2)); - ret = (op == NJS_VMCODE_TO_PROPERTY_KEY) - ? sizeof(njs_vmcode_2addr_t) - : sizeof(njs_vmcode_3addr_t); - break; + ret = njs_function_name_set(vm, njs_function(value2), value1, NULL); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - case NJS_VMCODE_SET_FUNCTION_NAME: - njs_vmcode_operand(vm, (njs_index_t) value2, value2); + ret = sizeof(njs_vmcode_2addr_t); + BREAK; - njs_assert(njs_is_function(value2)); - ret = njs_function_name_set(vm, njs_function(value2), value1, - NULL); - if (njs_slow_path(ret == NJS_ERROR)) { - return ret; - } + CASE (NJS_VMCODE_PROTO_INIT): + njs_vmcode_debug_opcode(); - ret = sizeof(njs_vmcode_2addr_t); - break; + njs_vmcode_operand(vm, vmcode->operand3, value2); + njs_vmcode_operand(vm, vmcode->operand2, value1); - case NJS_VMCODE_PROTO_INIT: - set = (njs_vmcode_prop_set_t *) pc; - njs_vmcode_operand(vm, set->value, retval); - ret = njs_vmcode_proto_init(vm, value1, value2, retval); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + set = (njs_vmcode_prop_set_t *) pc; + njs_vmcode_operand(vm, set->value, retval); + ret = njs_vmcode_proto_init(vm, value1, value2, retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - break; + BREAK; - case NJS_VMCODE_IMPORT: - import = (njs_vmcode_import_t *) pc; - retval = njs_scope_value(vm, import->retval); - ret = njs_vmcode_import(vm, import->module, retval); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + CASE (NJS_VMCODE_IMPORT): + njs_vmcode_debug_opcode(); - break; + import = (njs_vmcode_import_t *) pc; + retval = njs_scope_value(vm, import->retval); + ret = njs_vmcode_import(vm, import->module, retval); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - case NJS_VMCODE_AWAIT: - await = (njs_vmcode_await_t *) pc; + BREAK; - ret = njs_vmcode_await(vm, await, promise_cap, async_ctx); + CASE (NJS_VMCODE_AWAIT): + njs_vmcode_debug_opcode(); - njs_vmcode_debug(vm, pc, "EXIT AWAIT"); + await = (njs_vmcode_await_t *) pc; - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + ret = njs_vmcode_await(vm, await, promise_cap, async_ctx); - return ret; + njs_vmcode_debug(vm, pc, "EXIT AWAIT"); - case NJS_VMCODE_TRY_START: - ret = njs_vmcode_try_start(vm, value1, value2, pc); - if (njs_slow_path(ret == NJS_ERROR)) { - goto error; - } + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - break; + return ret; - case NJS_VMCODE_THROW: - njs_vmcode_operand(vm, (njs_index_t) value2, value2); - vm->retval = *value2; - goto error; + CASE (NJS_VMCODE_TRY_START): + njs_vmcode_debug_opcode(); - 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; - - case NJS_VMCODE_TRY_END: - ret = njs_vmcode_try_end(vm, value1, value2); - break; - - /* - * njs_vmcode_catch() is set on the start of a "catch" block to - * store exception and to remove a "try" block if there is no - * "finally" block or to update a catch address to the start of - * a "finally" block. - * njs_vmcode_catch() is set on the start of a "finally" block - * to store uncaught exception and to remove a "try" block. - */ - case NJS_VMCODE_CATCH: - *value1 = vm->retval; - - if ((njs_jump_off_t) value2 == sizeof(njs_vmcode_catch_t)) { - ret = njs_vmcode_try_end(vm, value1, value2); - - } else { - frame = (njs_frame_t *) vm->top_frame; - frame->exception.catch = pc + (njs_jump_off_t) value2; - ret = sizeof(njs_vmcode_catch_t); - } + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); - break; + ret = njs_vmcode_try_start(vm, value1, value2, pc); + if (njs_slow_path(ret == NJS_ERROR)) { + goto error; + } - case NJS_VMCODE_FINALLY: - finally = (njs_vmcode_finally_t *) pc; - value1 = njs_scope_value(vm, finally->exit_value); + BREAK; - ret = njs_vmcode_finally(vm, value1, value2, pc); + CASE (NJS_VMCODE_THROW): + njs_vmcode_debug_opcode(); - switch (ret) { - case NJS_OK: + value2 = (njs_value_t *) vmcode->operand1; - njs_vmcode_debug(vm, pc, "EXIT FINALLY"); + njs_vmcode_operand(vm, (njs_index_t) value2, value2); + vm->retval = *value2; - return NJS_OK; - case NJS_ERROR: - goto error; - } + goto error; - break; + CASE (NJS_VMCODE_TRY_BREAK): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_LET: - var = (njs_vmcode_variable_t *) pc; - value1 = njs_scope_value(vm, var->dst); + value2 = (njs_value_t *) vmcode->operand1; - if (njs_is_valid(value1)) { - value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); - if (njs_slow_path(value1 == NULL)) { - njs_memory_error(vm); - goto error; - } + try_trampoline = (njs_vmcode_try_trampoline_t *) pc; + value1 = njs_scope_value(vm, try_trampoline->exit_value); - njs_scope_value_set(vm, var->dst, value1); - } + ret = njs_vmcode_try_break(vm, value1, value2); + BREAK; - njs_set_undefined(value1); + CASE (NJS_VMCODE_TRY_CONTINUE): + njs_vmcode_debug_opcode(); - ret = sizeof(njs_vmcode_variable_t); - break; + value2 = (njs_value_t *) vmcode->operand1; - case NJS_VMCODE_LET_UPDATE: - var = (njs_vmcode_variable_t *) pc; - value2 = njs_scope_value(vm, var->dst); + try_trampoline = (njs_vmcode_try_trampoline_t *) pc; + value1 = njs_scope_value(vm, try_trampoline->exit_value); - value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); - if (njs_slow_path(value1 == NULL)) { - njs_memory_error(vm); - goto error; - } + ret = njs_vmcode_try_continue(vm, value1, value2); + BREAK; - *value1 = *value2; + CASE (NJS_VMCODE_TRY_END): + njs_vmcode_debug_opcode(); - njs_scope_value_set(vm, var->dst, value1); + ret = njs_vmcode_try_end(vm, NULL, (njs_value_t *) vmcode->operand1); + BREAK; - ret = sizeof(njs_vmcode_variable_t); - break; + /* + * njs_vmcode_catch() is set on the start of a "catch" block to + * store exception and to remove a "try" block if there is no + * "finally" block or to update a catch address to the start of + * a "finally" block. + * njs_vmcode_catch() is set on the start of a "finally" block + * to store uncaught exception and to remove a "try" block. + */ + CASE (NJS_VMCODE_CATCH): + njs_vmcode_debug_opcode(); - case NJS_VMCODE_INITIALIZATION_TEST: - var = (njs_vmcode_variable_t *) pc; - value1 = njs_scope_value(vm, var->dst); + value2 = (njs_value_t *) vmcode->operand1; + njs_vmcode_operand(vm, vmcode->operand2, value1); - if (njs_is_valid(value1)) { - ret = sizeof(njs_vmcode_variable_t); - break; - } + *value1 = vm->retval; - /* Fall through. */ + if ((njs_jump_off_t) value2 == sizeof(njs_vmcode_catch_t)) { + ret = njs_vmcode_try_end(vm, value1, value2); - case NJS_VMCODE_NOT_INITIALIZED: - njs_reference_error(vm, "cannot access variable " - "before initialization"); - goto error; + } else { + frame = (njs_frame_t *) vm->top_frame; + frame->exception.catch = pc + (njs_jump_off_t) value2; + ret = sizeof(njs_vmcode_catch_t); + } - case NJS_VMCODE_ERROR: - njs_vmcode_error(vm, pc); - goto error; + BREAK; - case NJS_VMCODE_ASSIGNMENT_ERROR: - njs_type_error(vm, "assignment to constant variable"); - goto error; + CASE (NJS_VMCODE_FINALLY): + njs_vmcode_debug_opcode(); + + value2 = (njs_value_t *) vmcode->operand1; + + finally = (njs_vmcode_finally_t *) pc; + value1 = njs_scope_value(vm, finally->exit_value); + + ret = njs_vmcode_finally(vm, NULL, value2, pc); + + switch (ret) { + case NJS_OK: + njs_vmcode_debug(vm, pc, "EXIT FINALLY"); + + return NJS_OK; + case NJS_ERROR: + goto error; + } - default: - njs_internal_error(vm, "%d has NO retval", op); + BREAK; + + CASE (NJS_VMCODE_LET): + njs_vmcode_debug_opcode(); + + var = (njs_vmcode_variable_t *) pc; + value1 = njs_scope_value(vm, var->dst); + + if (njs_is_valid(value1)) { + value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); + if (njs_slow_path(value1 == NULL)) { + njs_memory_error(vm); goto error; } + + njs_scope_value_set(vm, var->dst, value1); + } + + njs_set_undefined(value1); + + ret = sizeof(njs_vmcode_variable_t); + BREAK; + + CASE (NJS_VMCODE_LET_UPDATE): + njs_vmcode_debug_opcode(); + + var = (njs_vmcode_variable_t *) pc; + value2 = njs_scope_value(vm, var->dst); + + value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); + if (njs_slow_path(value1 == NULL)) { + njs_memory_error(vm); + goto error; + } + + *value1 = *value2; + + njs_scope_value_set(vm, var->dst, value1); + + ret = sizeof(njs_vmcode_variable_t); + BREAK; + + CASE (NJS_VMCODE_INITIALIZATION_TEST): + njs_vmcode_debug_opcode(); + + var = (njs_vmcode_variable_t *) pc; + value1 = njs_scope_value(vm, var->dst); + + if (njs_is_valid(value1)) { + ret = sizeof(njs_vmcode_variable_t); + BREAK; } - pc += ret; + FALLTHROUGH; + CASE (NJS_VMCODE_NOT_INITIALIZED): + njs_vmcode_debug_opcode(); + + njs_reference_error(vm, "cannot access variable before initialization"); + goto error; + + CASE (NJS_VMCODE_ERROR): + njs_vmcode_debug_opcode(); + + njs_vmcode_error(vm, pc); + goto error; + + CASE (NJS_VMCODE_ASSIGNMENT_ERROR): + njs_vmcode_debug_opcode(); + + njs_type_error(vm, "assignment to constant variable"); + goto error; + } error: @@ -1114,7 +1827,7 @@ error: if (catch != NULL) { pc = catch; - goto next; + NEXT; } } @@ -1905,7 +2618,7 @@ njs_function_frame_create(njs_vm_t *vm, njs_value_t *value, } -njs_object_t * +inline njs_object_t * njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor) { njs_value_t proto, bound; diff --git a/src/njs_vmcode.h b/src/njs_vmcode.h index 7353b168..9fe89f3c 100644 --- a/src/njs_vmcode.h +++ b/src/njs_vmcode.h @@ -46,16 +46,13 @@ enum { NJS_VMCODE_METHOD_FRAME, NJS_VMCODE_FUNCTION_CALL, NJS_VMCODE_PROPERTY_NEXT, - NJS_VMCODE_THIS, NJS_VMCODE_ARGUMENTS, NJS_VMCODE_PROTO_INIT, NJS_VMCODE_TO_PROPERTY_KEY, NJS_VMCODE_TO_PROPERTY_KEY_CHK, NJS_VMCODE_SET_FUNCTION_NAME, NJS_VMCODE_IMPORT, - NJS_VMCODE_AWAIT, - NJS_VMCODE_TRY_START, NJS_VMCODE_THROW, NJS_VMCODE_TRY_BREAK, @@ -63,21 +60,13 @@ enum { NJS_VMCODE_TRY_END, NJS_VMCODE_CATCH, NJS_VMCODE_FINALLY, - NJS_VMCODE_LET, NJS_VMCODE_LET_UPDATE, NJS_VMCODE_INITIALIZATION_TEST, NJS_VMCODE_NOT_INITIALIZED, NJS_VMCODE_ASSIGNMENT_ERROR, - NJS_VMCODE_ERROR, - - NJS_VMCODE_NORET = 127 -}; - - -enum { - NJS_VMCODE_MOVE = NJS_VMCODE_NORET + 1, + NJS_VMCODE_MOVE, NJS_VMCODE_PROPERTY_GET, NJS_VMCODE_INCREMENT, NJS_VMCODE_POST_INCREMENT, @@ -85,7 +74,6 @@ enum { NJS_VMCODE_POST_DECREMENT, NJS_VMCODE_TRY_RETURN, NJS_VMCODE_GLOBAL_GET, - NJS_VMCODE_LESS, NJS_VMCODE_GREATER, NJS_VMCODE_LESS_OR_EQUAL, @@ -93,7 +81,6 @@ enum { NJS_VMCODE_ADDITION, NJS_VMCODE_EQUAL, NJS_VMCODE_NOT_EQUAL, - NJS_VMCODE_SUBSTRACTION, NJS_VMCODE_MULTIPLICATION, NJS_VMCODE_EXPONENTIATION, @@ -110,15 +97,11 @@ enum { 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, @@ -127,14 +110,12 @@ enum { NJS_VMCODE_ARRAY, NJS_VMCODE_FUNCTION, NJS_VMCODE_REGEXP, - NJS_VMCODE_INSTANCE_OF, NJS_VMCODE_TYPEOF, NJS_VMCODE_VOID, NJS_VMCODE_DELETE, NJS_VMCODE_DEBUGGER, - - NJS_VMCODE_NOP = 255 + NJS_VMCODES }; @@ -457,8 +438,14 @@ njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor); (code != NULL) ? &code->name : &njs_entry_unknown); \ } while (0); \ } + +#define njs_vmcode_debug_opcode() \ + if (vm->options.opcode_debug) { \ + njs_disassemble(pc, NULL, 1, NULL); \ + } #else #define njs_vmcode_debug(vm, pc, prefix) +#define njs_vmcode_debug_opcode() #endif #endif /* _NJS_VMCODE_H_INCLUDED_ */ -- 2.47.3