Implement logical assignment operators (#4834)
JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik robert.fancsik@h-lab.eu
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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");
|
||||
@@ -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]
|
||||
-->
|
||||
|
||||
Reference in New Issue
Block a user