Implement logical assignment operators (#4834)

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik robert.fancsik@h-lab.eu
This commit is contained in:
Robert Fancsik
2021-11-30 10:44:27 +01:00
committed by GitHub
parent 70e275e92f
commit d69ac0e070
9 changed files with 679 additions and 245 deletions
+1 -1
View File
@@ -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
+1
View File
@@ -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) \
+63 -4
View File
@@ -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;
+4
View File
@@ -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. */
+384 -189
View File
@@ -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 */
+8
View File
@@ -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];
+2
View File
@@ -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 */
+216
View File
@@ -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");
-51
View File
@@ -1821,57 +1821,6 @@
<test id="built-ins/WeakRef/prototype/deref/this-does-not-have-internal-target-throws.js"><reason></reason></test>
<!-- END - ESNext stage 4 proposal: WeakRefs -->
<!-- ESNext stage 4 proposal: Logical Assignment Operators
features: [logical-assignment-operators]
https://github.com/tc39/proposal-logical-assignment
-->
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-bigint.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-lhs-before-rhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-arrow-function.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-class-expression.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-namedevaluation-function.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-non-extensible.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-lhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-assignment-operator.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-and-whitespace.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-bigint.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-lhs-before-rhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-arrow-function.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-class-expression.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-namedevaluation-function.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-extensible.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-lhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-assignment-operator.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-nullish-whitespace.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-bigint.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-lhs-before-rhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-arrow-function.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-class-expression.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-namedevaluation-function.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-non-extensible.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-lhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs-put.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-assignment-operator.js"><reason></reason></test>
<test id="language/expressions/logical-assignment/lgcl-or-whitespace.js"><reason></reason></test>
<!-- END - ESNext stage 4 proposal: Logical Assignment Operators -->
<!-- ES2015: Proper Tail Call (PTC) Optimization
features: [tail-call-optimization]
-->