Bug 1611777 - Part 7: Add missing entries to list of valid optional chain start expressions. r=yulia
authorAndré Bargull <andre.bargull@gmail.com>
Wed, 29 Jan 2020 16:26:06 +0000
changeset 512098 5c9241e7ce20ad0f1cfe2b852e4b86f8054f1c85
parent 512097 4a8a350bba27258f5821dec1e37157e39375e3d1
child 512099 958adec635321bfe453ac74672a8e2037b953259
push id37072
push usercsabou@mozilla.com
push dateThu, 30 Jan 2020 15:44:43 +0000
treeherdermozilla-central@f97c48da9cee [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyulia
bugs1611777
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1611777 - Part 7: Add missing entries to list of valid optional chain start expressions. r=yulia The previous list contained some invalid entries (`await` and comma-expression) and was missing some possible parse node kinds. Differential Revision: https://phabricator.services.mozilla.com/D61154
js/src/frontend/BytecodeEmitter.cpp
js/src/tests/non262/expressions/optional-chain-first-expression.js
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7751,35 +7751,45 @@ bool BytecodeEmitter::emitOptionalTree(P
         return false;
       }
       break;
     // List of accepted ParseNodeKinds that might appear only at the beginning
     // of an Optional Chain.
     // For example, a taggedTemplateExpr node might occur if we have
     // `test`?.b, with `test` as the taggedTemplateExpr ParseNode.
     default:
-      MOZ_ASSERT(
-          (kind == ParseNodeKind::ArrayExpr ||
-           kind == ParseNodeKind::ObjectExpr ||
-           kind == ParseNodeKind::TrueExpr ||
-           kind == ParseNodeKind::FalseExpr ||
-           kind == ParseNodeKind::StringExpr ||
-           kind == ParseNodeKind::NumberExpr ||
-           kind == ParseNodeKind::RawUndefinedExpr ||
-           kind == ParseNodeKind::NullExpr || kind == ParseNodeKind::Name ||
-           kind == ParseNodeKind::Function || kind == ParseNodeKind::ThisExpr ||
-           kind == ParseNodeKind::TaggedTemplateExpr ||
-           kind == ParseNodeKind::TemplateStringExpr ||
-           kind == ParseNodeKind::AwaitExpr ||
-           kind == ParseNodeKind::RegExpExpr ||
-           kind == ParseNodeKind::ClassDecl ||
-           kind == ParseNodeKind::CommaExpr || kind == ParseNodeKind::NewExpr ||
-           kind == ParseNodeKind::SetThis ||
-           kind == ParseNodeKind::NewTargetExpr),
-          "Unknown ParseNodeKind for OptionalChain");
+#ifdef DEBUG
+      // https://tc39.es/ecma262/#sec-primary-expression
+      bool isPrimaryExpression =
+          kind == ParseNodeKind::ThisExpr || kind == ParseNodeKind::Name ||
+          kind == ParseNodeKind::NullExpr || kind == ParseNodeKind::TrueExpr ||
+          kind == ParseNodeKind::FalseExpr ||
+          kind == ParseNodeKind::NumberExpr ||
+          kind == ParseNodeKind::BigIntExpr ||
+          kind == ParseNodeKind::StringExpr ||
+          kind == ParseNodeKind::ArrayExpr ||
+          kind == ParseNodeKind::ObjectExpr ||
+          kind == ParseNodeKind::Function || kind == ParseNodeKind::ClassDecl ||
+          kind == ParseNodeKind::RegExpExpr ||
+          kind == ParseNodeKind::TemplateStringExpr ||
+          kind == ParseNodeKind::RawUndefinedExpr || pn->isInParens();
+
+      // https://tc39.es/ecma262/#sec-left-hand-side-expressions
+      bool isMemberExpression = isPrimaryExpression ||
+                                kind == ParseNodeKind::TaggedTemplateExpr ||
+                                kind == ParseNodeKind::NewExpr ||
+                                kind == ParseNodeKind::NewTargetExpr ||
+                                kind == ParseNodeKind::ImportMetaExpr;
+
+      bool isCallExpression = kind == ParseNodeKind::SetThis ||
+                              kind == ParseNodeKind::CallImportExpr;
+
+      MOZ_ASSERT(isMemberExpression || isCallExpression,
+                 "Unknown ParseNodeKind for OptionalChain");
+#endif
       return emitTree(pn);
   }
   return true;
 }
 
 // Handle the case of a call made on a OptionalChainParseNode.
 // For example `(a?.b)()` and `(a?.b)?.()`.
 bool BytecodeEmitter::emitCalleeAndThisForOptionalChain(
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/expressions/optional-chain-first-expression.js
@@ -0,0 +1,92 @@
+// Verify bytecode emitter accepts all valid optional chain first expressions.
+
+const expressions = [
+  // https://tc39.es/ecma262/#sec-primary-expression
+  "this",
+  "ident",
+  "null",
+  "true",
+  "false",
+  "123",
+  "123n",
+  "'str'",
+  "[]",
+  "{}",
+  "function(){}",
+  "class{}",
+  "function*(){}",
+  "async function(){}",
+  "async function*(){}",
+  "/a/",
+  "`str`",
+  "(a + b)",
+
+  // https://tc39.es/ecma262/#sec-left-hand-side-expressions
+  "a[b]",
+  "a.b",
+  "a``",
+  "super[a]",
+  "super.a",
+  "new.target",
+  "import.meta",
+  "new C()",
+  "new C",
+  "f()",
+  "super()",
+  "a?.b",
+  "a?.[b]",
+  "a?.()",
+  "a?.``",
+];
+
+function tryParse(s, f = Function) {
+  try { f(s); } catch {}
+}
+
+function tryRun(s, f = Function) {
+  try { f(s)(); } catch {}
+}
+
+for (let expr of expressions) {
+  // Evaluate in an expression context.
+  tryRun(`void (${expr}?.());`);
+  tryRun(`void (${expr}?.p());`);
+
+  // Also try parenthesized.
+  tryRun(`void ((${expr})?.());`);
+  tryRun(`void ((${expr})?.p());`);
+}
+
+function inClassConstructor(s) {
+  return `class C { constructor() { ${s} } }`;
+}
+
+for (let expr of ["super[a]", "super.a", "super()"]) {
+  // Evaluate in an expression context.
+  tryRun(inClassConstructor(`void (${expr}?.());`));
+  tryRun(inClassConstructor(`void (${expr}?.p());`));
+
+  // Also try parenthesized.
+  tryRun(inClassConstructor(`void ((${expr})?.());`));
+  tryRun(inClassConstructor(`void ((${expr})?.p());`));
+}
+
+if (typeof parseModule === "function") {
+  const expressions = [
+    "import.meta",
+    "import('')",
+  ];
+
+  for (let expr of expressions) {
+    // Evaluate in an expression context.
+    tryParse(`void (${expr}?.());`, parseModule);
+    tryParse(`void (${expr}?.p());`, parseModule);
+
+    // Also try parenthesized.
+    tryParse(`void ((${expr})?.());`, parseModule);
+    tryParse(`void ((${expr})?.p());`, parseModule);
+  }
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);