Reland "Set .name of anonymous functions on the RHS of logical assignment."
This is a reland of c342ba8247730d96960f1da85d6b77b4e3cad9cc
Original change's description:
> Set .name of anonymous functions on the RHS of logical assignment.
>
> https://github.com/tc39/proposal-logical-assignment/pull/24 reached
> consensus in June TC39.
>
> Drive-by refactoring of testing for logical assignment ops using
> IsInRange.
>
> Bug: v8:10579
> Change-Id: I5a203ba552a905cd28f75c5d223998431a1966ce
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2225809
> Reviewed-by: Marja Hölttä <marja@chromium.org>
> Commit-Queue: Shu-yu Guo <syg@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#68101}
Bug: v8:10579
Change-Id: I321cf0e29515a146844abc05250e9b50ad651caf
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2227255
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68161}
diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h
index 903ce2b..88c7384 100644
--- a/src/parsing/parser-base.h
+++ b/src/parsing/parser-base.h
@@ -2755,8 +2755,7 @@
Token::Value op = peek();
if (!Token::IsArrowOrAssignmentOp(op)) return expression;
- if ((op == Token::ASSIGN_NULLISH || op == Token::ASSIGN_OR ||
- op == Token::ASSIGN_AND) &&
+ if (Token::IsLogicalAssignmentOp(op) &&
!flags().allow_harmony_logical_assignment()) {
return expression;
}
@@ -2830,13 +2829,8 @@
ExpressionT right = ParseAssignmentExpression();
- if (op == Token::ASSIGN) {
- // We try to estimate the set of properties set by constructors. We define a
- // new property whenever there is an assignment to a property of 'this'. We
- // should probably only add properties if we haven't seen them before.
- // Otherwise we'll probably overestimate the number of properties.
- if (impl()->IsThisProperty(expression)) function_state_->AddProperty();
-
+ // Anonymous function name inference applies to =, ||=, &&=, and ??=.
+ if (op == Token::ASSIGN || Token::IsLogicalAssignmentOp(op)) {
impl()->CheckAssigningFunctionLiteralToProperty(expression, right);
// Check if the right hand side is a call to avoid inferring a
@@ -2850,10 +2844,20 @@
impl()->SetFunctionNameFromIdentifierRef(right, expression);
} else {
+ fni_.RemoveLastFunction();
+ }
+
+ if (op == Token::ASSIGN) {
+ // We try to estimate the set of properties set by constructors. We define a
+ // new property whenever there is an assignment to a property of 'this'. We
+ // should probably only add properties if we haven't seen them before.
+ // Otherwise we'll probably overestimate the number of properties.
+ if (impl()->IsThisProperty(expression)) function_state_->AddProperty();
+ } else {
+ // Only initializers (i.e. no compound assignments) are allowed in patterns.
expression_scope()->RecordPatternError(
Scanner::Location(lhs_beg_pos, end_position()),
MessageTemplate::kInvalidDestructuringTarget);
- fni_.RemoveLastFunction();
}
return factory()->NewAssignment(op, expression, right, op_position);
diff --git a/src/parsing/token.h b/src/parsing/token.h
index ef92238..dabbff0 100644
--- a/src/parsing/token.h
+++ b/src/parsing/token.h
@@ -284,6 +284,10 @@
return base::IsInRange(token, INIT, ASSIGN_SUB);
}
+ static bool IsLogicalAssignmentOp(Value token) {
+ return base::IsInRange(token, ASSIGN_NULLISH, ASSIGN_AND);
+ }
+
static bool IsBinaryOp(Value op) { return base::IsInRange(op, COMMA, SUB); }
static bool IsCompareOp(Value op) { return base::IsInRange(op, EQ, IN); }
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
index a7a846b..5fbefb6 100644
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -1573,6 +1573,7 @@
kAllowHarmonyPrivateMethods,
kAllowHarmonyDynamicImport,
kAllowHarmonyImportMeta,
+ kAllowHarmonyLogicalAssignment,
};
enum ParserSyncTestResult {
@@ -1586,6 +1587,8 @@
i::FLAG_harmony_private_methods = flags.contains(kAllowHarmonyPrivateMethods);
i::FLAG_harmony_dynamic_import = flags.contains(kAllowHarmonyDynamicImport);
i::FLAG_harmony_import_meta = flags.contains(kAllowHarmonyImportMeta);
+ i::FLAG_harmony_logical_assignment =
+ flags.contains(kAllowHarmonyLogicalAssignment);
}
void SetParserFlags(i::UnoptimizedCompileFlags* compile_flags,
@@ -1597,6 +1600,8 @@
flags.contains(kAllowHarmonyDynamicImport));
compile_flags->set_allow_harmony_import_meta(
flags.contains(kAllowHarmonyImportMeta));
+ compile_flags->set_allow_harmony_logical_assignment(
+ flags.contains(kAllowHarmonyLogicalAssignment));
}
void TestParserSyncWithFlags(i::Handle<i::String> source,
@@ -11741,6 +11746,36 @@
SyntaxErrorTest(other_context_data, hashbang_data);
}
+TEST(LogicalAssignmentDestructuringErrors) {
+ // clang-format off
+ const char* context_data[][2] = {
+ { "if (", ") { foo(); }" },
+ { "(", ")" },
+ { "foo(", ")" },
+ { nullptr, nullptr }
+ };
+ const char* error_data[] = {
+ "[ x ] ||= [ 2 ]",
+ "[ x ||= 2 ] = [ 2 ]",
+ "{ x } ||= { x: 2 }",
+ "{ x: x ||= 2 ] = { x: 2 }",
+ "[ x ] &&= [ 2 ]",
+ "[ x &&= 2 ] = [ 2 ]",
+ "{ x } &&= { x: 2 }",
+ "{ x: x &&= 2 ] = { x: 2 }",
+ R"JS([ x ] ??= [ 2 ])JS",
+ R"JS([ x ??= 2 ] = [ 2 ])JS",
+ R"JS({ x } ??= { x: 2 })JS",
+ R"JS({ x: x ??= 2 ] = { x: 2 })JS",
+ nullptr
+ };
+ // clang-format on
+
+ static const ParserFlag flags[] = {kAllowHarmonyLogicalAssignment};
+ RunParserSyncTest(context_data, error_data, kError, nullptr, 0, flags,
+ arraysize(flags));
+}
+
} // namespace test_parsing
} // namespace internal
} // namespace v8
diff --git a/test/mjsunit/harmony/logical-assignment-function-name.js b/test/mjsunit/harmony/logical-assignment-function-name.js
new file mode 100644
index 0000000..f894664
--- /dev/null
+++ b/test/mjsunit/harmony/logical-assignment-function-name.js
@@ -0,0 +1,27 @@
+// 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 = undefined;
+ x ??= function() {};
+
+ assertEquals(x.name, "x");
+}
+
+
+{
+ let y = false;
+ y ||= function() {};
+
+ assertEquals(y.name, "y");
+}
+
+{
+ let z = true;
+ z &&= function() {};
+
+ assertEquals(z.name, "z");
+}