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:
kisbg 2020-07-17 15:42:51 +02:00 committed by GitHub
parent f60e16d9d5
commit dfd9d4497a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 323 additions and 72 deletions

View File

@ -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)

View File

@ -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, \

View File

@ -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:

View File

@ -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, /**< "{" */

View File

@ -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;
}

View File

@ -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);

View File

@ -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.
*/

View File

@ -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.
*/

View File

@ -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.";

View File

@ -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) */

View File

@ -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:
{

View File

@ -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 */

View 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);