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:
Gus Caplan 2020-04-22 11:57:57 -05:00 committed by Commit Bot
parent 6458a5296b
commit b151d8db22
12 changed files with 86 additions and 58 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -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([])