mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
Implement nullish coalescing operator (#3997)
JerryScript-DCO-1.0-Signed-off-by: bence gabor kis kisbg@inf.u-szeged.hu
This commit is contained in:
parent
f60e16d9d5
commit
dfd9d4497a
@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t)
|
||||
*/
|
||||
JERRY_STATIC_ASSERT (CBC_END == 238,
|
||||
number_of_cbc_opcodes_changed);
|
||||
JERRY_STATIC_ASSERT (CBC_EXT_END == 132,
|
||||
JERRY_STATIC_ASSERT (CBC_EXT_END == 135,
|
||||
number_of_cbc_ext_opcodes_changed);
|
||||
|
||||
#if ENABLED (JERRY_PARSER)
|
||||
|
||||
@ -576,6 +576,10 @@
|
||||
VM_OC_INITIALIZER_PUSH_PROP | VM_OC_GET_STACK) \
|
||||
CBC_FORWARD_BRANCH (CBC_EXT_DEFAULT_INITIALIZER, -1, \
|
||||
VM_OC_DEFAULT_INITIALIZER) \
|
||||
CBC_OPCODE (CBC_EXT_ERROR, CBC_NO_FLAG, 0, \
|
||||
VM_OC_ERROR) \
|
||||
CBC_FORWARD_BRANCH (CBC_EXT_BRANCH_IF_NULLISH, -1, \
|
||||
VM_OC_BRANCH_IF_NULLISH) \
|
||||
\
|
||||
/* Basic opcodes. */ \
|
||||
CBC_OPCODE (CBC_EXT_PUSH_LITERAL_PUSH_NUMBER_0, CBC_HAS_LITERAL_ARG, 2, \
|
||||
@ -600,8 +604,6 @@
|
||||
VM_OC_GET_TEMPLATE_OBJECT | VM_OC_PUT_STACK) \
|
||||
CBC_OPCODE (CBC_EXT_LINE, CBC_NO_FLAG, 0, \
|
||||
VM_OC_LINE) \
|
||||
CBC_OPCODE (CBC_EXT_ERROR, CBC_NO_FLAG, 0, \
|
||||
VM_OC_ERROR) \
|
||||
CBC_OPCODE (CBC_EXT_THROW_REFERENCE_ERROR, CBC_NO_FLAG, 1, \
|
||||
VM_OC_THROW_REFERENCE_ERROR) \
|
||||
CBC_OPCODE (CBC_EXT_THROW_ASSIGN_CONST_ERROR, CBC_NO_FLAG, 0, \
|
||||
|
||||
@ -1753,7 +1753,24 @@ lexer_next_token (parser_context_t *context_p) /**< context */
|
||||
LEXER_ASSIGN_BIT_XOR)
|
||||
|
||||
LEXER_TYPE_A_TOKEN (LIT_CHAR_TILDE, LEXER_BIT_NOT);
|
||||
LEXER_TYPE_A_TOKEN (LIT_CHAR_QUESTION, LEXER_QUESTION_MARK);
|
||||
case (uint8_t) (LIT_CHAR_QUESTION):
|
||||
{
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
if (length >= 2)
|
||||
{
|
||||
if (context_p->source_p[1] == (uint8_t) LIT_CHAR_QUESTION)
|
||||
{
|
||||
context_p->token.type = LEXER_NULLISH_COALESCING;
|
||||
length = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
context_p->token.type = LEXER_QUESTION_MARK;
|
||||
length = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
LEXER_TYPE_A_TOKEN (LIT_CHAR_COLON, LEXER_COLON);
|
||||
|
||||
case LIT_CHAR_SINGLE_QUOTE:
|
||||
|
||||
@ -98,31 +98,34 @@ typedef enum
|
||||
LEXER_ASSIGN_BIT_OR, /**< "|=" (prec: 3) */
|
||||
LEXER_ASSIGN_BIT_XOR, /**< "^=" (prec: 3) */
|
||||
LEXER_QUESTION_MARK, /**< "?" (prec: 4) */
|
||||
LEXER_LOGICAL_OR, /**< "||" (prec: 5) */
|
||||
LEXER_LOGICAL_AND, /**< "&&" (prec: 6) */
|
||||
LEXER_BIT_OR, /**< "|" (prec: 7) */
|
||||
LEXER_BIT_XOR, /**< "^" (prec: 8) */
|
||||
LEXER_BIT_AND, /**< "&" (prec: 9) */
|
||||
LEXER_EQUAL, /**< "==" (prec: 10) */
|
||||
LEXER_NOT_EQUAL, /**< "!=" (prec: 10) */
|
||||
LEXER_STRICT_EQUAL, /**< "===" (prec: 10) */
|
||||
LEXER_STRICT_NOT_EQUAL, /**< "!==" (prec: 10) */
|
||||
LEXER_LESS, /**< "<" (prec: 11) */
|
||||
LEXER_GREATER, /**< ">" (prec: 11) */
|
||||
LEXER_LESS_EQUAL, /**< "<=" (prec: 11) */
|
||||
LEXER_GREATER_EQUAL, /**< ">=" (prec: 11) */
|
||||
LEXER_KEYW_IN, /**< in (prec: 11) */
|
||||
LEXER_KEYW_INSTANCEOF, /**< instanceof (prec: 11) */
|
||||
LEXER_LEFT_SHIFT, /**< "<<" (prec: 12) */
|
||||
LEXER_RIGHT_SHIFT, /**< ">>" (prec: 12) */
|
||||
LEXER_UNS_RIGHT_SHIFT, /**< ">>>" (prec: 12) */
|
||||
LEXER_ADD, /**< "+" (prec: 13) */
|
||||
LEXER_SUBTRACT, /**< "-" (prec: 13) */
|
||||
LEXER_MULTIPLY, /**< "*" (prec: 14) */
|
||||
LEXER_DIVIDE, /**< "/" (prec: 14) */
|
||||
LEXER_MODULO, /**< "%" (prec: 14) */
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
LEXER_EXPONENTIATION, /**< "**" (prec: 15) */
|
||||
LEXER_NULLISH_COALESCING, /**< "??" (prec: 5) */
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
LEXER_LOGICAL_OR, /**< "||" (prec: 6) */
|
||||
LEXER_LOGICAL_AND, /**< "&&" (prec: 7) */
|
||||
LEXER_BIT_OR, /**< "|" (prec: 8) */
|
||||
LEXER_BIT_XOR, /**< "^" (prec: 9) */
|
||||
LEXER_BIT_AND, /**< "&" (prec: 10) */
|
||||
LEXER_EQUAL, /**< "==" (prec: 11) */
|
||||
LEXER_NOT_EQUAL, /**< "!=" (prec: 11) */
|
||||
LEXER_STRICT_EQUAL, /**< "===" (prec: 11) */
|
||||
LEXER_STRICT_NOT_EQUAL, /**< "!==" (prec: 11) */
|
||||
LEXER_LESS, /**< "<" (prec: 12) */
|
||||
LEXER_GREATER, /**< ">" (prec: 12) */
|
||||
LEXER_LESS_EQUAL, /**< "<=" (prec: 12) */
|
||||
LEXER_GREATER_EQUAL, /**< ">=" (prec: 12) */
|
||||
LEXER_KEYW_IN, /**< in (prec: 12) */
|
||||
LEXER_KEYW_INSTANCEOF, /**< instanceof (prec: 12) */
|
||||
LEXER_LEFT_SHIFT, /**< "<<" (prec: 13) */
|
||||
LEXER_RIGHT_SHIFT, /**< ">>" (prec: 13) */
|
||||
LEXER_UNS_RIGHT_SHIFT, /**< ">>>" (prec: 13) */
|
||||
LEXER_ADD, /**< "+" (prec: 14) */
|
||||
LEXER_SUBTRACT, /**< "-" (prec: 14) */
|
||||
LEXER_MULTIPLY, /**< "*" (prec: 15) */
|
||||
LEXER_DIVIDE, /**< "/" (prec: 15) */
|
||||
LEXER_MODULO, /**< "%" (prec: 15) */
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
LEXER_EXPONENTIATION, /**< "**" (prec: 16) */
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
LEXER_LEFT_BRACE, /**< "{" */
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
/**
|
||||
* Maximum precedence for right-to-left binary operation evaluation.
|
||||
*/
|
||||
#define PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE 6
|
||||
#define PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE 7
|
||||
|
||||
/**
|
||||
* Precedence for ternary operation.
|
||||
@ -45,12 +45,17 @@
|
||||
/**
|
||||
* Precedence for exponentiation operation.
|
||||
*/
|
||||
#define PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION 15
|
||||
#define PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION 16
|
||||
|
||||
/**
|
||||
* Value of grouping level increase and decrease.
|
||||
*/
|
||||
#define PARSER_GROUPING_LEVEL_INCREASE 2
|
||||
#define PARSER_GROUPING_LEVEL_INCREASE 4
|
||||
|
||||
/**
|
||||
* Represents whether logical expression was emitted in the current group expression.
|
||||
*/
|
||||
#define PARSER_GROUPING_LOGICAL_FOUND (1 << 1)
|
||||
|
||||
/**
|
||||
* Precedence of the binary tokens.
|
||||
@ -60,21 +65,56 @@
|
||||
*/
|
||||
static const uint8_t parser_binary_precedence_table[] =
|
||||
{
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, /**< "=" */
|
||||
3, /**< "+=" */
|
||||
3, /**< "-=" */
|
||||
3, /**< "*=" */
|
||||
3, /**< "/=" */
|
||||
3, /**< "=" */
|
||||
3, /**< "<<=" */
|
||||
3, /**< ">>=" */
|
||||
3, /**< ">>>=" */
|
||||
3, /**< "&=" */
|
||||
3, /**< "|=" */
|
||||
3, /**< "^=" */
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
3,
|
||||
3, /**< "**=" */
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
4, 5, 6, 7, 8, 9, 10, 10, 10, 10,
|
||||
11, 11, 11, 11, 11, 11, 12, 12, 12,
|
||||
13, 13, 14, 14, 14,
|
||||
4, /**< "?"*/
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
15,
|
||||
5, /**< "??" */
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
6, /**< "||" */
|
||||
7, /**< "&&" */
|
||||
8, /**< "|" */
|
||||
9, /**< "^" */
|
||||
10, /**< "&" */
|
||||
11, /**< "==" */
|
||||
11, /**< "!=" */
|
||||
11, /**< "===" */
|
||||
11, /**< "!==" */
|
||||
12, /**< "<" */
|
||||
12, /**< ">" */
|
||||
12, /**< "<=" */
|
||||
12, /**< ">=" */
|
||||
12, /**< in */
|
||||
12, /**< instanceof */
|
||||
13, /**< "<<" */
|
||||
13, /**< ">>" */
|
||||
13, /**< ">>>" */
|
||||
14, /**< "+" */
|
||||
14, /**< "-" */
|
||||
15, /**< "*" */
|
||||
15, /**< "/" */
|
||||
15, /**< "%" */
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
16, /**< "**" */
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
};
|
||||
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 38,
|
||||
parser_binary_precedence_table_should_have_38_values_in_es2015);
|
||||
JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 39,
|
||||
parser_binary_precedence_table_should_have_39_values_in_es2015);
|
||||
#else /* !ENABLED (JERRY_ESNEXT) */
|
||||
JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 36,
|
||||
parser_binary_precedence_table_should_have_36_values_in_es51);
|
||||
@ -2653,14 +2693,87 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**<
|
||||
return assign_opcode;
|
||||
} /* parser_append_binary_single_assignment_token */
|
||||
|
||||
/**
|
||||
* Check for invalid logical operator and nullish chaining
|
||||
*/
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
static void
|
||||
parser_check_invalid_logical_op (parser_context_t *context_p, /**< context */
|
||||
size_t grouping_level) /**< grouping_level */
|
||||
{
|
||||
parser_stack_iterator_t iterator;
|
||||
parser_stack_iterator_init (context_p, &iterator);
|
||||
bool logical_found = (grouping_level & PARSER_GROUPING_LOGICAL_FOUND) != 0;
|
||||
bool nullish_found = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
uint8_t token = parser_stack_iterator_read_uint8 (&iterator);
|
||||
|
||||
if (token == LEXER_EXPRESSION_START
|
||||
|| token == LEXER_LEFT_PAREN
|
||||
|| !LEXER_IS_BINARY_OP_TOKEN (token))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
parser_stack_iterator_skip (&iterator, sizeof (uint8_t));
|
||||
|
||||
if (token == LEXER_ASSIGN)
|
||||
{
|
||||
cbc_opcode_t opcode = (cbc_opcode_t) parser_stack_iterator_read_uint8 (&iterator);
|
||||
|
||||
if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG)
|
||||
{
|
||||
parser_stack_iterator_skip (&iterator, sizeof (uint16_t));
|
||||
}
|
||||
token = parser_stack_iterator_read_uint8 (&iterator);
|
||||
|
||||
if (token == LEXER_ASSIGN_GROUP_EXPR)
|
||||
{
|
||||
parser_stack_iterator_skip (&iterator, sizeof (uint8_t));
|
||||
}
|
||||
if (token == LEXER_ASSIGN_CONST)
|
||||
{
|
||||
parser_stack_iterator_skip (&iterator, sizeof (uint8_t));
|
||||
}
|
||||
}
|
||||
else if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND)
|
||||
{
|
||||
if (nullish_found)
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_INVALID_NULLISH_COALESCING);
|
||||
}
|
||||
|
||||
parser_stack_iterator_skip (&iterator, sizeof (parser_branch_t));
|
||||
logical_found = true;
|
||||
}
|
||||
else if (token == LEXER_NULLISH_COALESCING)
|
||||
{
|
||||
if (logical_found)
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_INVALID_NULLISH_COALESCING);
|
||||
}
|
||||
|
||||
parser_stack_iterator_skip (&iterator, sizeof (parser_branch_t));
|
||||
nullish_found = true;
|
||||
}
|
||||
}
|
||||
} /* parser_check_invalid_logical_op */
|
||||
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
/**
|
||||
* Append a binary token.
|
||||
*/
|
||||
static void
|
||||
parser_append_binary_token (parser_context_t *context_p) /**< context */
|
||||
parser_append_binary_token (parser_context_t *context_p, /**< context */
|
||||
size_t grouping_level) /**< grouping_level */
|
||||
{
|
||||
JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type));
|
||||
|
||||
#if !ENABLED (JERRY_ESNEXT)
|
||||
JERRY_UNUSED (grouping_level);
|
||||
#endif /* !ENABLED (JERRY_ESNEXT) */
|
||||
parser_push_result (context_p);
|
||||
|
||||
if (context_p->token.type == LEXER_ASSIGN)
|
||||
@ -2715,7 +2828,25 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */
|
||||
|
||||
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);
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
parser_check_invalid_logical_op (context_p, grouping_level);
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
return;
|
||||
}
|
||||
#if ENABLED (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);
|
||||
parser_check_invalid_logical_op (context_p, grouping_level);
|
||||
return;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
parser_stack_push_uint8 (context_p, context_p->token.type);
|
||||
} /* parser_append_binary_token */
|
||||
@ -2725,8 +2856,12 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */
|
||||
*/
|
||||
static void
|
||||
parser_process_binary_opcodes (parser_context_t *context_p, /**< context */
|
||||
uint8_t min_prec_treshold) /**< minimal precedence of tokens */
|
||||
uint8_t min_prec_treshold, /**< minimal precedence of tokens */
|
||||
size_t *grouping_level_p) /**< grouping level */
|
||||
{
|
||||
#if !ENABLED (JERRY_ESNEXT)
|
||||
JERRY_UNUSED (grouping_level_p);
|
||||
#endif /* !ENABLED (JERRY_ESNEXT) */
|
||||
while (true)
|
||||
{
|
||||
uint8_t token = context_p->stack_top_uint8;
|
||||
@ -2843,8 +2978,22 @@ parser_process_binary_opcodes (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);
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
JERRY_ASSERT (grouping_level_p != NULL);
|
||||
*grouping_level_p |= PARSER_GROUPING_LOGICAL_FOUND;
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
continue;
|
||||
}
|
||||
#if ENABLED (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;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
else
|
||||
{
|
||||
opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token);
|
||||
@ -3054,7 +3203,7 @@ parser_pattern_form_assignment (parser_context_t *context_p, /**< context */
|
||||
parser_set_branch_to_current_position (context_p, &skip_init);
|
||||
}
|
||||
|
||||
parser_process_binary_opcodes (context_p, 0);
|
||||
parser_process_binary_opcodes (context_p, 0, NULL);
|
||||
|
||||
JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START);
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
@ -3433,7 +3582,7 @@ parser_process_ternary_expression (parser_context_t *context_p) /**< context */
|
||||
* the result may come from the first branch. */
|
||||
parser_flush_cbc (context_p);
|
||||
|
||||
parser_process_binary_opcodes (context_p, 0);
|
||||
parser_process_binary_opcodes (context_p, 0, NULL);
|
||||
} /* parser_process_ternary_expression */
|
||||
|
||||
/**
|
||||
@ -3477,6 +3626,12 @@ parser_process_group_expression (parser_context_t *context_p, /**< context */
|
||||
parser_push_result (context_p);
|
||||
parser_flush_cbc (context_p);
|
||||
}
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
else
|
||||
{
|
||||
*grouping_level_p &= (size_t) ~(PARSER_GROUPING_LOGICAL_FOUND);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
parser_stack_pop_uint8 (context_p);
|
||||
lexer_next_token (context_p);
|
||||
@ -3555,7 +3710,7 @@ parser_parse_expression (parser_context_t *context_p, /**< context */
|
||||
{
|
||||
if (parser_parse_unary_expression (context_p, &grouping_level))
|
||||
{
|
||||
parser_process_binary_opcodes (context_p, 0);
|
||||
parser_process_binary_opcodes (context_p, 0, &grouping_level);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3592,9 +3747,8 @@ process_unary_expression:
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
}
|
||||
|
||||
parser_process_binary_opcodes (context_p, min_prec_treshold);
|
||||
parser_process_binary_opcodes (context_p, min_prec_treshold, &grouping_level);
|
||||
}
|
||||
|
||||
if (context_p->token.type == LEXER_RIGHT_PAREN
|
||||
&& (context_p->stack_top_uint8 == LEXER_LEFT_PAREN
|
||||
|| context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST))
|
||||
@ -3622,7 +3776,7 @@ process_unary_expression:
|
||||
}
|
||||
else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type))
|
||||
{
|
||||
parser_append_binary_token (context_p);
|
||||
parser_append_binary_token (context_p, grouping_level);
|
||||
lexer_next_token (context_p);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -646,6 +646,8 @@ void parser_stack_push_uint16 (parser_context_t *context_p, uint16_t uint16_valu
|
||||
uint16_t parser_stack_pop_uint16 (parser_context_t *context_p);
|
||||
void parser_stack_push (parser_context_t *context_p, const void *data_p, uint32_t length);
|
||||
void parser_stack_pop (parser_context_t *context_p, void *data_p, uint32_t length);
|
||||
void parser_stack_iterator_init (parser_context_t *context_p, parser_stack_iterator_t *iterator);
|
||||
uint8_t parser_stack_iterator_read_uint8 (parser_stack_iterator_t *iterator);
|
||||
void parser_stack_iterator_skip (parser_stack_iterator_t *iterator, size_t length);
|
||||
void parser_stack_iterator_read (parser_stack_iterator_t *iterator, void *data_p, size_t length);
|
||||
void parser_stack_iterator_write (parser_stack_iterator_t *iterator, const void *data_p, size_t length);
|
||||
|
||||
@ -621,6 +621,29 @@ parser_stack_pop (parser_context_t *context_p, /**< context */
|
||||
}
|
||||
} /* parser_stack_pop */
|
||||
|
||||
/**
|
||||
* Initialize stack iterator.
|
||||
*/
|
||||
inline void
|
||||
parser_stack_iterator_init (parser_context_t *context_p, /**< context */
|
||||
parser_stack_iterator_t *iterator) /**< iterator */
|
||||
{
|
||||
iterator->current_p = context_p->stack.first_p;
|
||||
iterator->current_position = context_p->stack.last_position;
|
||||
} /* parser_stack_iterator_init */
|
||||
|
||||
/**
|
||||
* Read the next byte from the stack.
|
||||
*
|
||||
* @return byte
|
||||
*/
|
||||
inline uint8_t
|
||||
parser_stack_iterator_read_uint8 (parser_stack_iterator_t *iterator) /**< iterator */
|
||||
{
|
||||
JERRY_ASSERT (iterator->current_position > 0 && iterator->current_position <= PARSER_STACK_PAGE_SIZE);
|
||||
return iterator->current_p->bytes[iterator->current_position - 1];
|
||||
} /* parser_stack_iterator_read_uint8 */
|
||||
|
||||
/**
|
||||
* Skip the next n bytes of the stack.
|
||||
*/
|
||||
|
||||
@ -307,30 +307,6 @@ parser_statement_length (uint8_t type) /**< type of statement */
|
||||
|
||||
return statement_lengths[type - PARSER_STATEMENT_BLOCK];
|
||||
} /* parser_statement_length */
|
||||
|
||||
/**
|
||||
* Initialize stack iterator.
|
||||
*/
|
||||
static inline void
|
||||
parser_stack_iterator_init (parser_context_t *context_p, /**< context */
|
||||
parser_stack_iterator_t *iterator) /**< iterator */
|
||||
{
|
||||
iterator->current_p = context_p->stack.first_p;
|
||||
iterator->current_position = context_p->stack.last_position;
|
||||
} /* parser_stack_iterator_init */
|
||||
|
||||
/**
|
||||
* Read the next byte from the stack.
|
||||
*
|
||||
* @return byte
|
||||
*/
|
||||
static inline uint8_t
|
||||
parser_stack_iterator_read_uint8 (parser_stack_iterator_t *iterator) /**< iterator */
|
||||
{
|
||||
JERRY_ASSERT (iterator->current_position > 0 && iterator->current_position <= PARSER_STACK_PAGE_SIZE);
|
||||
return iterator->current_p->bytes[iterator->current_position - 1];
|
||||
} /* parser_stack_iterator_read_uint8 */
|
||||
|
||||
/**
|
||||
* Parse expression enclosed in parens.
|
||||
*/
|
||||
|
||||
@ -1206,6 +1206,10 @@ parser_error_to_string (parser_error_t error) /**< error code */
|
||||
{
|
||||
return "Left operand of ** operator cannot be unary expression.";
|
||||
}
|
||||
case PARSER_ERR_INVALID_NULLISH_COALESCING:
|
||||
{
|
||||
return "Cannot chain nullish with logical AND or OR.";
|
||||
}
|
||||
case PARSER_ERR_FORMAL_PARAM_AFTER_REST_PARAMETER:
|
||||
{
|
||||
return "Rest parameter must be the last formal parameter.";
|
||||
|
||||
@ -154,6 +154,7 @@ typedef enum
|
||||
PARSER_ERR_INVALID_DESTRUCTURING_PATTERN, /**< invalid destructuring pattern */
|
||||
PARSER_ERR_ILLEGAL_PROPERTY_IN_DECLARATION, /**< illegal property in declaration context */
|
||||
PARSER_ERR_INVALID_EXPONENTIATION, /**< left operand of ** operator cannot be unary expression */
|
||||
PARSER_ERR_INVALID_NULLISH_COALESCING, /**< Cannot chain nullish with logical AND or OR. */
|
||||
PARSER_ERR_NEW_TARGET_EXPECTED, /**< expected new.target expression */
|
||||
PARSER_ERR_NEW_TARGET_NOT_ALLOWED, /**< new.target is not allowed in the given context */
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
@ -2922,6 +2922,20 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
ecma_fast_free_value (value);
|
||||
continue;
|
||||
}
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
case VM_OC_BRANCH_IF_NULLISH:
|
||||
{
|
||||
left_value = stack_top_p[-1];
|
||||
|
||||
if (!ecma_is_value_null (left_value) && !ecma_is_value_undefined (left_value))
|
||||
{
|
||||
byte_code_p = byte_code_start_p + branch_offset;
|
||||
continue;
|
||||
}
|
||||
--stack_top_p;
|
||||
continue;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
case VM_OC_PLUS:
|
||||
case VM_OC_MINUS:
|
||||
{
|
||||
|
||||
@ -161,6 +161,9 @@ typedef enum
|
||||
VM_OC_ERROR, /**< error while the vm_loop is suspended */
|
||||
|
||||
VM_OC_JUMP, /**< jump */
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
VM_OC_BRANCH_IF_NULLISH, /** branch if undefined or null */
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
VM_OC_BRANCH_IF_STRICT_EQUAL, /**< branch if strict equal */
|
||||
|
||||
/* These four opcodes must be in this order. */
|
||||
@ -296,6 +299,7 @@ typedef enum
|
||||
{
|
||||
#if !ENABLED (JERRY_ESNEXT)
|
||||
VM_OC_EXP = VM_OC_NONE, /**< exponentiation */
|
||||
VM_OC_BRANCH_IF_NULLISH = VM_OC_NONE, /** branch if undefined or null */
|
||||
#endif /* !ENABLED (JERRY_ESNEXT) */
|
||||
#if !ENABLED (JERRY_DEBUGGER)
|
||||
VM_OC_BREAKPOINT_ENABLED = VM_OC_NONE, /**< enabled breakpoint for debugger is unused */
|
||||
|
||||
51
tests/jerry/es.next/nullish-coalescing.js
Normal file
51
tests/jerry/es.next/nullish-coalescing.js
Normal file
@ -0,0 +1,51 @@
|
||||
// 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.
|
||||
|
||||
//basic valid cases with logical operator
|
||||
assert((1 ?? 2) == 1)
|
||||
assert((0 ?? 2) == 0)
|
||||
assert(null ?? 2 == 2)
|
||||
assert(null ?? undefined == undefined)
|
||||
assert(null ?? undefined ?? 2 == 2)
|
||||
assert(null ?? undefined ?? null ?? 10 == 10)
|
||||
assert(null ?? (undefined || null) ?? 10 == 10)
|
||||
assert(null ?? (undefined && null) ?? 10 == 10)
|
||||
assert((null ?? true) && (true ?? null) == true)
|
||||
|
||||
//cannot evulate the right expression if left is not null or undefined
|
||||
function poison () {
|
||||
throw 23;
|
||||
}
|
||||
assert(true ?? poison ())
|
||||
assert(null ?? null ?? true ?? poison ())
|
||||
|
||||
function checkSyntax (str) {
|
||||
try {
|
||||
eval (str);
|
||||
assert (false);
|
||||
} catch (e) {
|
||||
assert (e instanceof SyntaxError);
|
||||
}
|
||||
}
|
||||
|
||||
// invalid use cases
|
||||
var headNullish1 = "(null ?? null || null )";
|
||||
var headNullish2 = "(null ?? null && null )";
|
||||
var tailNullish1 = "(null || null ?? null )";
|
||||
var tailNullish2 = "(null || null ?? null )";
|
||||
|
||||
checkSyntax (headNullish1);
|
||||
checkSyntax (headNullish2);
|
||||
checkSyntax (tailNullish1);
|
||||
checkSyntax (tailNullish2);
|
||||
Loading…
x
Reference in New Issue
Block a user