Skip to content

Commit

Permalink
Implement logical assignment
Browse files Browse the repository at this point in the history
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}
  • Loading branch information
devsnek authored and Commit Bot committed Apr 23, 2020
1 parent 6458a52 commit b151d8d
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 58 deletions.
3 changes: 2 additions & 1 deletion src/flags/flag-definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
1 change: 1 addition & 0 deletions src/init/bootstrapper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
20 changes: 17 additions & 3 deletions src/interpreter/bytecode-generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/parsing/parse-info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/parsing/parse-info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 5 additions & 0 deletions src/parsing/parser-base.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
12 changes: 6 additions & 6 deletions src/parsing/scanner-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down
10 changes: 5 additions & 5 deletions src/parsing/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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 */ \
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 0 additions & 3 deletions test/cctest/test-parsing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions test/mjsunit/harmony/logical-assignment.js
Original file line number Diff line number Diff line change
@@ -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);
}
43 changes: 4 additions & 39 deletions test/test262/test262.status
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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 #############################

Expand Down
1 change: 1 addition & 0 deletions test/test262/testcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
'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([])
Expand Down

0 comments on commit b151d8d

Please sign in to comment.