Implement logical assignment
https://tc39.es/proposal-logical-assignment/ Bug: v8:10372 Change-Id: I538d54af6b4b24d450d1398c74f76dd57fdb0147 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2158119 Reviewed-by: Shu-yu Guo <syg@chromium.org> Reviewed-by: Marja Hölttä <marja@chromium.org> Reviewed-by: Mythri Alle <mythria@chromium.org> Commit-Queue: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/master@{#67330}
This commit is contained in:
parent
6458a5296b
commit
b151d8db22
@ -220,7 +220,8 @@ DEFINE_IMPLICATION(harmony_weak_refs_with_cleanup_some, harmony_weak_refs)
|
||||
V(harmony_weak_refs_with_cleanup_some, \
|
||||
"harmony weak references with FinalizationRegistry.prototype.cleanupSome") \
|
||||
V(harmony_regexp_match_indices, "harmony regexp match indices") \
|
||||
V(harmony_top_level_await, "harmony top level await")
|
||||
V(harmony_top_level_await, "harmony top level await") \
|
||||
V(harmony_logical_assignment, "harmony logical assignment")
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
|
@ -4246,6 +4246,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_meta)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_sequence)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_top_level_await)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_logical_assignment)
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_add_calendar_numbering_system)
|
||||
|
@ -4142,9 +4142,23 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
|
||||
}
|
||||
}
|
||||
|
||||
BinaryOperation* binop = expr->AsCompoundAssignment()->binary_operation();
|
||||
BinaryOperation* binop = expr->binary_operation();
|
||||
FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
|
||||
if (expr->value()->IsSmiLiteral()) {
|
||||
BytecodeLabel short_circuit;
|
||||
if (binop->op() == Token::NULLISH) {
|
||||
BytecodeLabel nullish;
|
||||
builder()
|
||||
->JumpIfUndefinedOrNull(&nullish)
|
||||
.Jump(&short_circuit)
|
||||
.Bind(&nullish);
|
||||
VisitForAccumulatorValue(expr->value());
|
||||
} else if (binop->op() == Token::OR) {
|
||||
builder()->JumpIfTrue(ToBooleanMode::kConvertToBoolean, &short_circuit);
|
||||
VisitForAccumulatorValue(expr->value());
|
||||
} else if (binop->op() == Token::AND) {
|
||||
builder()->JumpIfFalse(ToBooleanMode::kConvertToBoolean, &short_circuit);
|
||||
VisitForAccumulatorValue(expr->value());
|
||||
} else if (expr->value()->IsSmiLiteral()) {
|
||||
builder()->BinaryOperationSmiLiteral(
|
||||
binop->op(), expr->value()->AsLiteral()->AsSmiLiteral(),
|
||||
feedback_index(slot));
|
||||
@ -4154,9 +4168,9 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
|
||||
VisitForAccumulatorValue(expr->value());
|
||||
builder()->BinaryOperation(binop->op(), old_value, feedback_index(slot));
|
||||
}
|
||||
|
||||
builder()->SetExpressionPosition(expr);
|
||||
BuildAssignment(lhs_data, expr->op(), expr->lookup_hoisting_mode());
|
||||
builder()->Bind(&short_circuit);
|
||||
}
|
||||
|
||||
// Suspends the generator to resume at the next suspend_id, with output stored
|
||||
|
@ -38,6 +38,7 @@ UnoptimizedCompileFlags::UnoptimizedCompileFlags(Isolate* isolate,
|
||||
set_collect_source_positions(!FLAG_enable_lazy_source_positions ||
|
||||
isolate->NeedsDetailedOptimizedCodeLineInfo());
|
||||
set_allow_harmony_top_level_await(FLAG_harmony_top_level_await);
|
||||
set_allow_harmony_logical_assignment(FLAG_harmony_logical_assignment);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -66,7 +66,8 @@ class Zone;
|
||||
V(is_oneshot_iife, bool, 1, _) \
|
||||
V(collect_source_positions, bool, 1, _) \
|
||||
V(allow_harmony_top_level_await, bool, 1, _) \
|
||||
V(is_repl_mode, bool, 1, _)
|
||||
V(is_repl_mode, bool, 1, _) \
|
||||
V(allow_harmony_logical_assignment, bool, 1, _)
|
||||
|
||||
class V8_EXPORT_PRIVATE UnoptimizedCompileFlags {
|
||||
public:
|
||||
|
@ -2750,6 +2750,11 @@ ParserBase<Impl>::ParseAssignmentExpressionCoverGrammar() {
|
||||
Token::Value op = peek();
|
||||
|
||||
if (!Token::IsArrowOrAssignmentOp(op)) return expression;
|
||||
if ((op == Token::ASSIGN_NULLISH || op == Token::ASSIGN_OR ||
|
||||
op == Token::ASSIGN_AND) &&
|
||||
!flags().allow_harmony_logical_assignment()) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
// Arrow functions.
|
||||
if (V8_UNLIKELY(op == Token::ARROW)) {
|
||||
|
@ -364,14 +364,14 @@ V8_INLINE Token::Value Scanner::ScanSingleToken() {
|
||||
return Select(token);
|
||||
|
||||
case Token::CONDITIONAL:
|
||||
// ? ?. ??
|
||||
// ? ?. ?? ??=
|
||||
Advance();
|
||||
if (c0_ == '.') {
|
||||
Advance();
|
||||
if (!IsDecimalDigit(c0_)) return Token::QUESTION_PERIOD;
|
||||
PushBack('.');
|
||||
} else if (c0_ == '?') {
|
||||
return Select(Token::NULLISH);
|
||||
return Select('=', Token::ASSIGN_NULLISH, Token::NULLISH);
|
||||
}
|
||||
return Token::CONDITIONAL;
|
||||
|
||||
@ -471,16 +471,16 @@ V8_INLINE Token::Value Scanner::ScanSingleToken() {
|
||||
return Token::DIV;
|
||||
|
||||
case Token::BIT_AND:
|
||||
// & && &=
|
||||
// & && &= &&=
|
||||
Advance();
|
||||
if (c0_ == '&') return Select(Token::AND);
|
||||
if (c0_ == '&') return Select('=', Token::ASSIGN_AND, Token::AND);
|
||||
if (c0_ == '=') return Select(Token::ASSIGN_BIT_AND);
|
||||
return Token::BIT_AND;
|
||||
|
||||
case Token::BIT_OR:
|
||||
// | || |=
|
||||
// | || |= ||=
|
||||
Advance();
|
||||
if (c0_ == '|') return Select(Token::OR);
|
||||
if (c0_ == '|') return Select('=', Token::ASSIGN_OR, Token::OR);
|
||||
if (c0_ == '=') return Select(Token::ASSIGN_BIT_OR);
|
||||
return Token::BIT_OR;
|
||||
|
||||
|
@ -31,6 +31,9 @@ namespace internal {
|
||||
/* Binary operators */
|
||||
/* ADD and SUB are at the end since they are UnaryOp */
|
||||
#define BINARY_OP_TOKEN_LIST(T, E) \
|
||||
E(T, NULLISH, "??", 3) \
|
||||
E(T, OR, "||", 4) \
|
||||
E(T, AND, "&&", 5) \
|
||||
E(T, BIT_OR, "|", 6) \
|
||||
E(T, BIT_XOR, "^", 7) \
|
||||
E(T, BIT_AND, "&", 8) \
|
||||
@ -97,9 +100,6 @@ namespace internal {
|
||||
/* IsBinaryOp() relies on this block of enum values */ \
|
||||
/* being contiguous and sorted in the same order! */ \
|
||||
T(COMMA, ",", 1) \
|
||||
T(NULLISH, "??", 3) \
|
||||
T(OR, "||", 4) \
|
||||
T(AND, "&&", 5) \
|
||||
\
|
||||
/* Unary operators, starting at ADD in BINARY_OP_TOKEN_LIST */ \
|
||||
/* IsUnaryOp() relies on this block of enum values */ \
|
||||
@ -297,8 +297,8 @@ class V8_EXPORT_PRIVATE Token {
|
||||
}
|
||||
|
||||
static Value BinaryOpForAssignment(Value op) {
|
||||
DCHECK(base::IsInRange(op, ASSIGN_BIT_OR, ASSIGN_SUB));
|
||||
Value result = static_cast<Value>(op - ASSIGN_BIT_OR + BIT_OR);
|
||||
DCHECK(base::IsInRange(op, ASSIGN_NULLISH, ASSIGN_SUB));
|
||||
Value result = static_cast<Value>(op - ASSIGN_NULLISH + NULLISH);
|
||||
DCHECK(IsBinaryOp(result));
|
||||
return result;
|
||||
}
|
||||
|
@ -261,9 +261,6 @@ TEST(ArrowOrAssignmentOp) {
|
||||
bool TokenIsBinaryOp(Token::Value token) {
|
||||
switch (token) {
|
||||
case Token::COMMA:
|
||||
case Token::NULLISH:
|
||||
case Token::OR:
|
||||
case Token::AND:
|
||||
#define T(name, string, precedence) case Token::name:
|
||||
BINARY_OP_TOKEN_LIST(T, EXPAND_BINOP_TOKEN)
|
||||
#undef T
|
||||
|
42
test/mjsunit/harmony/logical-assignment.js
Normal file
42
test/mjsunit/harmony/logical-assignment.js
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-logical-assignment
|
||||
|
||||
{
|
||||
let x = null;
|
||||
let y = 0;
|
||||
x ??= y ||= 5;
|
||||
|
||||
assertEquals(x, 5);
|
||||
assertEquals(y, 5);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
let x = null;
|
||||
let y = 4;
|
||||
x ??= y ||= 5;
|
||||
|
||||
assertEquals(x, 4);
|
||||
assertEquals(y, 4);
|
||||
}
|
||||
|
||||
{
|
||||
let x = 1;
|
||||
let y = 0;
|
||||
x &&= y ||= 5;
|
||||
|
||||
assertEquals(x, 5);
|
||||
assertEquals(y, 5);
|
||||
}
|
||||
|
||||
{
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
x &&= y ||= 5;
|
||||
|
||||
assertEquals(x, 0);
|
||||
assertEquals(y, 0);
|
||||
}
|
@ -659,44 +659,6 @@
|
||||
'built-ins/AsyncFromSyncIteratorPrototype/return/absent-value-not-passed': [FAIL],
|
||||
'built-ins/AsyncFromSyncIteratorPrototype/throw/absent-value-not-passed': [FAIL],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=10394
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-bigint': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-lhs-before-rhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-no-set-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-non-extensible': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-non-writeable-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-lhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-unresolved-rhs-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-whitespace': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-bigint': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-lhs-before-rhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-no-set-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-extensible': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-writeable-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-lhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-unresolved-rhs-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-whitespace': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-bigint': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-lhs-before-rhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-no-set-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-non-extensible': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-non-writeable-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-lhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-unresolved-rhs-put': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-or-whitespace': [FAIL],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=10397
|
||||
'language/statements/for-await-of/iterator-close-throw-get-method-abrupt': [FAIL],
|
||||
'language/statements/for-of/iterator-close-throw-get-method-abrupt': [FAIL],
|
||||
@ -716,7 +678,10 @@
|
||||
# https://github.com/tc39/ecma262/pull/889
|
||||
'annexB/language/function-code/block-decl-func-skip-arguments': [FAIL],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=6538
|
||||
# Non-simple assignment targets are runtime errors instead of syntax errors for web compat.
|
||||
'language/expressions/logical-assignment/lgcl-or-assignment-operator-non-simple-lhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-and-assignment-operator-non-simple-lhs': [FAIL],
|
||||
'language/expressions/logical-assignment/lgcl-nullish-assignment-operator-non-simple-lhs': [FAIL],
|
||||
|
||||
############################ INVALID TESTS #############################
|
||||
|
||||
|
@ -63,6 +63,7 @@ FEATURE_FLAGS = {
|
||||
'class-methods-private': '--harmony-private-methods',
|
||||
'class-static-methods-private': '--harmony-private-methods',
|
||||
'AggregateError': '--harmony-promise-any',
|
||||
'logical-assignment-operators': '--harmony-logical-assignment',
|
||||
}
|
||||
|
||||
SKIPPED_FEATURES = set([])
|
||||
|
Loading…
Reference in New Issue
Block a user