diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c
index adc152f80..c188098b4 100644
--- a/jerry-core/parser/js/byte-code.c
+++ b/jerry-core/parser/js/byte-code.c
@@ -28,7 +28,7 @@ JERRY_STATIC_ASSERT (offsetof (cbc_uint8_arguments_t, script_value) == offsetof
* whenever new bytecodes are introduced or existing ones have been deleted.
*/
JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed);
-JERRY_STATIC_ASSERT (CBC_EXT_END == 165, number_of_cbc_ext_opcodes_changed);
+JERRY_STATIC_ASSERT (CBC_EXT_END == 166, number_of_cbc_ext_opcodes_changed);
#if JERRY_PARSER || JERRY_PARSER_DUMP_BYTE_CODE
diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h
index 9fb6b013a..13be776af 100644
--- a/jerry-core/parser/js/byte-code.h
+++ b/jerry-core/parser/js/byte-code.h
@@ -506,6 +506,7 @@
CBC_FORWARD_BRANCH (CBC_EXT_BRANCH_IF_NULLISH, -1, VM_OC_BRANCH_IF_NULLISH) \
\
/* Basic opcodes. */ \
+ CBC_OPCODE (CBC_EXT_POP_REFERENCE, CBC_NO_FLAG, -2, VM_OC_POP_REFERENCE) \
CBC_OPCODE (CBC_EXT_CREATE_ARGUMENTS, CBC_HAS_LITERAL_ARG, 0, VM_OC_CREATE_ARGUMENTS) \
CBC_OPCODE (CBC_EXT_CREATE_VAR_EVAL, CBC_HAS_LITERAL_ARG, 0, VM_OC_EXT_VAR_EVAL) \
CBC_OPCODE (CBC_EXT_CREATE_VAR_FUNC_EVAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 0, VM_OC_EXT_VAR_EVAL) \
diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c
index 11faa3da9..d42a41467 100644
--- a/jerry-core/parser/js/js-lexer.c
+++ b/jerry-core/parser/js/js-lexer.c
@@ -1509,6 +1509,55 @@ lexer_parse_number (parser_context_t *context_p) /**< context */
break; \
}
+/**
+ * Four tokens, where the first is the prefix of the other three
+ * and the second is prefix of the fourth (e.g. &, &&, &=, &&= ).
+ *
+ * @param char1 first character
+ * @param type1 type of the first character
+ * @param char2 second character
+ * @param type2 type of the second character
+ * @param char3 third character
+ * @param type3 type of the third character
+ * @param char4 fourth character
+ * @param type4 type of the fourth character
+ */
+#if JERRY_ESNEXT
+#define LEXER_TYPE_D_TOKEN(char1, type1, char2, type2, char3, type3, char4, type4) \
+ case (uint8_t) (char1): \
+ { \
+ if (length >= 2) \
+ { \
+ if (context_p->source_p[1] == (uint8_t) (char2)) \
+ { \
+ context_p->token.type = (type2); \
+ length = 2; \
+ break; \
+ } \
+ \
+ if (context_p->source_p[1] == (uint8_t) (char3)) \
+ { \
+ if (length >= 3 && context_p->source_p[2] == (uint8_t) (char4)) \
+ { \
+ context_p->token.type = (type4); \
+ length = 3; \
+ break; \
+ } \
+ context_p->token.type = (type3); \
+ length = 2; \
+ break; \
+ } \
+ } \
+ \
+ context_p->token.type = (type1); \
+ length = 1; \
+ break; \
+ }
+#else /* !JERRY_ESNEXT */
+#define LEXER_TYPE_D_TOKEN(char1, type1, char2, type2, char3, type3, char4, type4) \
+ LEXER_TYPE_C_TOKEN (char1, type1, char2, type2, char3, type3)
+#endif /* JERRY_ESNEXT */
+
/**
* Get next token.
*/
@@ -1759,18 +1808,22 @@ lexer_next_token (parser_context_t *context_p) /**< context */
LEXER_TYPE_B_TOKEN (LIT_CHAR_SLASH, LEXER_DIVIDE, LIT_CHAR_EQUALS, LEXER_ASSIGN_DIVIDE)
LEXER_TYPE_B_TOKEN (LIT_CHAR_PERCENT, LEXER_MODULO, LIT_CHAR_EQUALS, LEXER_ASSIGN_MODULO)
- LEXER_TYPE_C_TOKEN (LIT_CHAR_AMPERSAND,
+ LEXER_TYPE_D_TOKEN (LIT_CHAR_AMPERSAND,
LEXER_BIT_AND,
LIT_CHAR_EQUALS,
LEXER_ASSIGN_BIT_AND,
LIT_CHAR_AMPERSAND,
- LEXER_LOGICAL_AND)
- LEXER_TYPE_C_TOKEN (LIT_CHAR_VLINE,
+ LEXER_LOGICAL_AND,
+ LIT_CHAR_EQUALS,
+ LEXER_ASSIGN_LOGICAL_AND)
+ LEXER_TYPE_D_TOKEN (LIT_CHAR_VLINE,
LEXER_BIT_OR,
LIT_CHAR_EQUALS,
LEXER_ASSIGN_BIT_OR,
LIT_CHAR_VLINE,
- LEXER_LOGICAL_OR)
+ LEXER_LOGICAL_OR,
+ LIT_CHAR_EQUALS,
+ LEXER_ASSIGN_LOGICAL_OR)
LEXER_TYPE_B_TOKEN (LIT_CHAR_CIRCUMFLEX, LEXER_BIT_XOR, LIT_CHAR_EQUALS, LEXER_ASSIGN_BIT_XOR)
@@ -1782,6 +1835,12 @@ lexer_next_token (parser_context_t *context_p) /**< context */
{
if (context_p->source_p[1] == (uint8_t) LIT_CHAR_QUESTION)
{
+ if (length >= 3 && context_p->source_p[2] == (uint8_t) LIT_CHAR_EQUALS)
+ {
+ context_p->token.type = LEXER_ASSIGN_NULLISH_COALESCING;
+ length = 3;
+ break;
+ }
context_p->token.type = LEXER_NULLISH_COALESCING;
length = 2;
break;
diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h
index e4d661725..8d06baab5 100644
--- a/jerry-core/parser/js/js-lexer.h
+++ b/jerry-core/parser/js/js-lexer.h
@@ -123,6 +123,9 @@ typedef enum
LEXER_ASSIGN_MODULO, /**< "%=" (prec: 3) */
#if JERRY_ESNEXT
LEXER_ASSIGN_EXPONENTIATION, /**< "**=" (prec: 3) */
+ LEXER_ASSIGN_NULLISH_COALESCING, /**< "??=" (prec: 3) */
+ LEXER_ASSIGN_LOGICAL_OR, /**< "||=" (prec: 3) */
+ LEXER_ASSIGN_LOGICAL_AND, /**< "&&=" (prec: 3) */
#endif /* JERRY_ESNEXT */
LEXER_ASSIGN_LEFT_SHIFT, /**< "<<=" (prec: 3) */
LEXER_ASSIGN_RIGHT_SHIFT, /**< ">>=" (prec: 3) */
@@ -217,6 +220,7 @@ typedef enum
LEXER_ASSIGN_CONST, /**< a const binding is reassigned */
LEXER_INVALID_PATTERN, /**< special value for invalid destructuring pattern */
LEXER_PRIVATE_PRIMARY_EXPR, /**< private field in primary expession position */
+ LEXER_ASSIGN_REFERENCE, /**< special value for reference assignment */
#endif /* JERRY_ESNEXT */
/* Keywords which are not keyword tokens. */
diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c
index e8ea35473..2179d9a7c 100644
--- a/jerry-core/parser/js/js-parser-expr.c
+++ b/jerry-core/parser/js/js-parser-expr.c
@@ -73,6 +73,9 @@ static const uint8_t parser_binary_precedence_table[] = {
3, /**< "^=" */
#if JERRY_ESNEXT
3, /**< "**=" */
+ 3, /**< "??=" */
+ 3, /**< "||=" */
+ 3, /**< "&&=" */
#endif /* JERRY_ESNEXT */
4, /**< "?"*/
#if JERRY_ESNEXT
@@ -107,7 +110,7 @@ static const uint8_t parser_binary_precedence_table[] = {
};
#if JERRY_ESNEXT
-JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 39,
+JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 42,
parser_binary_precedence_table_should_have_39_values_in_es2015);
#else /* !JERRY_ESNEXT */
JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 36,
@@ -3180,6 +3183,90 @@ parser_check_invalid_logical_op (parser_context_t *context_p, /**< context */
#endif /* JERRY_ESNEXT */
+/**
+ * Append a binary lvalue token.
+ */
+static void
+parser_append_binary_lvalue_token (parser_context_t *context_p, /**< context */
+ bool is_logical_assignment) /**< true - if form logical assignment reference
+ * false - otherwise */
+{
+ if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode)
+ && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
+ {
+ parser_check_invalid_assign (context_p);
+
+ parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE);
+
+#if JERRY_ESNEXT
+ if (!is_logical_assignment && scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index))
+ {
+ parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST);
+ }
+#endif /* JERRY_ESNEXT */
+ }
+ else if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode))
+ {
+ context_p->last_cbc_opcode = PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE (context_p->last_cbc_opcode);
+ }
+ else
+ {
+ /* Invalid LeftHandSide expression. */
+#if JERRY_ESNEXT
+ parser_check_invalid_new_target (context_p, CBC_ASSIGN);
+ parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT);
+#else /* !JERRY_ESNEXT */
+ parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
+#endif /* JERRY_ESNEXT */
+
+ parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE);
+ }
+
+ if (!is_logical_assignment)
+ {
+ parser_stack_push_uint8 (context_p, (uint8_t) context_p->token.type);
+ }
+} /* parser_append_binary_lvalue_token */
+
+/**
+ * Append a logical token.
+ */
+static void
+parser_append_logical_token (parser_context_t *context_p, /**< context */
+ uint16_t opcode) /**< opcode */
+{
+#if JERRY_ESNEXT
+ if (opcode != PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH))
+ {
+ parser_check_invalid_logical_op (context_p, LEXER_NULLISH_COALESCING, LEXER_NULLISH_COALESCING);
+ }
+#endif /* JERRY_ESNEXT */
+
+ parser_branch_t branch;
+
+ parser_emit_cbc_forward_branch (context_p, opcode, &branch);
+ parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
+ parser_stack_push_uint8 (context_p, (uint8_t) context_p->token.type);
+} /* parser_append_logical_token */
+
+#if JERRY_ESNEXT
+/**
+ * Append a logical token.
+ */
+static void
+parser_append_logical_assignment_token (parser_context_t *context_p, /**< context */
+ uint16_t opcode) /**< opcode */
+{
+ uint16_t last_cbc_opcode = context_p->last_cbc_opcode;
+ parser_append_binary_single_assignment_token (context_p, 0);
+ parser_stack_change_last_uint8 (context_p, LEXER_ASSIGN_REFERENCE);
+ context_p->last_cbc_opcode = last_cbc_opcode;
+
+ parser_append_binary_lvalue_token (context_p, true);
+ parser_append_logical_token (context_p, opcode);
+} /* parser_append_logical_assignment_token */
+#endif /* JERRY_ESNEXT */
+
/**
* Append a binary token.
*/
@@ -3189,80 +3276,270 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */
JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type));
parser_push_result (context_p);
- if (context_p->token.type == LEXER_ASSIGN)
+ switch (context_p->token.type)
{
- parser_append_binary_single_assignment_token (context_p, 0);
- return;
- }
-
- if (LEXER_IS_BINARY_LVALUE_OP_TOKEN (context_p->token.type))
- {
- if (PARSER_IS_PUSH_LITERALS_WITH_THIS (context_p->last_cbc_opcode)
- && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
+ case LEXER_ASSIGN:
{
- parser_check_invalid_assign (context_p);
-
- parser_emit_ident_reference (context_p, CBC_PUSH_IDENT_REFERENCE);
-
-#if JERRY_ESNEXT
- if (scanner_literal_is_const_reg (context_p, context_p->last_cbc.literal_index))
- {
- parser_stack_push_uint8 (context_p, LEXER_ASSIGN_CONST);
- }
-#endif /* JERRY_ESNEXT */
+ parser_append_binary_single_assignment_token (context_p, 0);
+ break;
}
- else if (PARSER_IS_PUSH_PROP (context_p->last_cbc_opcode))
- {
- context_p->last_cbc_opcode = PARSER_PUSH_PROP_TO_PUSH_PROP_REFERENCE (context_p->last_cbc_opcode);
- }
- else
- {
- /* Invalid LeftHandSide expression. */
+ /* Binary lvalue-opcodes */
+ case LEXER_ASSIGN_ADD:
+ case LEXER_ASSIGN_SUBTRACT:
+ case LEXER_ASSIGN_MULTIPLY:
+ case LEXER_ASSIGN_DIVIDE:
+ case LEXER_ASSIGN_MODULO:
#if JERRY_ESNEXT
- parser_check_invalid_new_target (context_p, CBC_ASSIGN);
- parser_raise_error (context_p, PARSER_ERR_INVALID_LHS_ASSIGNMENT);
-#else /* !JERRY_ESNEXT */
- parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
+ case LEXER_ASSIGN_EXPONENTIATION:
#endif /* JERRY_ESNEXT */
-
- parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE);
+ case LEXER_ASSIGN_LEFT_SHIFT:
+ case LEXER_ASSIGN_RIGHT_SHIFT:
+ case LEXER_ASSIGN_UNS_RIGHT_SHIFT:
+ case LEXER_ASSIGN_BIT_AND:
+ case LEXER_ASSIGN_BIT_OR:
+ case LEXER_ASSIGN_BIT_XOR:
+ {
+ parser_append_binary_lvalue_token (context_p, false);
+ break;
+ }
+#if JERRY_ESNEXT
+ case LEXER_ASSIGN_NULLISH_COALESCING:
+ {
+ parser_append_logical_assignment_token (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH));
+ break;
+ }
+ case LEXER_ASSIGN_LOGICAL_OR:
+ {
+ parser_append_logical_assignment_token (context_p, CBC_BRANCH_IF_LOGICAL_TRUE);
+ break;
+ }
+ case LEXER_ASSIGN_LOGICAL_AND:
+ {
+ parser_append_logical_assignment_token (context_p, CBC_BRANCH_IF_LOGICAL_FALSE);
+ break;
+ }
+ case LEXER_NULLISH_COALESCING:
+ {
+ parser_append_logical_token (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH));
+ break;
+ }
+#endif /* JERRY_ESNEXT */
+ case LEXER_LOGICAL_OR:
+ {
+ parser_append_logical_token (context_p, CBC_BRANCH_IF_LOGICAL_TRUE);
+ break;
+ }
+ case LEXER_LOGICAL_AND:
+ {
+ parser_append_logical_token (context_p, CBC_BRANCH_IF_LOGICAL_FALSE);
+ break;
+ }
+ default:
+ {
+ parser_stack_push_uint8 (context_p, (uint8_t) context_p->token.type);
+ break;
}
}
- else if (context_p->token.type == LEXER_LOGICAL_OR || context_p->token.type == LEXER_LOGICAL_AND)
- {
- parser_branch_t branch;
- uint16_t opcode = CBC_BRANCH_IF_LOGICAL_TRUE;
-
- if (context_p->token.type == LEXER_LOGICAL_AND)
- {
- opcode = CBC_BRANCH_IF_LOGICAL_FALSE;
- }
-
-#if JERRY_ESNEXT
- parser_check_invalid_logical_op (context_p, LEXER_NULLISH_COALESCING, LEXER_NULLISH_COALESCING);
-#endif /* JERRY_ESNEXT */
-
- parser_emit_cbc_forward_branch (context_p, opcode, &branch);
- parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
- parser_stack_push_uint8 (context_p, context_p->token.type);
- return;
- }
-#if JERRY_ESNEXT
- else if (context_p->token.type == LEXER_NULLISH_COALESCING)
- {
- parser_branch_t branch;
-
- uint16_t opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH);
- parser_emit_cbc_forward_branch (context_p, opcode, &branch);
- parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
- parser_stack_push_uint8 (context_p, context_p->token.type);
- return;
- }
-#endif /* JERRY_ESNEXT */
-
- parser_stack_push_uint8 (context_p, context_p->token.type);
} /* parser_append_binary_token */
+/**
+ * Emit opcode for binary assignment token.
+ */
+static void
+parser_process_binary_assignment_token (parser_context_t *context_p, /**< context */
+ uint8_t token) /**< token */
+{
+#if !JERRY_ESNEXT
+ JERRY_UNUSED (token);
+#endif /* !JERRY_ESNEXT */
+
+ uint16_t index = PARSER_INVALID_LITERAL_INDEX;
+ uint16_t opcode = context_p->stack_top_uint8;
+
+#if JERRY_ESNEXT
+ if (JERRY_UNLIKELY (opcode == CBC_EXT_OPCODE))
+ {
+ parser_stack_pop_uint8 (context_p);
+ JERRY_ASSERT (context_p->stack_top_uint8 == CBC_EXT_ASSIGN_SUPER
+ || context_p->stack_top_uint8 == CBC_EXT_ASSIGN_PRIVATE);
+ opcode = PARSER_TO_EXT_OPCODE (context_p->stack_top_uint8);
+ parser_stack_pop_uint8 (context_p);
+ }
+ else
+#endif /* JERRY_ESNEXT */
+ {
+ parser_stack_pop_uint8 (context_p);
+
+ if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG)
+ {
+ JERRY_ASSERT (opcode == CBC_ASSIGN_SET_IDENT || opcode == CBC_ASSIGN_PROP_LITERAL
+ || opcode == CBC_ASSIGN_PROP_THIS_LITERAL || opcode == CBC_ASSIGN_LET_CONST
+ || opcode == CBC_INIT_ARG_OR_CATCH || opcode == CBC_INIT_LET || opcode == CBC_INIT_CONST);
+
+ index = parser_stack_pop_uint16 (context_p);
+ }
+ }
+
+#if JERRY_ESNEXT
+ bool group_expr_assingment = false;
+
+ if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_GROUP_EXPR))
+ {
+ group_expr_assingment = true;
+ parser_stack_pop_uint8 (context_p);
+ }
+
+ if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_CONST))
+ {
+ parser_stack_pop_uint8 (context_p);
+ parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR);
+ }
+#endif /* JERRY_ESNEXT */
+
+ if (index == PARSER_INVALID_LITERAL_INDEX)
+ {
+#if JERRY_ESNEXT
+ if (JERRY_UNLIKELY (token == LEXER_ASSIGN_REFERENCE))
+ {
+ opcode = CBC_ASSIGN_PUSH_RESULT;
+ }
+#endif /* JERRY_ESNEXT */
+
+ parser_emit_cbc (context_p, opcode);
+ return;
+ }
+
+#if JERRY_ESNEXT
+ if (!group_expr_assingment)
+ {
+ uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p);
+
+ if (function_literal_index == PARSER_ANONYMOUS_CLASS)
+ {
+ uint16_t name_index = scanner_save_literal (context_p, index);
+ parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index);
+ }
+ else if (function_literal_index < PARSER_NAMED_FUNCTION)
+ {
+ parser_set_function_name (context_p, function_literal_index, (uint16_t) index, 0);
+ }
+ }
+
+ if (JERRY_UNLIKELY (token == LEXER_ASSIGN_REFERENCE))
+ {
+ parser_emit_cbc (context_p, CBC_ASSIGN_PUSH_RESULT);
+ return;
+ }
+#endif /* JERRY_ESNEXT */
+
+ if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && opcode == CBC_ASSIGN_SET_IDENT)
+ {
+ JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
+
+ context_p->last_cbc.value = index;
+ context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT;
+ return;
+ }
+
+ parser_emit_cbc_literal (context_p, (uint16_t) opcode, index);
+
+ if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL && (context_p->stack_depth >= context_p->stack_limit))
+ {
+ /* Stack limit is increased for VM_OC_ASSIGN_PROP_THIS. Needed by vm.c. */
+ JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit);
+
+ context_p->stack_limit++;
+
+ if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT)
+ {
+ parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED);
+ }
+ }
+} /* parser_process_binary_opcodes */
+
+/**
+ * Emit opcode for logical tokens.
+ */
+static void
+parser_process_logical_token (parser_context_t *context_p) /**< context */
+{
+ parser_branch_t branch;
+ parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
+ parser_set_branch_to_current_position (context_p, &branch);
+} /* parser_process_logical_token */
+
+#if JERRY_ESNEXT
+/**
+ * Emit opcode for logical assignment tokens.
+ */
+static void
+parser_process_logical_assignment_token (parser_context_t *context_p) /**< context */
+{
+ parser_branch_t condition_branch;
+ parser_stack_pop (context_p, &condition_branch, sizeof (parser_branch_t));
+
+ uint8_t token = context_p->stack_top_uint8;
+ JERRY_ASSERT (token == LEXER_ASSIGN_REFERENCE);
+ parser_stack_pop_uint8 (context_p);
+ parser_process_binary_assignment_token (context_p, token);
+
+ parser_branch_t prop_reference_branch;
+ parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &prop_reference_branch);
+
+ parser_set_branch_to_current_position (context_p, &condition_branch);
+ parser_emit_cbc_ext (context_p, CBC_EXT_POP_REFERENCE);
+
+ JERRY_ASSERT (context_p->stack_limit - context_p->stack_depth >= 2);
+ PARSER_PLUS_EQUAL_U16 (context_p->stack_depth, 2);
+
+ parser_set_branch_to_current_position (context_p, &prop_reference_branch);
+} /* parser_process_logical_assignment_token */
+
+#endif /* JERRY_ESNEXT */
+
+/**
+ * Emit opcode for binary tokens.
+ */
+static void
+parser_process_binary_token (parser_context_t *context_p, /**< context */
+ uint8_t token) /**< token */
+{
+ uint16_t opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token);
+
+ if (PARSER_IS_PUSH_NUMBER (context_p->last_cbc_opcode))
+ {
+ lexer_convert_push_number_to_push_literal (context_p);
+ }
+
+ if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
+ {
+ JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, opcode + CBC_BINARY_WITH_LITERAL));
+ context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_LITERAL);
+ return;
+ }
+
+ if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
+ {
+ JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_WITH_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
+ context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_TWO_LITERALS);
+ return;
+ }
+
+ parser_emit_cbc (context_p, opcode);
+} /* parser_process_binary_token */
+
+/**
+ * Emit opcode for binary lvalue tokens.
+ */
+static void
+parser_process_binary_lvalue_token (parser_context_t *context_p, /**< context */
+ uint8_t token) /**< token */
+{
+ parser_stack_push_uint8 (context_p, CBC_ASSIGN);
+ parser_stack_push_uint8 (context_p, LEXER_ASSIGN);
+ parser_stack_push_uint8 (context_p, lexer_convert_binary_lvalue_token_to_binary (token));
+} /* parser_process_binary_lvalue_token */
+
/**
* Emit opcode for binary computations.
*/
@@ -3273,7 +3550,6 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */
while (true)
{
uint8_t token = context_p->stack_top_uint8;
- uint16_t opcode;
/* For left-to-right operators (all binary operators except assignment
* and logical operators), the byte code is flushed if the precedence
@@ -3290,149 +3566,68 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */
parser_push_result (context_p);
parser_stack_pop_uint8 (context_p);
- if (token == LEXER_ASSIGN)
+ switch (token)
{
- uint16_t index = PARSER_INVALID_LITERAL_INDEX;
- opcode = context_p->stack_top_uint8;
-
+ case LEXER_ASSIGN:
+ {
+ parser_process_binary_assignment_token (context_p, token);
+ continue;
+ }
+ /* Binary lvalue-opcodes */
+ case LEXER_ASSIGN_ADD:
+ case LEXER_ASSIGN_SUBTRACT:
+ case LEXER_ASSIGN_MULTIPLY:
+ case LEXER_ASSIGN_DIVIDE:
+ case LEXER_ASSIGN_MODULO:
#if JERRY_ESNEXT
- if (JERRY_UNLIKELY (opcode == CBC_EXT_OPCODE))
- {
- parser_stack_pop_uint8 (context_p);
- JERRY_ASSERT (context_p->stack_top_uint8 == CBC_EXT_ASSIGN_SUPER
- || context_p->stack_top_uint8 == CBC_EXT_ASSIGN_PRIVATE);
- opcode = PARSER_TO_EXT_OPCODE (context_p->stack_top_uint8);
- parser_stack_pop_uint8 (context_p);
- }
- else
+ case LEXER_ASSIGN_EXPONENTIATION:
#endif /* JERRY_ESNEXT */
+ case LEXER_ASSIGN_LEFT_SHIFT:
+ case LEXER_ASSIGN_RIGHT_SHIFT:
+ case LEXER_ASSIGN_UNS_RIGHT_SHIFT:
+ case LEXER_ASSIGN_BIT_AND:
+ case LEXER_ASSIGN_BIT_OR:
+ case LEXER_ASSIGN_BIT_XOR:
{
- parser_stack_pop_uint8 (context_p);
-
- if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG)
- {
- JERRY_ASSERT (opcode == CBC_ASSIGN_SET_IDENT || opcode == CBC_ASSIGN_PROP_LITERAL
- || opcode == CBC_ASSIGN_PROP_THIS_LITERAL || opcode == CBC_ASSIGN_LET_CONST
- || opcode == CBC_INIT_ARG_OR_CATCH || opcode == CBC_INIT_LET || opcode == CBC_INIT_CONST);
-
- index = parser_stack_pop_uint16 (context_p);
- }
+ parser_process_binary_lvalue_token (context_p, token);
+ continue;
}
-
#if JERRY_ESNEXT
- bool group_expr_assingment = false;
-
- if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_GROUP_EXPR))
+ case LEXER_ASSIGN_NULLISH_COALESCING:
+ case LEXER_ASSIGN_LOGICAL_OR:
+ case LEXER_ASSIGN_LOGICAL_AND:
{
- group_expr_assingment = true;
- parser_stack_pop_uint8 (context_p);
- }
-
- if (JERRY_UNLIKELY (context_p->stack_top_uint8 == LEXER_ASSIGN_CONST))
- {
- parser_stack_pop_uint8 (context_p);
- parser_emit_cbc_ext (context_p, CBC_EXT_THROW_ASSIGN_CONST_ERROR);
+ parser_process_logical_assignment_token (context_p);
+ continue;
}
+ case LEXER_NULLISH_COALESCING:
#endif /* JERRY_ESNEXT */
-
- if (index != PARSER_INVALID_LITERAL_INDEX)
+ case LEXER_LOGICAL_OR:
+ case LEXER_LOGICAL_AND:
{
+ parser_process_logical_token (context_p);
+ continue;
+ }
#if JERRY_ESNEXT
- if (!group_expr_assingment)
+ case LEXER_KEYW_IN:
+ {
+ if (context_p->stack_top_uint8 == LEXER_PRIVATE_PRIMARY_EXPR)
{
- uint16_t function_literal_index = parser_check_anonymous_function_declaration (context_p);
-
- if (function_literal_index == PARSER_ANONYMOUS_CLASS)
- {
- uint16_t name_index = scanner_save_literal (context_p, index);
- parser_emit_cbc_ext_literal (context_p, CBC_EXT_SET_CLASS_NAME, name_index);
- }
- else if (function_literal_index < PARSER_NAMED_FUNCTION)
- {
- parser_set_function_name (context_p, function_literal_index, (uint16_t) index, 0);
- }
- }
-#endif /* JERRY_ESNEXT */
-
- if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && opcode == CBC_ASSIGN_SET_IDENT)
- {
- JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
-
- context_p->last_cbc.value = index;
- context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT;
+ parser_stack_pop_uint8 (context_p);
+ uint16_t lit_id = parser_stack_pop_uint16 (context_p);
+ parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_IN, lit_id);
continue;
}
- parser_emit_cbc_literal (context_p, (uint16_t) opcode, index);
-
- if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL && (context_p->stack_depth >= context_p->stack_limit))
- {
- /* Stack limit is increased for VM_OC_ASSIGN_PROP_THIS. Needed by vm.c. */
- JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit);
-
- context_p->stack_limit++;
-
- if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT)
- {
- parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED);
- }
- }
- continue;
+ /* FALLTHRU */
}
- }
- else if (LEXER_IS_BINARY_LVALUE_OP_TOKEN (token))
- {
- parser_stack_push_uint8 (context_p, CBC_ASSIGN);
- parser_stack_push_uint8 (context_p, LEXER_ASSIGN);
- parser_stack_push_uint8 (context_p, lexer_convert_binary_lvalue_token_to_binary (token));
- continue;
- }
- else if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND)
- {
- parser_branch_t branch;
- parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
- parser_set_branch_to_current_position (context_p, &branch);
- continue;
- }
-#if JERRY_ESNEXT
- else if (token == LEXER_NULLISH_COALESCING)
- {
- parser_branch_t branch;
- parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
- parser_set_branch_to_current_position (context_p, &branch);
- continue;
- }
- else if (token == LEXER_KEYW_IN && context_p->stack_top_uint8 == LEXER_PRIVATE_PRIMARY_EXPR)
- {
- parser_stack_pop_uint8 (context_p);
- uint16_t lit_id = parser_stack_pop_uint16 (context_p);
- parser_emit_cbc_ext_literal (context_p, CBC_EXT_PUSH_PRIVATE_PROP_LITERAL_IN, lit_id);
- continue;
- }
#endif /* JERRY_ESNEXT */
- else
- {
- opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token);
-
- if (PARSER_IS_PUSH_NUMBER (context_p->last_cbc_opcode))
+ default:
{
- lexer_convert_push_number_to_push_literal (context_p);
- }
-
- if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
- {
- JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, opcode + CBC_BINARY_WITH_LITERAL));
- context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_LITERAL);
- continue;
- }
- else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
- {
- JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_WITH_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
- context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_TWO_LITERALS);
+ parser_process_binary_token (context_p, token);
continue;
}
}
- parser_emit_cbc (context_p, opcode);
}
} /* parser_process_binary_opcodes */
diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c
index dacb35415..13cbd7ec0 100644
--- a/jerry-core/vm/vm.c
+++ b/jerry-core/vm/vm.c
@@ -3331,6 +3331,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
continue;
}
#if JERRY_ESNEXT
+ case VM_OC_POP_REFERENCE:
+ {
+ ecma_free_value (stack_top_p[-2]);
+ ecma_free_value (stack_top_p[-3]);
+ stack_top_p[-3] = stack_top_p[-1];
+ stack_top_p -= 2;
+ continue;
+ }
case VM_OC_BRANCH_IF_NULLISH:
{
left_value = stack_top_p[-1];
diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h
index c831ad1e6..7fa5a717b 100644
--- a/jerry-core/vm/vm.h
+++ b/jerry-core/vm/vm.h
@@ -166,6 +166,7 @@ typedef enum
VM_OC_JUMP, /**< jump */
#if JERRY_ESNEXT
VM_OC_BRANCH_IF_NULLISH, /** branch if undefined or null */
+ VM_OC_POP_REFERENCE, /** prop identifier or property reference from the stack */
#endif /* JERRY_ESNEXT */
VM_OC_BRANCH_IF_STRICT_EQUAL, /**< branch if strict equal */
@@ -325,6 +326,7 @@ typedef enum
#if !JERRY_ESNEXT
VM_OC_EXP = VM_OC_NONE, /**< exponentiation */
VM_OC_BRANCH_IF_NULLISH = VM_OC_NONE, /** branch if undefined or null */
+ VM_OC_POP_REFERENCE = VM_OC_NONE, /** prop identifier or property reference from the stack */
#endif /* !JERRY_ESNEXT */
#if !JERRY_DEBUGGER
VM_OC_BREAKPOINT_ENABLED = VM_OC_NONE, /**< enabled breakpoint for debugger is unused */
diff --git a/tests/jerry/es.next/logical-assignment.js b/tests/jerry/es.next/logical-assignment.js
new file mode 100644
index 000000000..7232404fa
--- /dev/null
+++ b/tests/jerry/es.next/logical-assignment.js
@@ -0,0 +1,216 @@
+// Copyright JS Foundation and other contributors, http://js.foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+function testLogicalOr(truish, falsish, value) {
+ let a = truish;
+ a ||= value;
+ expectSame(a, truish);
+
+ a = falsish;
+ a ||= value;
+ expectSame(a, value);
+
+ a = {
+ a: truish
+ };
+ a.a ||= value
+ expectSame(a.a, truish);
+
+ a = {
+ a: falsish
+ };
+ a.a ||= value;
+ expectSame(a.a, value);
+
+ let called = false;
+ let setter = function (_) {
+ called = true
+ };
+ a = Object.defineProperty({}, "a", {
+ set: setter
+ });
+ let obj = {
+ a: value
+ }
+ a ||= obj;
+ expectSame(called, false);
+ expectSame(Object.getOwnPropertyDescriptor(a, "a").set, setter);
+
+ a = falsish;
+ a ||= function () {};
+ expectSame(typeof a, 'function');
+ expectSame(a.name, 'a');
+
+ a = falsish;
+ a ||= class {};
+ expectSame(typeof a, 'function');
+ expectSame(a.name, 'a');
+
+ const b = truish;
+ b ||= value;
+ expectSame(b, truish);
+
+ expectSame(this.b, undefined);
+ this.b ||= value;
+ expectSame(this.b, value);
+}
+
+function testLogicalAnd(truish, falsish, value) {
+ let a = falsish;
+ a &&= value;
+ expectSame(a, falsish);
+
+ a = truish;
+ a &&= value;
+ expectSame(a, value);
+
+ a = {
+ a: falsish
+ };
+ a.a &&= value
+ expectSame(a.a, falsish);
+
+ a = {
+ a: truish
+ };
+ a.a &&= value;
+ expectSame(a.a, value);
+
+ let called = false;
+ let setter = function (_) {
+ called = true
+ };
+ a = Object.defineProperty({}, "a", {
+ set: setter
+ });
+ let obj = {
+ a: value
+ };
+ a &&= obj;
+ expectSame(called, false);
+ expectSame(Object.getOwnPropertyDescriptor(a, "a").value, obj.a);
+
+ a = truish;
+ a &&= function () {};
+ expectSame(typeof a, 'function');
+ expectSame(a.name, 'a');
+
+ a = truish;
+ a &&= class {};
+ expectSame(typeof a, 'function');
+ expectSame(a.name, 'a');
+
+ const b = falsish;
+ b &&= value;
+ expectSame(b, falsish);
+
+ expectSame(this.b, undefined);
+ this.b &&= value;
+ expectSame(this.b, undefined);
+}
+
+function testNullish(truish, falsish, value) {
+ let a = falsish;
+ a ??= value;
+ expectSame(a, value);
+
+ a = truish;
+ a ??= value;
+ expectSame(a, truish);
+
+ a = {
+ a: falsish
+ };
+ a.a ??= value
+ expectSame(a.a, value);
+
+ a = {
+ a: truish
+ };
+ a.a ??= value;
+ expectSame(a.a, truish);
+
+ let called = false;
+ let setter = function (_) {
+ called = true
+ };
+ a = Object.defineProperty({}, "a", {
+ set: setter
+ });
+ let obj = {
+ a: value
+ };
+ a ??= obj;
+ expectSame(called, false);
+ expectSame(Object.getOwnPropertyDescriptor(a, "a").set, setter);
+
+ a = falsish;
+ a ??= function () {};
+ expectSame(typeof a, 'function');
+ expectSame(a.name, 'a');
+
+ a = falsish;
+ a ??= class {};
+ expectSame(typeof a, 'function');
+ expectSame(a.name, 'a');
+
+ const b = truish;
+ b ??= value;
+ expectSame(b, truish);
+
+ expectSame(this.b, undefined);
+ this.b ??= value;
+ expectSame(this.b, value);
+}
+
+const nullishValues = [undefined, null];
+const logicalTrueValues = [1.1, Infinity, 1n, true, "foo", {}, Symbol()];
+const logicalFalseValues = [0, 0.0, NaN, 0n, false, "", ...nullishValues];
+const allValues = [...logicalTrueValues, ...logicalFalseValues];
+
+function testValues(cb, falseValues) {
+ for (const truish of logicalTrueValues) {
+ for (const falsish of falseValues) {
+ for (const value of allValues) {
+ cb.call({}, truish, falsish, value);
+ }
+ }
+ }
+}
+
+function expectSame(x, value) {
+ if (typeof x === 'number' && isNaN(x)) {
+ assert(typeof value === 'number');
+ assert(isNaN(value));
+ } else {
+ assert(x === value);
+ }
+}
+
+testValues(testLogicalOr, logicalFalseValues);
+testValues(testLogicalAnd, logicalFalseValues);
+testValues(testNullish, nullishValues);
+
+function expectTypeError(str) {
+ try {
+ eval(str);
+ assert(false);
+ } catch (e) {
+ assert(e instanceof TypeError);
+ }
+}
+
+expectTypeError("const c = 0; c ||= 8");
+expectTypeError("const c = 1; c &&= 8");
+expectTypeError("const c = undefined; c ??= 8");
diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml
index 492e5a302..27e7f383d 100644
--- a/tests/test262-esnext-excludelist.xml
+++ b/tests/test262-esnext-excludelist.xml
@@ -1821,57 +1821,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-