Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2022-05-05 09:16:13 +08:00
commit f3c7008077
333 changed files with 15703 additions and 5992 deletions

View file

@ -361,6 +361,8 @@ int main(int argc, char* argv[], char* envp[])
return 255;
}
XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
#ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
XRE_EnableSameExecutableForContentProc();
#endif

View file

@ -1042,11 +1042,6 @@
if (this.mCurrentBrowser == newBrowser && !aForceUpdate)
return;
if (!aForceUpdate) {
window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
.beginTabSwitch();
}
var oldTab = this.mCurrentTab;
// Preview mode should not reset the owner
@ -1757,8 +1752,6 @@
if (animate) {
requestAnimationFrame(function () {
this.tabContainer._handleTabTelemetryStart(t, aURI);
// kick the animation off
t.setAttribute("fadein", "true");
@ -1959,8 +1952,6 @@
return;
}
this.tabContainer._handleTabTelemetryStart(aTab);
this._blurTab(aTab);
aTab.style.maxWidth = ""; // ensure that fade-out transition happens
aTab.removeAttribute("fadein");
@ -3628,7 +3619,6 @@
window.addEventListener("resize", this, false);
window.addEventListener("load", this, false);
this._tabAnimationLoggingEnabled = Services.prefs.getBoolPref("browser.tabs.animationLogging.enabled", false);
this._browserNewtabpageEnabled = Services.prefs.getBoolPref("browser.newtabpage.enabled");
]]>
</constructor>
@ -4327,85 +4317,6 @@
</body>
</method>
<method name="_handleTabTelemetryStart">
<parameter name="aTab"/>
<parameter name="aURI"/>
<body>
<![CDATA[
// Animation-smoothness telemetry/logging
if (this._tabAnimationLoggingEnabled) {
if (aURI == "about:newtab" && (aTab._tPos == 1 || aTab._tPos == 2)) {
// Indicate newtab page animation where other tabs are unaffected
// (for which case, the 2nd or 3rd tabs are good representatives, even if not absolute)
aTab._recordingTabOpenPlain = true;
}
aTab._recordingHandle = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.startFrameTimeRecording();
}
// Overall animation duration
aTab._animStartTime = Date.now();
]]>
</body>
</method>
<method name="_handleTabTelemetryEnd">
<parameter name="aTab"/>
<body>
<![CDATA[
if (!aTab._animStartTime) {
return;
}
aTab._animStartTime = 0;
// Handle tab animation smoothness telemetry/logging of frame intervals and paint times
if (!("_recordingHandle" in aTab)) {
return;
}
let paints = {};
let intervals = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.stopFrameTimeRecording(aTab._recordingHandle, paints);
delete aTab._recordingHandle;
paints = paints.value; // The result array itself.
let frameCount = intervals.length;
if (this._tabAnimationLoggingEnabled) {
let msg = "Tab " + (aTab.closing ? "close" : "open") + " (Frame-interval / paint-processing):\n";
for (let i = 0; i < frameCount; i++) {
msg += Math.round(intervals[i]) + " / " + Math.round(paints[i]) + "\n";
}
Services.console.logStringMessage(msg);
}
// For telemetry, the first frame interval is not useful since it may represent an interval
// to a relatively old frame (prior to recording start). So we'll ignore it for the average.
// But if we recorded only 1 frame (very rare), then the first paint duration is a good
// representative of the first frame interval for our cause (indicates very bad animation).
// First paint duration is always useful for us.
if (frameCount > 0) {
let averageInterval = 0;
let averagePaint = paints[0];
for (let i = 1; i < frameCount; i++) {
averageInterval += intervals[i];
averagePaint += paints[i];
};
averagePaint /= frameCount;
averageInterval = (frameCount == 1)
? averagePaint
: averageInterval / (frameCount - 1);
if (aTab._recordingTabOpenPlain) {
delete aTab._recordingTabOpenPlain;
}
}
]]>
</body>
</method>
<!-- Deprecated stuff, implemented for backwards compatibility. -->
<property name="mTabstripClosebutton" readonly="true"
onget="return document.getElementById('tabs-closebutton');"/>
@ -4422,8 +4333,6 @@
var tab = event.target;
this._handleTabTelemetryEnd(tab);
if (tab.getAttribute("fadein") == "true") {
if (tab._fullyOpen)
this.adjustTabstrip();

View file

@ -1 +1 @@
28.10.5a1
28.10.6a1

View file

@ -195,7 +195,6 @@ this.AutoCompletePopup = {
this.openedPopup.hidePopup();
} catch(e) {
Cu.reportError(e);
console.log("Debug: ", this.openedPopup);
}
}
AutoCompleteTreeView.clearResults();

View file

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
4.8.4
4.8.5

View file

@ -178,6 +178,22 @@ function JSPropertyProvider(dbgObject, anEnvironment, inputValue, cursor) {
}
let completionPart = inputValue.substring(beginning.startPos);
// Strip optional chaining characters from completion part, which we check
// by looking if it has ?. and if there are no digits (marker for ternary).
let optionalChainRegex = /\?\./g;
let optionalElemAccessRegex = /\?\.\[/g;
let digitRegex = /\?\.\d/;
// Handle optional element access
if (optionalElemAccessRegex.test(completionPart)) {
completionPart = completionPart.replace(optionalElemAccessRegex, "[");
}
// Handle optional chaining characters
if (optionalChainRegex.test(completionPart) &&
!digitRegex.test(completionPart)) {
completionPart = completionPart.replace(optionalChainRegex, ".");
}
let lastDot = completionPart.lastIndexOf(".");
// Don't complete on just an empty string.

View file

@ -145,6 +145,38 @@ function runChecks(dbgObject, dbgEnv) {
do_print("Test that suggestions are not given if there is an hyphen in the chain.");
results = JSPropertyProvider(dbgObject, dbgEnv, "testHyphenated['prop-A'].");
do_check_null(results);
do_print("Test that expression with optional chaining operator are completed");
results = JSPropertyProvider(dbgObject, dbgEnv, "testObject?.prop");
test_has_result(results, "propA");
results = JSPropertyProvider(dbgObject, dbgEnv, "testObject?.propA[0]?.propB?.to");
test_has_result(results, "toString");
results = JSPropertyProvider(dbgObject, dbgEnv, "testObject?.propA?.[0]?.propB?.to");
test_has_result(results, "toString");
results = JSPropertyProvider(dbgObject, dbgEnv, "[1,2,3]?.");
test_has_result(results, "indexOf");
results = JSPropertyProvider(dbgObject, dbgEnv, "'foo'?.");
test_has_result(results, "charAt");
// Check this doesn't throw since `propC` is not defined.
results = JSPropertyProvider(dbgObject, dbgEnv, "testObject?.propC?.this?.does?.not?.exist?.d");
// Test more ternary
results = JSPropertyProvider(dbgObject, dbgEnv, "true ? t");
test_has_result(results, "testObject");
results = JSPropertyProvider(dbgObject, dbgEnv, "true ?? t");
test_has_result(results, "testObject");
results = JSPropertyProvider(dbgObject, dbgEnv, "true ? /* comment */ t");
test_has_result(results, "testObject");
results = JSPropertyProvider(dbgObject, dbgEnv, "true?<t");
test_has_no_results(results);
}
/**

View file

@ -69,9 +69,12 @@ public:
}
DOMHighResTimeStamp FetchStart() const {
return mTiming
if (mTiming) {
return mTiming->TimingAllowed()
? mTiming->FetchStartHighRes()
: 0;
: StartTime();
}
return 0;
}
DOMHighResTimeStamp RedirectStart() const {

View file

@ -576,10 +576,11 @@ class NodeBuilder
MutableHandleValue dst);
MOZ_MUST_USE bool callExpression(HandleValue callee, NodeVector& args, TokenPos* pos,
MutableHandleValue dst);
MutableHandleValue dst, bool isOptional = false);
MOZ_MUST_USE bool memberExpression(bool computed, HandleValue expr, HandleValue member,
TokenPos* pos, MutableHandleValue dst);
TokenPos* pos, MutableHandleValue dst,
bool isOptional = false);
MOZ_MUST_USE bool arrayExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
@ -593,6 +594,11 @@ class NodeBuilder
MOZ_MUST_USE bool spreadExpression(HandleValue expr, TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool optionalExpression(HandleValue expr, TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool deleteOptionalExpression(HandleValue expr, TokenPos* pos,
MutableHandleValue dst);
MOZ_MUST_USE bool computedName(HandleValue name, TokenPos* pos, MutableHandleValue dst);
MOZ_MUST_USE bool objectExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
@ -1121,7 +1127,7 @@ NodeBuilder::sequenceExpression(NodeVector& elts, TokenPos* pos, MutableHandleVa
bool
NodeBuilder::callExpression(HandleValue callee, NodeVector& args, TokenPos* pos,
MutableHandleValue dst)
MutableHandleValue dst, bool isOptional)
{
RootedValue array(cx);
if (!newArray(args, &array))
@ -1131,9 +1137,12 @@ NodeBuilder::callExpression(HandleValue callee, NodeVector& args, TokenPos* pos,
if (!cb.isNull())
return callback(cb, callee, array, pos, dst);
return newNode(AST_CALL_EXPR, pos,
"callee", callee,
"arguments", array,
return newNode(isOptional ? AST_OPT_CALL_EXPR : AST_CALL_EXPR,
pos,
"callee",
callee,
"arguments",
array,
dst);
}
@ -1157,7 +1166,7 @@ NodeBuilder::newExpression(HandleValue callee, NodeVector& args, TokenPos* pos,
bool
NodeBuilder::memberExpression(bool computed, HandleValue expr, HandleValue member, TokenPos* pos,
MutableHandleValue dst)
MutableHandleValue dst, bool isOptional)
{
RootedValue computedVal(cx, BooleanValue(computed));
@ -1165,10 +1174,14 @@ NodeBuilder::memberExpression(bool computed, HandleValue expr, HandleValue membe
if (!cb.isNull())
return callback(cb, computedVal, expr, member, pos, dst);
return newNode(AST_MEMBER_EXPR, pos,
"object", expr,
"property", member,
"computed", computedVal,
return newNode(isOptional ? AST_OPT_MEMBER_EXPR : AST_MEMBER_EXPR,
pos,
"object",
expr,
"property",
member,
"computed",
computedVal,
dst);
}
@ -1231,6 +1244,22 @@ NodeBuilder::spreadExpression(HandleValue expr, TokenPos* pos, MutableHandleValu
dst);
}
bool
NodeBuilder::optionalExpression(HandleValue expr, TokenPos* pos, MutableHandleValue dst)
{
return newNode(AST_OPTIONAL_EXPR, pos,
"expression", expr,
dst);
}
bool
NodeBuilder::deleteOptionalExpression(HandleValue expr, TokenPos* pos, MutableHandleValue dst)
{
return newNode(AST_DELETE_OPTIONAL_EXPR, pos,
"expression", expr,
dst);
}
bool
NodeBuilder::propertyPattern(HandleValue key, HandleValue patt, bool isShorthand, TokenPos* pos,
MutableHandleValue dst)
@ -2950,9 +2979,22 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_GENEXP:
return generatorExpression(pn->generatorExpr(), dst);
case PNK_DELETEOPTCHAIN: {
RootedValue expr(cx);
return expression(pn->pn_kid, &expr) &&
builder.deleteOptionalExpression(expr, &pn->pn_pos, dst);
}
case PNK_OPTCHAIN: {
RootedValue expr(cx);
return expression(pn->pn_kid, &expr) &&
builder.optionalExpression(expr, &pn->pn_pos, dst);
}
case PNK_NEW:
case PNK_TAGGED_TEMPLATE:
case PNK_CALL:
case PNK_OPTCALL:
case PNK_SUPERCALL:
{
ParseNode* next = pn->pn_head;
@ -2984,13 +3026,15 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
if (pn->getKind() == PNK_TAGGED_TEMPLATE)
return builder.taggedTemplate(callee, args, &pn->pn_pos, dst);
bool isOptional = pn->isKind(PNK_OPTCALL);
// SUPERCALL is Call(super, args)
return pn->isKind(PNK_NEW)
? builder.newExpression(callee, args, &pn->pn_pos, dst)
: builder.callExpression(callee, args, &pn->pn_pos, dst);
: builder.callExpression(callee, args, &pn->pn_pos, dst, isOptional);
}
case PNK_OPTDOT:
case PNK_DOT:
{
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
@ -2999,7 +3043,10 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
RootedValue propname(cx);
RootedAtom pnAtom(cx, pn->pn_atom);
if (pn->as<PropertyAccess>().isSuper()) {
bool isSuper = pn->is<PropertyAccess>() &&
pn->as<PropertyAccess>().isSuper();
if (isSuper) {
if (!builder.super(&pn->pn_expr->pn_pos, &expr))
return false;
} else {
@ -3007,10 +3054,14 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
return false;
}
bool isOptional = pn->isKind(PNK_OPTDOT);
return identifier(pnAtom, nullptr, &propname) &&
builder.memberExpression(false, expr, propname, &pn->pn_pos, dst);
builder.memberExpression(false, expr, propname, &pn->pn_pos,
dst, isOptional);
}
case PNK_OPTELEM:
case PNK_ELEM:
{
MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
@ -3018,7 +3069,10 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
RootedValue left(cx), right(cx);
if (pn->as<PropertyByValue>().isSuper()) {
bool isSuper = pn->is<PropertyByValue>() &&
pn->as<PropertyByValue>().isSuper();
if (isSuper) {
if (!builder.super(&pn->pn_left->pn_pos, &left))
return false;
} else {
@ -3026,8 +3080,11 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
return false;
}
bool isOptional = pn->isKind(PNK_OPTELEM);
return expression(pn->pn_right, &right) &&
builder.memberExpression(true, left, right, &pn->pn_pos, dst);
builder.memberExpression(true, left, right, &pn->pn_pos, dst,
isOptional);
}
case PNK_CALLSITEOBJ:

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,8 @@
#include "frontend/SourceNotes.h"
#include "vm/Interpreter.h"
class OptionalEmitter;
namespace js {
namespace frontend {
@ -456,6 +458,11 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitTree(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue,
EmitLineNumberNote emitLineNote = EMIT_LINENOTE);
// Emit code for the optional tree rooted at pn.
MOZ_MUST_USE bool emitOptionalTree(ParseNode* pn,
OptionalEmitter& oe,
ValueUsage valueUsage = ValueUsage::WantValue);
// Emit code for the tree rooted at pn with its own TDZ cache.
MOZ_MUST_USE bool emitTreeInBranch(ParseNode* pn,
ValueUsage valueUsage = ValueUsage::WantValue);
@ -731,6 +738,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitAsyncIterator();
// XXX currently used only by OptionalEmitter, research still required
// to identify when this was introduced in m-c.
MOZ_MUST_USE bool emitPushNotUndefinedOrNull();
// Pops iterator from the top of the stack. Pushes the result of |.next()|
// onto the stack.
MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync,
@ -776,6 +787,32 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitDeleteElement(ParseNode* pn);
MOZ_MUST_USE bool emitDeleteExpression(ParseNode* pn);
// Optional methods which emit Optional Jump Target
MOZ_MUST_USE bool emitOptionalChain(ParseNode* optionalChain,
ValueUsage valueUsage);
MOZ_MUST_USE bool emitCalleeAndThisForOptionalChain(ParseNode* optionalChain,
ParseNode* callNode,
bool isCall);
MOZ_MUST_USE bool emitDeleteOptionalChain(ParseNode* deleteNode);
// Optional methods which emit a shortCircuit jump. They need to be called by
// a method which emits an Optional Jump Target, see below.
MOZ_MUST_USE bool emitOptionalDotExpression(PropertyAccessBase* prop,
OptionalEmitter& oe,
ParseNode* calleeNode,
bool isCall);
MOZ_MUST_USE bool emitOptionalElemExpression(PropertyByValueBase* elem,
OptionalEmitter& oe,
ParseNode* calleeNode,
bool isCall);
MOZ_MUST_USE bool emitOptionalCall(ParseNode* callNode,
OptionalEmitter& oe,
ValueUsage valueUsage);
MOZ_MUST_USE bool emitDeletePropertyInOptChain(PropertyAccessBase* propExpr,
OptionalEmitter& oe);
MOZ_MUST_USE bool emitDeleteElementInOptChain(PropertyByValueBase* elemExpr,
OptionalEmitter& oe);
// |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR.
MOZ_MUST_USE bool emitTypeof(ParseNode* node, JSOp op);
@ -794,7 +831,22 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool isRestParameter(ParseNode* pn, bool* result);
MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn,
ValueUsage valueUsage = ValueUsage::WantValue);
MOZ_MUST_USE bool emitCalleeAndThis(ParseNode* callNode,
ParseNode* calleeNode,
bool isCall);
MOZ_MUST_USE bool emitOptionalCalleeAndThis(ParseNode* callNode,
ParseNode* calleeNode,
bool isCall,
OptionalEmitter& oe);
MOZ_MUST_USE bool emitCallOrNewThis(ParseNode* callNode,
bool isCall);
MOZ_MUST_USE bool emitCallOrNewArgumentsAndEnd(ParseNode* callNode,
ParseNode* calleeNode,
bool isCall,
ValueUsage valueUsage);
MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn);

View file

@ -319,6 +319,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_DELETEPROP:
case PNK_DELETEELEM:
case PNK_DELETEEXPR:
case PNK_DELETEOPTCHAIN:
case PNK_POS:
case PNK_NEG:
case PNK_PREINCREMENT:
@ -368,6 +369,10 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_DOT:
case PNK_ELEM:
case PNK_CALL:
case PNK_OPTCHAIN:
case PNK_OPTDOT:
case PNK_OPTELEM:
case PNK_OPTCALL:
case PNK_NAME:
case PNK_TEMPLATE_STRING:
case PNK_TEMPLATE_STRING_LIST:
@ -1304,7 +1309,7 @@ FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>&
{
ParseNode* node = *nodePtr;
MOZ_ASSERT(node->isKind(PNK_ELEM));
MOZ_ASSERT(node->isKind(PNK_ELEM) || node->isKind(PNK_OPTELEM));
MOZ_ASSERT(node->isArity(PN_BINARY));
ParseNode*& expr = node->pn_left;
@ -1348,9 +1353,15 @@ FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>&
// Optimization 3: We have expr["foo"] where foo is not an index. Convert
// to a property access (like expr.foo) that optimizes better downstream.
ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
if (!dottedAccess)
ParseNode* dottedAccess;
if (node->isKind(PNK_OPTELEM)) {
dottedAccess = parser.handler.newOptionalPropertyAccess(expr, name, node->pn_pos.end);
} else {
dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
}
if (!dottedAccess) {
return false;
}
dottedAccess->setInParens(node->isInParens());
ReplaceNode(nodePtr, dottedAccess);
@ -1522,7 +1533,9 @@ static bool
FoldCall(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
MOZ_ASSERT(node->isKind(PNK_CALL) ||
node->isKind(PNK_OPTCALL) ||
node->isKind(PNK_SUPERCALL) ||
node->isKind(PNK_TAGGED_TEMPLATE));
MOZ_ASSERT(node->isArity(PN_LIST));
@ -1599,13 +1612,13 @@ static bool
FoldDottedProperty(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_DOT));
MOZ_ASSERT(node->isKind(PNK_DOT) || node->isKind(PNK_OPTDOT));
MOZ_ASSERT(node->isArity(PN_NAME));
// Iterate through a long chain of dotted property accesses to find the
// most-nested non-dotted property node, then fold that.
ParseNode** nested = &node->pn_expr;
while ((*nested)->isKind(PNK_DOT)) {
while ((*nested)->isKind(PNK_DOT) || (*nested)->isKind(PNK_OPTDOT)) {
MOZ_ASSERT((*nested)->isArity(PN_NAME));
nested = &(*nested)->pn_expr;
}
@ -1632,6 +1645,9 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
JS_CHECK_RECURSION(cx, return false);
ParseNode* pn = *pnp;
#ifdef DEBUG
ParseNodeKind kind = pn->getKind();
#endif
switch (pn->getKind()) {
case PNK_NOP:
@ -1713,6 +1729,8 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
MOZ_ASSERT(pn->isArity(PN_BINARY));
return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
case PNK_DELETEOPTCHAIN:
case PNK_OPTCHAIN:
case PNK_SEMI:
case PNK_THIS:
MOZ_ASSERT(pn->isArity(PN_UNARY));
@ -1806,6 +1824,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
case PNK_CLASS:
return FoldClass(cx, pn, parser, inGenexpLambda);
case PNK_OPTELEM:
case PNK_ELEM:
return FoldElement(cx, pnp, parser, inGenexpLambda);
@ -1813,6 +1832,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
return FoldAdd(cx, pnp, parser, inGenexpLambda);
case PNK_CALL:
case PNK_OPTCALL:
case PNK_SUPERCALL:
case PNK_TAGGED_TEMPLATE:
return FoldCall(cx, pn, parser, inGenexpLambda);
@ -1896,6 +1916,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
MOZ_ASSERT(pn->isArity(PN_NAME));
return Fold(cx, &pn->pn_expr, parser, inGenexpLambda);
case PNK_OPTDOT:
case PNK_DOT:
return FoldDottedProperty(cx, pn, parser, inGenexpLambda);

View file

@ -74,6 +74,10 @@ class FullParseHandler
return node->isKind(PNK_DOT) || node->isKind(PNK_ELEM);
}
bool isOptionalPropertyAccess(ParseNode* node) {
return node->isKind(PNK_OPTDOT) || node->isKind(PNK_OPTELEM);
}
bool isFunctionCall(ParseNode* node) {
// Note: super() is a special form, *not* a function call.
return node->isKind(PNK_CALL);
@ -213,6 +217,18 @@ class FullParseHandler
if (expr->isKind(PNK_ELEM))
return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr);
if (expr->isKind(PNK_OPTCHAIN)) {
ParseNode* kid = expr->pn_kid;
// Handle property deletion explicitly. OptionalCall is handled
// via DeleteExpr.
if (kid->isKind(PNK_DOT) ||
kid->isKind(PNK_OPTDOT) ||
kid->isKind(PNK_ELEM) ||
kid->isKind(PNK_OPTELEM)) {
return newUnary(PNK_DELETEOPTCHAIN, JSOP_NOP, begin, kid);
}
}
return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr);
}
@ -319,6 +335,10 @@ class FullParseHandler
return newList(PNK_CALL, JSOP_CALL);
}
ParseNode* newOptionalCall() {
return newList(PNK_OPTCALL, JSOP_CALL);
}
ParseNode* newTaggedTemplate() {
return newList(PNK_TAGGED_TEMPLATE, JSOP_CALL);
}
@ -459,6 +479,11 @@ class FullParseHandler
return new_<UnaryNode>(PNK_AWAIT, JSOP_AWAIT, pos, value);
}
ParseNode* newOptionalChain(uint32_t begin, ParseNode* value) {
TokenPos pos(begin, value->pn_pos.end);
return new_<UnaryNode>(PNK_OPTCHAIN, JSOP_NOP, pos, value);
}
// Statements
ParseNode* newStatementList(const TokenPos& pos) {
@ -678,6 +703,14 @@ class FullParseHandler
return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
}
ParseNode* newOptionalPropertyAccess(ParseNode* pn, PropertyName* name, uint32_t end) {
return new_<OptionalPropertyAccess>(pn, name, pn->pn_pos.begin, end);
}
ParseNode* newOptionalPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
return new_<OptionalPropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
}
inline MOZ_MUST_USE bool addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
ParseNode* catchName, ParseNode* catchGuard,
ParseNode* catchBody);
@ -920,7 +953,9 @@ class FullParseHandler
return pn->isKind(PNK_CALL);
}
PropertyName* maybeDottedProperty(ParseNode* pn) {
return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr;
return pn->is<PropertyAccessBase>() ?
&pn->as<PropertyAccessBase>().name() :
nullptr;
}
JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) {
if (JSAtom* atom = pn->isStringExprStatement()) {

View file

@ -228,6 +228,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
return PushUnaryNodeChild(pn, stack);
// Nodes with a single nullable child.
case PNK_OPTCHAIN:
case PNK_DELETEOPTCHAIN:
case PNK_THIS:
case PNK_SEMI: {
MOZ_ASSERT(pn->isArity(PN_UNARY));
@ -253,6 +255,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_MODASSIGN:
case PNK_POWASSIGN:
// ...and a few others.
case PNK_OPTELEM:
case PNK_ELEM:
case PNK_IMPORT_SPEC:
case PNK_EXPORT_SPEC:
@ -449,6 +452,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_POW:
case PNK_COMMA:
case PNK_NEW:
case PNK_OPTCALL:
case PNK_CALL:
case PNK_SUPERCALL:
case PNK_GENEXP:
@ -483,6 +487,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
}
case PNK_LABEL:
case PNK_OPTDOT:
case PNK_DOT:
case PNK_NAME:
return PushNameNodeChildren(pn, stack);

View file

@ -34,6 +34,10 @@ class ObjectBox;
F(POSTDECREMENT) \
F(DOT) \
F(ELEM) \
F(OPTDOT) \
F(OPTCHAIN) \
F(OPTELEM) \
F(OPTCALL) \
F(ARRAY) \
F(ELISION) \
F(STATEMENTLIST) \
@ -76,6 +80,7 @@ class ObjectBox;
F(DELETEPROP) \
F(DELETEELEM) \
F(DELETEEXPR) \
F(DELETEOPTCHAIN) \
F(TRY) \
F(CATCH) \
F(CATCHLIST) \
@ -371,6 +376,29 @@ IsTypeofKind(ParseNodeKind kind)
* for a more-specific PNK_DELETE* unless constant
* folding (or a similar parse tree manipulation) has
* occurred
* PNK_DELETEOPTCHAIN unary pn_kid: MEMBER expr that's evaluated, then the
* overall delete evaluates to true; If constant
* folding occurs, PNK_ELEM may become PNK_DOT.
* PNK_OPTELEM does not get folded into PNK_OPTDOT.
* PNK_OPTCHAIN unary pn_kid: expression that is evaluated as a chain.
* Contains one or more optional nodes. Its first node
* is always an optional node, such as PNK_OPTELEM,
* PNK_OPTDOT, or PNK_OPTCALL. This will shortcircuit
* and return 'undefined' without evaluating the rest
* of the expression if any of the optional nodes it
* contains are nullish. An optional chain can also
* contain nodes such as PNK_DOT, PNK_ELEM, PNK_NAME,
* PNK_CALL, etc. These are evaluated normally.
* PNK_OPTDOT name pn_expr: MEMBER expr to left of .
* short circuits back to PNK_OPTCHAIN if nullish.
* pn_atom: name to right of .
* PNK_OPTELEM binary pn_left: MEMBER expr to left of [
* short circuits back to PNK_OPTCHAIN if nullish.
* pn_right: expr between [ and ]
* PNK_OPTCALL list pn_head: list of call, arg1, arg2, ... argN
* pn_count: 1 + N (where N is number of args)
* call is a MEMBER expr naming a callable object,
* short circuits back to PNK_OPTCHAIN if nullish.
* PNK_DOT name pn_expr: MEMBER expr to left of .
* pn_atom: name to right of .
* PNK_ELEM binary pn_left: MEMBER expr to left of [
@ -1182,11 +1210,12 @@ class RegExpLiteral : public NullaryNode
}
};
class PropertyAccess : public ParseNode
class PropertyAccessBase : public ParseNode
{
public:
PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end)
: ParseNode(PNK_DOT, JSOP_NOP, PN_NAME, TokenPos(begin, end))
PropertyAccessBase(ParseNodeKind kind, ParseNode* lhs, PropertyName* name,
uint32_t begin, uint32_t end)
: ParseNode(kind, JSOP_NOP, PN_NAME, TokenPos(begin, end))
{
MOZ_ASSERT(lhs != nullptr);
MOZ_ASSERT(name != nullptr);
@ -1195,7 +1224,8 @@ class PropertyAccess : public ParseNode
}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_DOT);
bool match = node.isKind(PNK_DOT) ||
node.isKind(PNK_OPTDOT);
MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
return match;
}
@ -1207,6 +1237,24 @@ class PropertyAccess : public ParseNode
PropertyName& name() const {
return *pn_u.name.atom->asPropertyName();
}
};
class PropertyAccess : public PropertyAccessBase
{
public:
PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin,
uint32_t end)
: PropertyAccessBase(PNK_DOT, lhs, name, begin, end)
{
MOZ_ASSERT(lhs != nullptr);
MOZ_ASSERT(name != nullptr);
}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_DOT);
MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
return match;
}
bool isSuper() const {
// PNK_SUPERBASE cannot result from any expression syntax.
@ -1214,16 +1262,49 @@ class PropertyAccess : public ParseNode
}
};
class PropertyByValue : public ParseNode
class OptionalPropertyAccess : public PropertyAccessBase
{
public:
PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, uint32_t end)
: ParseNode(PNK_ELEM, JSOP_NOP, PN_BINARY, TokenPos(begin, end))
OptionalPropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin,
uint32_t end)
: PropertyAccessBase(PNK_OPTDOT, lhs, name, begin, end)
{
MOZ_ASSERT(lhs != nullptr);
MOZ_ASSERT(name != nullptr);
}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_OPTDOT);
MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
return match;
}
};
class PropertyByValueBase : public ParseNode
{
public:
PropertyByValueBase(ParseNodeKind kind, ParseNode* lhs, ParseNode* propExpr,
uint32_t begin, uint32_t end)
: ParseNode(kind, JSOP_NOP, PN_BINARY, TokenPos(begin, end))
{
pn_u.binary.left = lhs;
pn_u.binary.right = propExpr;
}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_ELEM) ||
node.isKind(PNK_OPTELEM);
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
return match;
}
};
class PropertyByValue : public PropertyByValueBase {
public:
PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin,
uint32_t end)
: PropertyByValueBase(PNK_ELEM, lhs, propExpr, begin, end) {}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_ELEM);
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
@ -1235,6 +1316,19 @@ class PropertyByValue : public ParseNode
}
};
class OptionalPropertyByValue : public PropertyByValueBase {
public:
OptionalPropertyByValue(ParseNode* lhs, ParseNode* propExpr,
uint32_t begin, uint32_t end)
: PropertyByValueBase(PNK_OPTELEM, lhs, propExpr, begin, end) {}
static bool test(const ParseNode& node) {
bool match = node.isKind(PNK_OPTELEM);
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
return match;
}
};
/*
* A CallSiteNode represents the implicit call site object argument in a TaggedTemplate.
*/

View file

@ -7156,7 +7156,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
if (hasHeritage) {
if (!tokenStream.getToken(&tt))
return null();
classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt);
classHeritage = optionalExpr(yieldHandling, TripledotProhibited, tt);
if (!classHeritage)
return null();
}
@ -8363,6 +8363,108 @@ Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kin
return handler.newUnary(kind, op, begin, kid);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::optionalExpr(
YieldHandling yieldHandling, TripledotHandling tripledotHandling,
TokenKind tt, bool allowCallSyntax /* = true */,
PossibleError* possibleError /* = nullptr */,
InvokedPrediction invoked /* = PredictUninvoked */)
{
JS_CHECK_RECURSION(context, return null());
uint32_t begin = pos().begin;
Node lhs = memberExpr(yieldHandling, tripledotHandling, tt,
/* allowCallSyntax = */ true, possibleError, invoked);
if (!lhs) {
return null();
}
// Note: TokenStream::None is equivalent to TokenStream::SlashIsDiv
// in current Mozilla code, see bug 1539821.
if (!tokenStream.peekToken(&tt, TokenStream::None)) {
return null();
}
if (tt != TOK_OPTCHAIN) {
return lhs;
}
while (true) {
if (!tokenStream.getToken(&tt)) {
return null();
}
if (tt == TOK_EOF) {
break;
}
Node nextMember;
if (tt == TOK_OPTCHAIN) {
if (!tokenStream.getToken(&tt)) {
return null();
}
if (TokenKindIsPossibleIdentifierName(tt)) {
nextMember = memberPropertyAccess(lhs, OptionalKind::Optional);
if (!nextMember) {
return null();
}
} else if (tt == TOK_LB) {
nextMember = memberElemAccess(lhs, yieldHandling,
OptionalKind::Optional);
if (!nextMember) {
return null();
}
} else if (tt == TOK_LP) {
nextMember = memberCall(tt, lhs, yieldHandling, possibleError,
OptionalKind::Optional);
if (!nextMember) {
return null();
}
} else {
error(JSMSG_NAME_AFTER_DOT);
return null();
}
} else if (tt == TOK_DOT) {
if (!tokenStream.getToken(&tt)) {
return null();
}
if (TokenKindIsPossibleIdentifierName(tt)) {
nextMember = memberPropertyAccess(lhs);
if (!nextMember) {
return null();
}
} else {
error(JSMSG_NAME_AFTER_DOT);
return null();
}
} else if (tt == TOK_LB) {
nextMember = memberElemAccess(lhs, yieldHandling);
if (!nextMember) {
return null();
}
} else if (allowCallSyntax && tt == TOK_LP) {
nextMember = memberCall(tt, lhs, yieldHandling, possibleError);
if (!nextMember) {
return null();
}
} else if (tt == TOK_TEMPLATE_HEAD || tt == TOK_NO_SUBS_TEMPLATE) {
error(JSMSG_BAD_OPTIONAL_TEMPLATE);
return null();
} else {
tokenStream.ungetToken();
break;
}
MOZ_ASSERT(nextMember);
lhs = nextMember;
}
return handler.newOptionalChain(begin, lhs);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
@ -8414,7 +8516,7 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
return null();
uint32_t operandOffset = pos().begin;
Node operand = memberExpr(yieldHandling, TripledotProhibited, tt2);
Node operand = optionalExpr(yieldHandling, TripledotProhibited, tt2);
if (!operand || !checkIncDecOperand(operand, operandOffset))
return null();
@ -8456,8 +8558,9 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
MOZ_FALLTHROUGH;
default: {
Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
possibleError, invoked);
Node expr = optionalExpr(yieldHandling, tripledotHandling, tt,
/* allowCallSyntax = */ true,
possibleError, invoked);
if (!expr)
return null();
@ -8906,6 +9009,18 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
handler.addList(lhs, ctorExpr);
// If we have encountered an optional chain, in the form of `new
// ClassName?.()` then we need to throw, as this is disallowed
// by the spec.
bool optionalToken;
if (!tokenStream.matchToken(&optionalToken, TOK_OPTCHAIN)) {
return null();
}
if (optionalToken) {
errorAt(newBegin, JSMSG_BAD_NEW_OPTIONAL);
return null();
}
bool matched;
if (!tokenStream.matchToken(&matched, TOK_LP))
return null();
@ -8943,12 +9058,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
if (!tokenStream.getToken(&tt))
return null();
if (TokenKindIsPossibleIdentifierName(tt)) {
PropertyName* field = tokenStream.currentName();
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
error(JSMSG_BAD_SUPERPROP, "property");
return null();
}
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
nextMember = memberPropertyAccess(lhs);
if (!nextMember)
return null();
} else {
@ -8956,17 +9066,7 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
return null();
}
} else if (tt == TOK_LB) {
Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
if (!propExpr)
return null();
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
error(JSMSG_BAD_SUPERPROP, "member");
return null();
}
nextMember = handler.newPropertyByValue(lhs, propExpr, pos().end);
nextMember = memberElemAccess(lhs, yieldHandling);
if (!nextMember)
return null();
} else if ((allowCallSyntax && tt == TOK_LP) ||
@ -9002,82 +9102,17 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
if (!thisName)
return null();
// XXX This was refactored out into memberSuperCall as part of
// bug 1566143, but was not used elsewhere aside from here, so
// I opted not to move this into a separate function. However,
// I'm unsure if this will be needed eventually in the future.
nextMember = handler.newSetThis(thisName, nextMember);
if (!nextMember)
return null();
} else {
if (options().selfHostingMode && handler.isPropertyAccess(lhs)) {
error(JSMSG_SELFHOSTED_METHOD_CALL);
return null();
}
nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate();
nextMember = memberCall(tt, lhs, yieldHandling, possibleError);
if (!nextMember)
return null();
JSOp op = JSOP_CALL;
bool maybeAsyncArrow = false;
if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
// Use the JSOP_FUN{APPLY,CALL} optimizations given the
// right syntax.
if (prop == context->names().apply) {
op = JSOP_FUNAPPLY;
if (pc->isFunctionBox()) {
pc->functionBox()->usesApply = true;
}
} else if (prop == context->names().call) {
op = JSOP_FUNCALL;
}
} else if (tt == TOK_LP) {
if (handler.isAsyncKeyword(lhs, context)) {
// |async (| can be the start of an async arrow
// function, so we need to defer reporting possible
// errors from destructuring syntax. To give better
// error messages, we only allow the AsyncArrowHead
// part of the CoverCallExpressionAndAsyncArrowHead
// syntax when the initial name is "async".
maybeAsyncArrow = true;
} else if (handler.isEvalAnyParentheses(lhs, context)) {
// Select the right EVAL op and flag pc as having a
// direct eval.
op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
pc->sc()->setBindingsAccessedDynamically();
pc->sc()->setHasDirectEval();
// In non-strict mode code, direct calls to eval can
// add variables to the call object.
if (pc->isFunctionBox() && !pc->sc()->strict())
pc->functionBox()->setHasExtensibleScope();
// If we're in a method, mark the method as requiring
// support for 'super', since direct eval code can use
// it. (If we're not in a method, that's fine, so
// ignore the return value.)
checkAndMarkSuperScope();
}
}
handler.setBeginPosition(nextMember, lhs);
handler.addList(nextMember, lhs);
if (tt == TOK_LP) {
bool isSpread = false;
PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr;
if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError))
return null();
if (isSpread) {
if (op == JSOP_EVAL)
op = JSOP_SPREADEVAL;
else if (op == JSOP_STRICTEVAL)
op = JSOP_STRICTSPREADEVAL;
else
op = JSOP_SPREADCALL;
}
} else {
if (!taggedTemplate(yieldHandling, nextMember, tt))
return null();
}
handler.setOp(nextMember, op);
}
} else {
tokenStream.ungetToken();
@ -9111,6 +9146,158 @@ Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
return handler.newName(name, pos, context);
}
template <class ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::memberPropertyAccess(
Node lhs, OptionalKind optionalKind /* = OptionalKind::NonOptional */)
{
MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tokenStream.currentToken().type));
PropertyName* field = tokenStream.currentName();
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
error(JSMSG_BAD_SUPERPROP, "property");
return null();
}
if (optionalKind == OptionalKind::Optional) {
MOZ_ASSERT(!handler.isSuperBase(lhs));
return handler.newOptionalPropertyAccess(lhs, field, pos().end);
}
return handler.newPropertyAccess(lhs, field, pos().end);
}
template <class ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::memberElemAccess(
Node lhs, YieldHandling yieldHandling,
OptionalKind optionalKind /* = OptionalKind::NonOptional */)
{
MOZ_ASSERT(tokenStream.currentToken().type == TOK_LB);
Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
if (!propExpr) {
return null();
}
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
error(JSMSG_BAD_SUPERPROP, "member");
return null();
}
if (optionalKind == OptionalKind::Optional) {
MOZ_ASSERT(!handler.isSuperBase(lhs));
return handler.newOptionalPropertyByValue(lhs, propExpr, pos().end);
}
return handler.newPropertyByValue(lhs, propExpr, pos().end);
}
template <class ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::memberCall(
TokenKind tt, Node lhs, YieldHandling yieldHandling,
PossibleError* possibleError /* = nullptr */,
OptionalKind optionalKind /* = OptionalKind::NonOptional */)
{
if (options().selfHostingMode && (handler.isPropertyAccess(lhs) ||
handler.isOptionalPropertyAccess(lhs))) {
error(JSMSG_SELFHOSTED_METHOD_CALL);
return null();
}
MOZ_ASSERT(tt == TOK_LP ||
tt == TOK_TEMPLATE_HEAD ||
tt == TOK_NO_SUBS_TEMPLATE,
"Unexpected token kind for member call");
Node nextMember;
if (tt == TOK_LP) {
if (optionalKind == OptionalKind::Optional) {
nextMember = handler.newOptionalCall();
} else {
nextMember = handler.newCall();
}
} else {
nextMember = handler.newTaggedTemplate();
}
JSOp op = JSOP_CALL;
bool maybeAsyncArrow = false;
if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
// Use the JSOP_FUN{APPLY,CALL} optimizations given the
// right syntax.
if (prop == context->names().apply) {
op = JSOP_FUNAPPLY;
if (pc->isFunctionBox()) {
pc->functionBox()->usesApply = true;
}
} else if (prop == context->names().call) {
op = JSOP_FUNCALL;
}
} else if (tt == TOK_LP && optionalKind == OptionalKind::NonOptional) {
if (handler.isAsyncKeyword(lhs, context)) {
// |async (| can be the start of an async arrow
// function, so we need to defer reporting possible
// errors from destructuring syntax. To give better
// error messages, we only allow the AsyncArrowHead
// part of the CoverCallExpressionAndAsyncArrowHead
// syntax when the initial name is "async".
maybeAsyncArrow = true;
} else if (handler.isEvalAnyParentheses(lhs, context)) {
// Select the right EVAL op and flag pc as having a
// direct eval.
op = pc->sc()->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
pc->sc()->setBindingsAccessedDynamically();
pc->sc()->setHasDirectEval();
// In non-strict mode code, direct calls to eval can
// add variables to the call object.
if (pc->isFunctionBox() && !pc->sc()->strict()) {
pc->functionBox()->setHasExtensibleScope();
}
// If we're in a method, mark the method as requiring
// support for 'super', since direct eval code can use
// it. (If we're not in a method, that's fine, so
// ignore the return value.)
checkAndMarkSuperScope();
}
}
handler.setBeginPosition(nextMember, lhs);
handler.addList(nextMember, lhs);
if (tt == TOK_LP) {
bool isSpread = false;
PossibleError* asyncPossibleError = maybeAsyncArrow ?
possibleError :
nullptr;
if (!argumentList(yieldHandling, nextMember, &isSpread, asyncPossibleError)) {
return null();
}
if (isSpread) {
if (op == JSOP_EVAL) {
op = JSOP_SPREADEVAL;
} else if (op == JSOP_STRICTEVAL) {
op = JSOP_STRICTSPREADEVAL;
} else {
op = JSOP_SPREADCALL;
}
}
} else {
if (!taggedTemplate(yieldHandling, nextMember, tt)) {
return null();
}
if (optionalKind == OptionalKind::Optional) {
error(JSMSG_BAD_OPTIONAL_TEMPLATE);
return null();
}
}
handler.setOp(nextMember, op);
return nextMember;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkLabelOrIdentifierReference(HandlePropertyName ident,

View file

@ -1321,6 +1321,11 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
PossibleError* possibleError = nullptr,
InvokedPrediction invoked = PredictUninvoked);
Node optionalExpr(YieldHandling yieldHandling,
TripledotHandling tripledotHandling, TokenKind tt,
bool allowCallSyntax = true,
PossibleError* possibleError = nullptr,
InvokedPrediction invoked = PredictUninvoked);
Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
TokenKind tt, bool allowCallSyntax = true,
PossibleError* possibleError = nullptr,
@ -1532,6 +1537,18 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
bool asmJS(Node list);
enum class OptionalKind {
NonOptional = 0,
Optional,
};
Node memberPropertyAccess(
Node lhs, OptionalKind optionalKind = OptionalKind::NonOptional);
Node memberElemAccess(Node lhs, YieldHandling yieldHandling,
OptionalKind optionalKind = OptionalKind::NonOptional);
Node memberCall(TokenKind tt, Node lhs, YieldHandling yieldHandling,
PossibleError* possibleError,
OptionalKind optionalKind = OptionalKind::NonOptional);
};
template <typename ParseHandler>

View file

@ -49,6 +49,7 @@ namespace js {
M(SRC_BREAK, "break", 0) /* JSOP_GOTO is a break. */ \
M(SRC_BREAK2LABEL, "break2label", 0) /* JSOP_GOTO for 'break label'. */ \
M(SRC_SWITCHBREAK, "switchbreak", 0) /* JSOP_GOTO is a break in a switch. */ \
M(SRC_OPTCHAIN, "optchain", 0) /* JSOP_GOTO for optional chains. */ \
M(SRC_TABLESWITCH, "tableswitch", 1) /* JSOP_TABLESWITCH; offset points to end of switch. */ \
M(SRC_CONDSWITCH, "condswitch", 2) /* JSOP_CONDSWITCH; 1st offset points to end of switch, \
2nd points to first JSOP_CASE. */ \
@ -63,7 +64,6 @@ namespace js {
M(SRC_COLSPAN, "colspan", 1) /* Number of columns this opcode spans. */ \
M(SRC_NEWLINE, "newline", 0) /* Bytecode follows a source newline. */ \
M(SRC_SETLINE, "setline", 1) /* A file-absolute source line number note. */ \
M(SRC_UNUSED21, "unused21", 0) /* Unused. */ \
M(SRC_UNUSED22, "unused22", 0) /* Unused. */ \
M(SRC_UNUSED23, "unused23", 0) /* Unused. */ \
M(SRC_XDELTA, "xdelta", 0) /* 24-31 are for extended delta notes. */

View file

@ -58,6 +58,7 @@ class SyntaxParseHandler
// in code not actually executed (or at least not executed enough to be
// noticed).
NodeFunctionCall,
NodeOptionalFunctionCall,
// Nodes representing *parenthesized* IsValidSimpleAssignmentTarget
// nodes. We can't simply treat all such parenthesized nodes
@ -78,7 +79,9 @@ class SyntaxParseHandler
NodeParenthesizedName,
NodeDottedProperty,
NodeOptionalDottedProperty,
NodeElement,
NodeOptionalElement,
// Destructuring target patterns can't be parenthesized: |([a]) = [3];|
// must be a syntax error. (We can't use NodeGeneric instead of these
@ -146,6 +149,10 @@ class SyntaxParseHandler
return node == NodeDottedProperty || node == NodeElement;
}
bool isOptionalPropertyAccess(Node node) {
return node == NodeOptionalDottedProperty || node == NodeOptionalElement;
}
bool isFunctionCall(Node node) {
// Note: super() is a special form, *not* a function call.
return node == NodeFunctionCall;
@ -283,6 +290,7 @@ class SyntaxParseHandler
void addArrayElement(Node literal, Node element) { }
Node newCall() { return NodeFunctionCall; }
Node newOptionalCall() { return NodeOptionalFunctionCall; }
Node newTaggedTemplate() { return NodeGeneric; }
Node newObjectLiteral(uint32_t begin) { return NodeUnparenthesizedObject; }
@ -303,6 +311,7 @@ class SyntaxParseHandler
Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; }
Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; }
Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; }
Node newOptionalChain(uint32_t begin, Node value) { return NodeGeneric; }
// Statements
@ -353,8 +362,15 @@ class SyntaxParseHandler
return NodeDottedProperty;
}
Node newOptionalPropertyAccess(Node pn, PropertyName* name, uint32_t end) {
lastAtom = name;
return NodeOptionalDottedProperty;
}
Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
Node newOptionalPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeOptionalElement; }
MOZ_MUST_USE bool addCatchBlock(Node catchList, Node letBlock, Node catchName,
Node catchGuard, Node catchBody) { return true; }
@ -471,7 +487,8 @@ class SyntaxParseHandler
list == NodeUnparenthesizedCommaExpr ||
list == NodeVarDeclaration ||
list == NodeLexicalDeclaration ||
list == NodeFunctionCall);
list == NodeFunctionCall ||
list == NodeOptionalFunctionCall);
}
Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs, JSOp op) {
@ -590,7 +607,7 @@ class SyntaxParseHandler
// |this|. It's not really eligible for the funapply/funcall
// optimizations as they're currently implemented (assuming a single
// value is used for both retrieval and |this|).
if (node != NodeDottedProperty)
if (node != NodeDottedProperty && node != NodeOptionalDottedProperty)
return nullptr;
return lastAtom->asPropertyName();
}

View file

@ -63,6 +63,7 @@
macro(DEC, "'--'") /* decrement */ \
macro(DOT, "'.'") /* member operator */ \
macro(TRIPLEDOT, "'...'") /* rest arguments and spread operator */ \
macro(OPTCHAIN, "'?.'") \
macro(LB, "'['") \
macro(RB, "']'") \
macro(LC, "'{'") \

View file

@ -1285,7 +1285,7 @@ static const uint8_t firstCharKinds[] = {
/* 30+ */ _______, _______, Space, _______, String, _______, Ident, _______, _______, String,
/* 40+ */ TOK_LP, TOK_RP, _______, _______, T_COMMA,_______, _______, _______,BasePrefix, Dec,
/* 50+ */ Dec, Dec, Dec, Dec, Dec, Dec, Dec, Dec, T_COLON,TOK_SEMI,
/* 60+ */ _______, _______, _______,TOK_HOOK, _______, Ident, Ident, Ident, Ident, Ident,
/* 60+ */ _______, _______, _______, _______, _______, Ident, Ident, Ident, Ident, Ident,
/* 70+ */ Ident, Ident, Ident, Ident, Ident, Ident, Ident, Ident, Ident, Ident,
/* 80+ */ Ident, Ident, Ident, Ident, Ident, Ident, Ident, Ident, Ident, Ident,
/* 90+ */ Ident, TOK_LB, _______, TOK_RB, _______, Ident, Templat, Ident, Ident, Ident,
@ -1796,6 +1796,25 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
tp->type = matchChar('=') ? TOK_BITANDASSIGN : TOK_BITAND;
goto out;
case '?':
if (matchChar('.')) {
c = getCharIgnoreEOL();
if (JS7_ISDEC(c)) {
// if the code unit is followed by a number, for example it has
// the following form `<...> ?.5 <..> then it should be treated
// as a ternary rather than as an optional chain
tp->type = TOK_HOOK;
ungetCharIgnoreEOL(c);
ungetChar('.');
} else {
ungetCharIgnoreEOL(c);
tp->type = TOK_OPTCHAIN;
}
} else {
tp->type = TOK_HOOK;
}
goto out;
case '!':
if (matchChar('='))
tp->type = matchChar('=') ? TOK_STRICTNE : TOK_NE;

View file

@ -0,0 +1,5 @@
// |jit-test| skip-if: !('oomTest' in this)
var code = `
(\`\${key}: \${(args[1]?.toString)?.()}\`)
`;
oomTest(function() { return parseModule(code); });

View file

@ -0,0 +1,5 @@
// |jit-test| skip-if: !('oomTest' in this)
var code = `
(\`\${key}: \${args[1]?.toString()}\`)
`;
oomTest(function() { return parseModule(code); });

View file

@ -0,0 +1,34 @@
// Tests for JSOp::CallIgnoresRv in optional chains.
// Note:: IgnoresReturnValueNative is supported for Array.prototype.splice.
// Test for optional call.
function testOptionalCall() {
for (var i = 0; i < 100; ++i) {
var x = [1, 2, 3];
x.splice?.(0);
}
}
for (var i = 0; i < 5; ++i) { testOptionalCall(); }
// Test for optional prop directly followed by call.
function testOptionalProp() {
for (var i = 0; i < 100; ++i) {
var x = [1, 2, 3];
x?.splice(0);
}
}
for (var i = 0; i < 5; ++i) { testOptionalProp(); }
// Test for call in optional chain expression.
function testOptionalChain() {
for (var i = 0; i < 100; ++i) {
var x = [1, 2, 3];
var o = {x};
o?.x.splice(0);
}
}
for (var i = 0; i < 5; ++i) { testOptionalChain(); }

View file

@ -0,0 +1,60 @@
// Tests for JSOp::FunCall and JSOp::FunApply in optional calls.
function f1() {
return 0;
}
function f2(a) {
return a * 2;
}
function funCall(fn) {
// Without arguments.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.call(), 0);
}
// Only this-arg.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.call(null), 0);
}
// With one arg.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.call(null, 1), 0);
assertEq(f2?.call(null, 5), 10);
}
// With multiple args.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.call(null, 1, 2, 3), 0);
assertEq(f2?.call(null, 4, 5, 6), 8);
}
}
for (var i = 0; i < 5; ++i) { funCall(); }
function funApply(fn) {
// Without arguments.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.apply(), 0);
}
// Only this-arg.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.apply(null), 0);
}
// With one arg.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.apply(null, [1]), 0);
assertEq(f2?.apply(null, [5]), 10);
}
// With multiple args.
for (var i = 0; i < 100; ++i) {
assertEq(f1?.apply(null, [1, 2, 3]), 0);
assertEq(f2?.apply(null, [4, 5, 6]), 8);
}
}
for (var i = 0; i < 5; ++i) { funApply(); }

View file

@ -1649,6 +1649,12 @@ IonBuilder::snoopControlFlow(JSOp op)
// while (cond) { }
return whileOrForInLoop(sn);
case SRC_OPTCHAIN:
// XXX Instead of aborting early, breaking at this point works.
// However, I'm not sure if we still need to further process
// optional chains under IonBuilder.
break;
default:
// Hard assert for now - make an error later.
MOZ_CRASH("unknown goto case");

View file

@ -354,6 +354,8 @@ MSG_DEF(JSMSG_BAD_COLUMN_NUMBER, 0, JSEXN_RANGEERR, "column number out of
MSG_DEF(JSMSG_COMPUTED_NAME_IN_PATTERN,0, JSEXN_SYNTAXERR, "computed property names aren't supported in this destructuring declaration")
MSG_DEF(JSMSG_DEFAULT_IN_PATTERN, 0, JSEXN_SYNTAXERR, "destructuring defaults aren't supported in this destructuring declaration")
MSG_DEF(JSMSG_BAD_NEWTARGET, 0, JSEXN_SYNTAXERR, "new.target only allowed within functions")
MSG_DEF(JSMSG_BAD_NEW_OPTIONAL, 0, JSEXN_SYNTAXERR, "new keyword cannot be used with an optional chain")
MSG_DEF(JSMSG_BAD_OPTIONAL_TEMPLATE, 0, JSEXN_SYNTAXERR, "tagged template cannot be used with optional chain")
MSG_DEF(JSMSG_ESCAPED_KEYWORD, 0, JSEXN_SYNTAXERR, "keywords must be written literally, without embedded escapes")
// asm.js

View file

@ -27,10 +27,14 @@ ASTDEF(AST_LOGICAL_EXPR, "LogicalExpression", "logicalExpr
ASTDEF(AST_UPDATE_EXPR, "UpdateExpression", "updateExpression")
ASTDEF(AST_NEW_EXPR, "NewExpression", "newExpression")
ASTDEF(AST_CALL_EXPR, "CallExpression", "callExpression")
ASTDEF(AST_OPT_CALL_EXPR, "OptionalCallExpression", "optionalCallExpression")
ASTDEF(AST_MEMBER_EXPR, "MemberExpression", "memberExpression")
ASTDEF(AST_OPT_MEMBER_EXPR, "OptionalMemberExpression", "optionalMemberExpression")
ASTDEF(AST_FUNC_EXPR, "FunctionExpression", "functionExpression")
ASTDEF(AST_ARROW_EXPR, "ArrowFunctionExpression", "arrowFunctionExpression")
ASTDEF(AST_ARRAY_EXPR, "ArrayExpression", "arrayExpression")
ASTDEF(AST_DELETE_OPTIONAL_EXPR, "DeleteOptionalExpression", "deleteOptionalExpression")
ASTDEF(AST_OPTIONAL_EXPR, "OptionalExpression", "optionalExpression")
ASTDEF(AST_SPREAD_EXPR, "SpreadExpression", "spreadExpression")
ASTDEF(AST_OBJECT_EXPR, "ObjectExpression", "objectExpression")
ASTDEF(AST_THIS_EXPR, "ThisExpression", "thisExpression")

View file

@ -699,6 +699,30 @@ IsEqualityOp(JSOp op)
return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
}
inline bool
IsSpreadOp(JSOp op)
{
return JOF_OPTYPE(op) == JOF_BYTE;
}
inline bool
IsNewOp(JSOp op)
{
return op == JSOP_NEW ||
op == JSOP_SPREADNEW ||
op == JSOP_SUPERCALL ||
op == JSOP_SPREADSUPERCALL;
}
inline bool
IsEvalOp(JSOp op)
{
return op == JSOP_EVAL ||
op == JSOP_STRICTEVAL ||
op == JSOP_SPREADEVAL ||
op == JSOP_STRICTSPREADEVAL;
}
inline bool
IsCheckStrictOp(JSOp op)
{

View file

@ -0,0 +1,10 @@
// Optional expression can be part of a class heritage expression.
var a = {b: null};
class C extends a?.b {}
assertEq(Object.getPrototypeOf(C.prototype), null);
if (typeof reportCompare === "function")
reportCompare(true, true);

View file

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

View file

@ -0,0 +1,12 @@
// Don't assert.
var obj = {
m() {
super[0]?.a
}
};
obj.m();
if (typeof reportCompare === "function")
reportCompare(true, true);

View file

@ -0,0 +1,28 @@
// Test TDZ for optional chaining.
// TDZ for lexical |let| bindings with optional chaining.
{
assertThrowsInstanceOf(() => {
const Null = null;
Null?.[b];
b = 0;
let b;
}, ReferenceError);
assertThrowsInstanceOf(() => {
const Null = null;
Null?.[b]();
b = 0;
let b;
}, ReferenceError);
assertThrowsInstanceOf(() => {
const Null = null;
delete Null?.[b];
b = 0;
let b;
}, ReferenceError);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View file

@ -0,0 +1,223 @@
var BUGNUMBER = 1566143;
var summary = "Implement the Optional Chain operator (?.) proposal";
print(BUGNUMBER + ": " + summary);
// These tests are originally from webkit.
// webkit specifics have been removed and error messages have been updated.
function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}
function shouldThrowSyntaxError(script) {
let error;
try {
eval(script);
} catch (e) {
error = e;
}
if (!(error instanceof SyntaxError))
throw new Error('Expected SyntaxError!');
}
function shouldNotThrowSyntaxError(script) {
let error;
try {
eval(script);
} catch (e) {
error = e;
}
if ((error instanceof SyntaxError))
throw new Error('Unxpected SyntaxError!');
}
function shouldThrowTypeError(func, messagePrefix) {
let error;
try {
func();
} catch (e) {
error = e;
}
if (!(error instanceof TypeError))
throw new Error('Expected TypeError!');
}
function testBasicSuccessCases() {
shouldBe(undefined?.valueOf(), undefined);
shouldBe(null?.valueOf(), undefined);
shouldBe(true?.valueOf(), true);
shouldBe(false?.valueOf(), false);
shouldBe(0?.valueOf(), 0);
shouldBe(1?.valueOf(), 1);
shouldBe(''?.valueOf(), '');
shouldBe('hi'?.valueOf(), 'hi');
shouldBe(({})?.constructor, Object);
shouldBe(({ x: 'hi' })?.x, 'hi');
shouldBe([]?.length, 0);
shouldBe(['hi']?.length, 1);
shouldBe(undefined?.['valueOf'](), undefined);
shouldBe(null?.['valueOf'](), undefined);
shouldBe(true?.['valueOf'](), true);
shouldBe(false?.['valueOf'](), false);
shouldBe(0?.['valueOf'](), 0);
shouldBe(1?.['valueOf'](), 1);
shouldBe(''?.['valueOf'](), '');
shouldBe('hi'?.['valueOf'](), 'hi');
shouldBe(({})?.['constructor'], Object);
shouldBe(({ x: 'hi' })?.['x'], 'hi');
shouldBe([]?.['length'], 0);
shouldBe(['hi']?.[0], 'hi');
shouldBe(undefined?.(), undefined);
shouldBe(null?.(), undefined);
shouldBe((() => 3)?.(), 3);
}
function testBasicFailureCases() {
shouldThrowTypeError(() => true?.(), 'true is not a function');
shouldThrowTypeError(() => false?.(), 'false is not a function');
shouldThrowTypeError(() => 0?.(), '0 is not a function');
shouldThrowTypeError(() => 1?.(), '1 is not a function');
shouldThrowTypeError(() => ''?.(), '"" is not a function');
shouldThrowTypeError(() => 'hi'?.(), '"hi" is not a function');
shouldThrowTypeError(() => ({})?.(), '({}) is not a function');
shouldThrowTypeError(() => ({ x: 'hi' })?.(), '({x:"hi"}) is not a function');
shouldThrowTypeError(() => []?.(), '[] is not a function');
shouldThrowTypeError(() => ['hi']?.(), '[...] is not a function');
}
testBasicSuccessCases();
testBasicFailureCases();
shouldThrowTypeError(() => ({})?.i(), '(intermediate value).i is not a function');
shouldBe(({}).i?.(), undefined);
shouldBe(({})?.i?.(), undefined);
shouldThrowTypeError(() => ({})?.['i'](), '(intermediate value)["i"] is not a function');
shouldBe(({})['i']?.(), undefined);
shouldBe(({})?.['i']?.(), undefined);
shouldThrowTypeError(() => ({})?.a['b'], 'can\'t access property "b", (intermediate value).a is undefined');
shouldBe(({})?.a?.['b'], undefined);
shouldBe(null?.a['b']().c, undefined);
shouldThrowTypeError(() => ({})?.['a'].b, 'can\'t access property "b", (intermediate value)["a"] is undefined');
shouldBe(({})?.['a']?.b, undefined);
shouldBe(null?.['a'].b()['c'], undefined);
shouldBe(null?.()().a['b'], undefined);
const o0 = { a: { b() { return this._b.bind(this); }, _b() { return this.__b; }, __b: { c: 42 } } };
shouldBe(o0?.a?.['b']?.()?.()?.c, 42);
shouldBe(o0?.i?.['j']?.()?.()?.k, undefined);
shouldBe((o0.a?._b)?.().c, 42);
shouldBe((o0.a?._b)().c, 42);
shouldBe((o0.a?.b?.())?.().c, 42);
shouldBe((o0.a?.['b']?.())?.().c, 42);
shouldBe(({ undefined: 3 })?.[null?.a], 3);
shouldBe((() => 3)?.(null?.a), 3);
const o1 = { count: 0, get x() { this.count++; return () => {}; } };
o1.x?.y;
shouldBe(o1.count, 1);
o1.x?.['y'];
shouldBe(o1.count, 2);
o1.x?.();
shouldBe(o1.count, 3);
null?.(o1.x);
shouldBe(o1.count, 3);
shouldBe(delete undefined?.foo, true);
shouldBe(delete null?.foo, true);
shouldBe(delete undefined?.['foo'], true);
shouldBe(delete null?.['foo'], true);
shouldBe(delete undefined?.(), true);
shouldBe(delete null?.(), true);
shouldBe(delete ({}).a?.b?.b, true);
shouldBe(delete ({a : {b: undefined}}).a?.b?.b, true);
shouldBe(delete ({a : {b: undefined}}).a?.["b"]?.["b"], true);
const o2 = { x: 0, y: 0, z() {} };
shouldBe(delete o2?.x, true);
shouldBe(o2.x, undefined);
shouldBe(o2.y, 0);
shouldBe(delete o2?.x, true);
shouldBe(delete o2?.['y'], true);
shouldBe(o2.y, undefined);
shouldBe(delete o2?.['y'], true);
shouldBe(delete o2.z?.(), true);
function greet(name) { return `hey, ${name}${this.suffix ?? '.'}`; }
shouldBe(eval?.('greet("world")'), 'hey, world.');
shouldBe(greet?.call({ suffix: '!' }, 'world'), 'hey, world!');
shouldBe(greet.call?.({ suffix: '!' }, 'world'), 'hey, world!');
shouldBe(null?.call({ suffix: '!' }, 'world'), undefined);
shouldBe(({}).call?.({ suffix: '!' }, 'world'), undefined);
shouldBe(greet?.apply({ suffix: '?' }, ['world']), 'hey, world?');
shouldBe(greet.apply?.({ suffix: '?' }, ['world']), 'hey, world?');
shouldBe(null?.apply({ suffix: '?' }, ['world']), undefined);
shouldBe(({}).apply?.({ suffix: '?' }, ['world']), undefined);
shouldThrowSyntaxError('class C {} class D extends C { foo() { return super?.bar; } }');
shouldThrowSyntaxError('class C {} class D extends C { foo() { return super?.["bar"]; } }');
shouldThrowSyntaxError('class C {} class D extends C { constructor() { super?.(); } }');
shouldThrowSyntaxError('const o = { C: class {} }; new o?.C();')
shouldThrowSyntaxError('const o = { C: class {} }; new o?.["C"]();')
shouldThrowSyntaxError('class C {} new C?.();')
shouldThrowSyntaxError('function foo() { new?.target; }');
shouldThrowSyntaxError('function tag() {} tag?.``;');
shouldThrowSyntaxError('const o = { tag() {} }; o?.tag``;');
// NOT an optional chain
shouldBe(false?.4:5, 5);
function testSideEffectCountFunction() {
let count = 0;
let a = {
b: {
c: {
d: () => {
count++;
return a;
}
}
}
}
a.b.c.d?.()?.b?.c?.d
shouldBe(count, 1);
}
function testSideEffectCountGetters() {
let count = 0;
let a = {
get b() {
count++;
return { c: {} };
}
}
a.b?.c?.d;
shouldBe(count, 1);
a.b?.c?.d;
shouldBe(count, 2);
}
testSideEffectCountFunction();
testSideEffectCountGetters();
// stress test SM
shouldBe(({a : {b: undefined}}).a.b?.()()(), undefined);
shouldBe(({a : {b: undefined}}).a.b?.()?.()(), undefined);
shouldBe(({a : {b: () => undefined}}).a.b?.()?.(), undefined);
shouldThrowTypeError(() => delete ({a : {b: undefined}}).a?.b.b.c, 'can\'t access property "b", (intermediate value).a.b is undefined');
shouldBe(delete ({a : {b: undefined}}).a?.["b"]?.["b"], true);
shouldThrowTypeError(() => (({a : {b: () => undefined}}).a.b?.())(), 'undefined is not a function');
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View file

View file

View file

@ -0,0 +1,57 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-nested-memberexpr-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/assignment-expr.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName Initializer) (AssignmentExpression)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var x = {};
0, [x?.y = 42] = [23];

View file

@ -0,0 +1,60 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-put-obj-literal-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/assignment-expr.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (AssignmentExpression)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
0, [{
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y = 42] = [23];

View file

@ -0,0 +1,57 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-memberexpr-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/assignment-expr.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName Initializer) (AssignmentExpression)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var y = {};
0, { x: y?.z = 42 } = { x: 23 };

View file

@ -0,0 +1,60 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-obj-literal-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/assignment-expr.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (AssignmentExpression)
esid: sec-variable-statement-runtime-semantics-evaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
VariableDeclaration : BindingPattern Initializer
1. Let rhs be the result of evaluating Initializer.
2. Let rval be GetValue(rhs).
3. ReturnIfAbrupt(rval).
4. Return the result of performing BindingInitialization for
BindingPattern passing rval and undefined as arguments.
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
0, { x: {
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y = 42} = {x: 42};

View file

@ -0,0 +1,16 @@
// GENERATED, DO NOT EDIT
// file: tcoHelper.js
// Copyright (C) 2016 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: |
This defines the number of consecutive recursive function calls that must be
made in order to prove that stack frames are properly destroyed according to
ES2015 tail call optimization semantics.
defines: [$MAX_ITERATIONS]
---*/
var $MAX_ITERATIONS = 100000;

View file

@ -0,0 +1,24 @@
// |reftest| error:SyntaxError
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
should not suppress error if super called on class with no base
info: |
Left-Hand-Side Expressions
OptionalExpression:
SuperCall OptionalChain
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
class C {
constructor () {
super()?.a;
}
}

View file

@ -0,0 +1,77 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on call expression
info: |
Left-Hand-Side Expressions
OptionalExpression:
CallExpression OptionalChain
features: [optional-chaining]
---*/
// CallExpression CoverCallExpressionAndAsyncArrowHead
function fn () {
return {a: 33};
};
const obj = {
fn () {
return 44;
}
}
assert.sameValue(33, fn()?.a);
assert.sameValue(undefined, fn()?.b);
assert.sameValue(44, obj?.fn());
// CallExpression SuperCall
class A {}
class B extends A {
constructor () {
assert.sameValue(undefined, super()?.a);
}
}
new B();
// CallExpression Arguments
function fn2 () {
return () => {
return {a: 66};
};
}
function fn3 () {
return () => {
return null;
};
}
assert.sameValue(66, fn2()()?.a);
assert.sameValue(undefined, fn3()()?.a);
// CallExpression [Expression]
function fn4 () {
return [{a: 77}];
}
function fn5 () {
return [];
}
assert.sameValue(77, fn4()[0]?.a);
assert.sameValue(undefined, fn5()[0]?.a);
// CallExpression .IdentifierName
function fn6 () {
return {
a: {
b: 88
}
};
}
assert.sameValue(88, fn6().a?.b);
assert.sameValue(undefined, fn6().b?.c);
// CallExpression TemplateLiteral
function fn7 () {
return () => {};
}
assert.sameValue(undefined, fn7()`hello`?.a);
reportCompare(0, 0);

View file

@ -0,0 +1,26 @@
// |reftest| error:SyntaxError
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
// This production exists in order to prevent automatic semicolon
// insertion rules.
null?.
`hello`

View file

@ -0,0 +1,23 @@
// |reftest| error:SyntaxError
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
null?.`hello`;

View file

@ -0,0 +1,26 @@
// |reftest| error:SyntaxError
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
// This production exists in order to prevent automatic semicolon
// insertion rules.
null?.fn
`hello`

View file

@ -0,0 +1,23 @@
// |reftest| error:SyntaxError
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
null?.fn`hello`;

View file

@ -0,0 +1,28 @@
// |reftest| error:SyntaxError
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
const a = function() {};
// This production exists in order to prevent automatic semicolon
// insertion rules.
a?.
`hello`

View file

@ -0,0 +1,25 @@
// |reftest| error:SyntaxError
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
const a = function() {};
a?.`hello`;

View file

@ -0,0 +1,28 @@
// |reftest| error:SyntaxError
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
const a = {fn() {}};
// This production exists in order to prevent automatic semicolon
// insertion rules.
a?.fn
`hello`

View file

@ -0,0 +1,25 @@
// |reftest| error:SyntaxError
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
template string passed to tail position of optional chain
info: |
Static Semantics: Early Errors
OptionalChain:
?.TemplateLiteral
OptionalChain TemplateLiteral
It is a Syntax Error if any code matches this production.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
const a = {fn() {}};
a?.fn`hello`;

View file

@ -0,0 +1,41 @@
// Copyright 2020 Toru Nagashima. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-optional-chaining-chain-evaluation
description: optional call invoked on eval function should be indirect eval.
info: |
Runtime Semantics: ChainEvaluation
OptionalChain: ?. Arguments
1. Let thisChain be this OptionalChain.
2. Let tailCall be IsInTailPosition(thisChain).
3. Return ? EvaluateCall(baseValue, baseReference, Arguments, tailCall).
Runtime Semantics: EvaluateCall ( func, ref, arguments, tailPosition )
...
7. Let result be Call(func, thisValue, argList).
...
eval ( x )
...
4. Return ? PerformEval(x, callerRealm, false, false).
Runtime Semantics: PerformEval ( x, callerRealm, strictCaller, direct )
features: [optional-chaining]
---*/
const a = 'global';
function fn() {
const a = 'local';
return eval?.('a');
}
assert.sameValue(fn(), 'global', 'fn() returns "global" value from indirect eval');
const b = (a => eval?.('a'))('local');
assert.sameValue(b, 'global', 'b is "global", from indirect eval not observing parameter');
reportCompare(0, 0);

View file

@ -0,0 +1,20 @@
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain in test portion of do while statement
info: |
IterationStatement
do Statement while (OptionalExpression)
features: [optional-chaining]
---*/
let count = 0;
const obj = {a: true};
do {
count++;
break;
} while (obj?.a);
assert.sameValue(1, count);
reportCompare(0, 0);

View file

@ -0,0 +1,36 @@
// |reftest| async
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain RHS of for await statement
info: |
IterationStatement
for await (LeftHandSideExpression of AssignmentExpression) Statement
features: [optional-chaining]
flags: [async]
---*/
const obj = {
iterable: {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
}
};
async function checkAssertions() {
let count = 0;
for await (const num of obj?.iterable) {
count += num;
}
assert.sameValue(3, count);
}
checkAssertions().then($DONE, $DONE);

View file

@ -0,0 +1,24 @@
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain in test portion of do while statement
info: |
IterationStatement
for (LeftHandSideExpression in Expression) Statement
features: [optional-chaining]
---*/
const obj = {
inner: {
a: 1,
b: 2
}
};
let str = '';
for (const key in obj?.inner) {
str += key;
}
assert.sameValue('ab', str);
reportCompare(0, 0);

View file

@ -0,0 +1,30 @@
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain returning undefined in RHS of for of statement
info: |
IterationStatement
for (LeftHandSideExpression of Expression) Statement
features: [optional-chaining]
---*/
assert.throws(TypeError, function() {
for (const key of {}?.a) ;
});
assert.throws(TypeError, function() {
for (const key of {}?.a) {}
});
const obj = undefined;
assert.throws(TypeError, function() {
for (const key of obj?.a) {}
});
assert.throws(TypeError, function() {
for (const key of obj?.a);
});
reportCompare(0, 0);

View file

@ -0,0 +1,45 @@
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain in init/test/update of for statement
info: |
IterationStatement
for (Expression; Expression; Expression) Statement
features: [optional-chaining]
---*/
// OptionalExpression in test.
let count;
const obj = {a: true};
for (count = 0; obj?.a; count++) {
if (count > 0) break;
}
assert.sameValue(count, 1);
// OptionalExpression in init/test/update.
let count2 = 0;
const obj2 = undefined;
for (obj?.a; obj2?.a; obj?.a) { count2++; }
assert.sameValue(count2, 0);
for (obj?.a; undefined?.a; obj?.a) { count2++; }
assert.sameValue(count2, 0);
// Short-circuiting
let touched = 0;
const obj3 = {
get a() {
count++;
return undefined; // explicit for clarity
}
};
for (count = 0; true; obj3?.a?.[touched++]) {
if (count > 0) { break; }
}
assert.sameValue(count, 1);
assert.sameValue(touched, 0);
reportCompare(0, 0);

View file

@ -0,0 +1,20 @@
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain in test portion of while statement
info: |
IterationStatement
while (Expression) Statement
features: [optional-chaining]
---*/
let count = 0;
const obj = {a: true};
while (obj?.a) {
count++;
break;
}
assert.sameValue(1, count);
reportCompare(0, 0);

View file

@ -0,0 +1,33 @@
// |reftest| async
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on member expression in async context
info: |
Left-Hand-Side Expressions
OptionalExpression
MemberExpression [PrimaryExpression identifier] OptionalChain
features: [optional-chaining]
flags: [async]
---*/
const a = undefined;
const c = {d: Promise.resolve(11)};
async function checkAssertions() {
assert.sameValue(await a?.b, undefined);
assert.sameValue(await c?.d, 11);
Promise.prototype.x = 42;
var res = await Promise.resolve(undefined)?.x;
assert.sameValue(res, 42, 'await unwraps the evaluation of the whole optional chaining expression #1');
Promise.prototype.y = 43;
var res = await Promise.reject(undefined)?.y;
assert.sameValue(res, 43, 'await unwraps the evaluation of the whole optional chaining expression #2');
c.e = Promise.resolve(39);
assert.sameValue(await c?.e, 39, 'await unwraps the promise given after the evaluation of the OCE');
}
checkAssertions().then($DONE, $DONE);

View file

@ -0,0 +1,20 @@
// |reftest| async
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on member expression in async context
info: |
Left-Hand-Side Expressions
OptionalExpression:
MemberExpression [PrimaryExpression literal] OptionalChain
features: [optional-chaining]
flags: [async]
---*/
async function checkAssertions() {
assert.sameValue(await "hello"?.[0], 'h');
assert.sameValue(await null?.a, undefined);
}
checkAssertions().then($DONE, $DONE);

View file

@ -0,0 +1,21 @@
// |reftest| async
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on member expression in async context
info: |
Left-Hand-Side Expressions
OptionalExpression:
MemberExpression [PrimaryExpression this] OptionalChain
features: [optional-chaining]
flags: [async]
---*/
async function thisFn() {
return await this?.a
}
thisFn.call({a: Promise.resolve(33)}).then(function(arg) {
assert.sameValue(33, arg);
}).then($DONE, $DONE);

View file

@ -0,0 +1,106 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on member expression
info: |
Left-Hand-Side Expressions
OptionalExpression:
MemberExpression OptionalChain
features: [optional-chaining]
---*/
// PrimaryExpression
// IdentifierReference
const a = {b: 22};
assert.sameValue(22, a?.b);
// this
function fn () {
return this?.a
}
assert.sameValue(33, fn.call({a: 33}));
// Literal
assert.sameValue(undefined, "hello"?.a);
assert.sameValue(undefined, null?.a);
// ArrayLiteral
assert.sameValue(2, [1, 2]?.[1]);
// ObjectLiteral
assert.sameValue(44, {a: 44}?.a);
// FunctionExpression
assert.sameValue('a', (function a () {}?.name));
// ClassExpression
assert.sameValue('Foo', (class Foo {}?.name));
// GeneratorFunction
assert.sameValue('a', (function * a () {}?.name));
// AsyncFunctionExpression
assert.sameValue('a', (async function a () {}?.name));
// AsyncGeneratorExpression
assert.sameValue('a', (async function * a () {}?.name));
// RegularExpressionLiteral
assert.sameValue(true, /[a-z]/?.test('a'));
// TemplateLiteral
assert.sameValue('h', `hello`?.[0]);
// CoverParenthesizedExpressionAndArrowParameterList
assert.sameValue(undefined, ({a: 33}, null)?.a);
assert.sameValue(33, (undefined, {a: 33})?.a);
// MemberExpression [ Expression ]
const arr = [{a: 33}];
assert.sameValue(33, arr[0]?.a);
assert.sameValue(undefined, arr[1]?.a);
// MemberExpression .IdentifierName
const obj = {a: {b: 44}};
assert.sameValue(44, obj.a?.b);
assert.sameValue(undefined, obj.c?.b);
// MemberExpression TemplateLiteral
function f2 () {
return {a: 33};
}
function f3 () {}
assert.sameValue(33, f2`hello world`?.a);
assert.sameValue(undefined, f3`hello world`?.a);
// MemberExpression SuperProperty
class A {
a () {}
undf () {
return super.a?.c;
}
}
class B extends A {
dot () {
return super.a?.name;
}
expr () {
return super['a']?.name;
}
undf2 () {
return super.b?.c;
}
}
const subcls = new B();
assert.sameValue('a', subcls.dot());
assert.sameValue('a', subcls.expr());
assert.sameValue(undefined, subcls.undf2());
assert.sameValue(undefined, (new A()).undf());
// MemberExpression MetaProperty
class C {
constructor () {
assert.sameValue(undefined, new.target?.a);
}
}
new C();
// new MemberExpression Arguments
class D {
constructor (val) {
this.a = val;
}
}
assert.sameValue(99, new D(99)?.a);
reportCompare(0, 0);

View file

@ -0,0 +1,32 @@
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional call invoked on new.target should be equivalent to call
info: |
OptionalExpression
MemberExpression OptionalChain
NewTarget OptionalChain
features: [optional-chaining]
---*/
const newTargetContext = (function() { return this; })();
let called = false;
// should be set to 'undefined' or global context, depending on whether
// mode is strict or sloppy.
let context = null;
function Base() {
called = true;
context = this;
}
function Foo(blerg) {
new.target?.();
}
Reflect.construct(Foo, [], Base);
assert(context === newTargetContext);
assert.sameValue(called, true);
reportCompare(0, 0);

View file

@ -0,0 +1,29 @@
// Copyright (C) 2019 Sony Interactive Entertainment Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-optional-chaining-chain-evaluation
description: >
optional call must preserve this context, as with a non-optional call
info: |
OptionalChain : ?. Arguments
1. Let thisChain be this OptionalChain.
2. Let tailCall be IsInTailPosition(thisChain).
3. Return ? EvaluateCall(baseValue, baseReference, Arguments, tailCall).
features: [optional-chaining]
---*/
const a = {
b() { return this._b; },
_b: { c: 42 }
};
assert.sameValue(a?.b().c, 42);
assert.sameValue((a?.b)().c, 42);
assert.sameValue(a.b?.().c, 42);
assert.sameValue((a.b)?.().c, 42);
assert.sameValue(a?.b?.().c, 42);
assert.sameValue((a?.b)?.().c, 42);
reportCompare(0, 0);

View file

@ -0,0 +1,29 @@
// |reftest| async
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain expansions in an async context
info: |
Left-Hand-Side Expressions
OptionalExpression
MemberExpression [PrimaryExpression Identifier] OptionalChain
OptionalChain OptionalChain ?.[Expression]
features: [optional-chaining]
flags: [async]
---*/
async function checkAssertions() {
assert.sameValue(await {a: [11]}?.a[0], 11);
const b = {c: [22, 33]};
assert.sameValue(b?.c[await Promise.resolve(1)], 33);
function e(val) {
return val;
}
assert.sameValue({d: e}?.d(await Promise.resolve([44, 55]))[1], 55);
assert.sameValue(undefined?.arr[
await Promise.reject(new Error('unreachable'))
], undefined);
}
checkAssertions().then($DONE, $DONE);

View file

@ -0,0 +1,25 @@
// |reftest| async
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain expansions in an async context
info: |
Left-Hand-Side Expressions
OptionalExpression
MemberExpression [PrimaryExpression Identifier] OptionalChain
OptionalChain ?.[Expression]
features: [optional-chaining]
flags: [async]
---*/
async function checkAssertions() {
assert.sameValue(await [11]?.[0], 11);
assert.sameValue([22, 33]?.[await Promise.resolve(1)], 33);
assert.sameValue([44, await Promise.resolve(55)]?.[1], 55);
assert.sameValue(undefined?.[
await Promise.reject(new Error('unreachable'))
], undefined);
}
checkAssertions().then($DONE, $DONE);

View file

@ -0,0 +1,22 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain bracket notation containing optional expresion
info: |
OptionalChain:
?. [OptionalExpression]
features: [optional-chaining]
---*/
const a = undefined;
const b = {e: 0};
const c = {};
c[undefined] = 11;
const d = [22];
assert.sameValue(undefined, a?.[a?.b]);
assert.sameValue(11, c?.[a?.b]);
assert.sameValue(22, d?.[b?.e]);
reportCompare(0, 0);

View file

@ -0,0 +1,21 @@
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
Productions for ?. Arguments
info: |
OptionalChain[Yield, Await]:
?. Arguments
features: [optional-chaining]
---*/
function fn(arg1, arg2, arg3 = 0) {
return arg1 + arg2 + arg3;
}
assert.sameValue(fn?.(10, 20), 30, 'regular');
assert.sameValue(String?.(42), '42', 'built-in');
assert.sameValue(fn ?. (...[10, 20, 40]), 70, 'spread');
reportCompare(0, 0);

View file

@ -0,0 +1,44 @@
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
Productions for ?. [Expression]
info: |
OptionalChain:
?.[ Expression ]
features: [optional-chaining]
---*/
const $ = 'x';
const arr = [39, 42];
arr.true = 'prop';
arr[1.1] = 'other prop';
const obj = {
a: 'hello',
undefined: 40,
$: 0,
NaN: 41,
null: 42,
x: 43,
true: 44
};
assert.sameValue(arr?.[0], 39, '[0]');
assert.sameValue(arr?.[0, 1], 42, '[0, 1]');
assert.sameValue(arr?.[1], 42, '[1]');
assert.sameValue(arr?.[1, 0], 39, '[1, 0]');
assert.sameValue(arr?.[{}, NaN, undefined, 2, 0, 10 / 10], 42, '[{}, NaN, undefined, 2, 0, 10 / 10]');
assert.sameValue(arr?.[true], 'prop', '[true]');
assert.sameValue(arr?.[1.1], 'other prop', '[1.1]');
assert.sameValue(obj?.[undefined], 40, '[undefined]');
assert.sameValue(obj?.[NaN], 41, '[NaN]');
assert.sameValue(obj?.[null], 42, '[null]');
assert.sameValue(obj?.['$'], 0, '["$"]');
assert.sameValue(obj?.[$], 43, '[$]');
assert.sameValue(obj?.[true], 44, '[true]');
reportCompare(0, 0);

View file

@ -0,0 +1,40 @@
// Copyright 2020 Salesforce.com, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
Productions for ?. IdentifierName
info: |
OptionalChain[Yield, Await]:
?. IdentifierName
features: [optional-chaining]
---*/
const arr = [10, 11];
const obj = {
a: 'hello'
};
assert.sameValue(obj?.a, 'hello');
assert.sameValue(obj?.\u0061, 'hello');
assert.sameValue(obj?.\u{0061}, 'hello');
assert.sameValue(obj?.\u0062, undefined);
assert.sameValue(obj?.\u{0062}, undefined);
assert.sameValue(arr ?. length, 2);
assert.sameValue(arr ?. l\u0065ngth, 2);
assert.sameValue(arr ?. l\u{0065}ngth, 2);
assert.sameValue(obj?.$, undefined);
obj.$ = 42;
assert.sameValue(obj?.$, 42);
assert.sameValue(obj?._, undefined);
obj._ = 39;
assert.sameValue(obj?._, 39);
reportCompare(0, 0);

View file

@ -0,0 +1,52 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
various optional chain expansions
info: |
OptionalChain[Yield, Await]:
?.[Expression]
?.IdentifierName
?.Arguments
?.TemplateLiteral
OptionalChain [Expression]
OptionalChain .IdentifierName
OptionalChain Arguments[?Yield, ?Await]
OptionalChain TemplateLiteral
features: [optional-chaining]
---*/
const arr = [10, 11];
const obj = {
a: 'hello',
b: {val: 13},
c(arg1) {
return arg1 * 2;
},
arr: [11, 12]
};
const i = 0;
// OptionalChain: ?.[Expression]
assert.sameValue(11, arr?.[i + 1]);
// OptionalChain: ?.IdentifierName
assert.sameValue('hello', obj?.a);
// OptionalChain: ?.Arguments
const fn = (arg1, arg2) => {
return arg1 + arg2;
}
assert.sameValue(30, fn?.(10, 20));
// OptionalChain: OptionalChain [Expression]
assert.sameValue(12, obj?.arr[i + 1]);
// OptionalChain: OptionalChain .IdentifierName
assert.sameValue(13, obj?.b.val);
// OptionalChain: OptionalChain Arguments
assert.sameValue(20, obj?.c(10));
reportCompare(0, 0);

View file

@ -0,0 +1,29 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chain on recursive optional expression
info: |
Left-Hand-Side Expressions
OptionalExpression:
OptionalExpression OptionalChain
features: [optional-chaining]
---*/
const obj = {
a: {
b: 22
}
};
function fn () {
return {};
}
// OptionalExpression (MemberExpression OptionalChain) OptionalChain
assert.sameValue(22, obj?.a?.b);
// OptionalExpression (CallExpression OptionalChain) OptionalChain
assert.sameValue(undefined, fn()?.a?.b);
reportCompare(0, 0);

View file

@ -0,0 +1,17 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
ternary operation with decimal does not evaluate as optional chain
info: |
Punctuators
OptionalChainingPunctuator::
?.[lookahead DecimalDigit]
features: [optional-chaining]
---*/
const value = true ?.30 : false;
assert.sameValue(.30, value);
reportCompare(0, 0);

View file

@ -0,0 +1,20 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
accessing optional value on undefined or null returns undefined.
info: |
If baseValue is undefined or null, then
Return undefined.
features: [optional-chaining]
---*/
const nul = null;
const undf = undefined;
assert.sameValue(undefined, nul?.a);
assert.sameValue(undefined, undf?.b);
assert.sameValue(undefined, null?.a);
assert.sameValue(undefined, undefined?.b);
reportCompare(0, 0);

View file

@ -0,0 +1,24 @@
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
demonstrate syntax-based short-circuiting.
info: |
If the expression on the LHS of ?. evaluates to null/undefined, the RHS is
not evaluated
features: [optional-chaining]
---*/
const a = undefined;
let x = 1;
a?.[++x] // short-circuiting.
a?.b.c(++x).d; // long short-circuiting.
undefined?.[++x] // short-circuiting.
undefined?.b.c(++x).d; // long short-circuiting.
assert.sameValue(1, x);
reportCompare(0, 0);

View file

@ -0,0 +1,24 @@
// |reftest| error:SyntaxError
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
an optional expression cannot be target of assignment
info: |
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression:
OptionalExpression
Return false.
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
const obj = {};
obj?.a = 33;

View file

@ -0,0 +1,32 @@
// Copyright 2019 Google, LLC. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional call invoked on super method should be equivalent to call
info: |
OptionalExpression
MemberExpression OptionalChain
SuperProperty OptionalChain
features: [optional-chaining]
---*/
let called = false;
let context;
class Base {
method() {
called = true;
context = this;
}
}
class Foo extends Base {
method() {
super.method?.();
}
}
const foo = new Foo();
foo.method();
assert(foo === context);
assert.sameValue(called, true);
reportCompare(0, 0);

View file

@ -0,0 +1,24 @@
// |reftest| error:SyntaxError
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chaining is forbidden in write contexts
info: |
UpdateExpression[Yield, Await]:
LeftHandSideExpression++
LeftHandSideExpression--
++UnaryExpression
--UnaryExpression
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
// LeftHandSideExpression ++
const a = {};
a?.b++;

View file

@ -0,0 +1,24 @@
// |reftest| error:SyntaxError
// Copyright 2019 Google, Inc. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: prod-OptionalExpression
description: >
optional chaining is forbidden in write contexts
info: |
UpdateExpression[Yield, Await]:
LeftHandSideExpression++
LeftHandSideExpression--
++UnaryExpression
--UnaryExpression
features: [optional-chaining]
negative:
phase: parse
type: SyntaxError
---*/
$DONOTEVALUATE();
// --UnaryExpression
const a = {};
--a?.b;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-nested-memberexpr-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName Initializer) (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var x = {};
for ([x?.y = 42] in [[23]]) ;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-nested-memberexpr-optchain-prop-ref.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName) (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var x = {};
for ([x?.y] in [[23]]) ;

View file

@ -0,0 +1,69 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-put-obj-literal-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
for ([{
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y = 42] in [[23]]) ;

View file

@ -0,0 +1,69 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-put-obj-literal-optchain-prop-ref.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
for ([{
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y] in [[23]]) ;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-memberexpr-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName Initializer) (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var y = {};
for ({ x: y?.z = 42 } in [{ x: 23 }]) ;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-memberexpr-optchain-prop-ref.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName) (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var y = {};
for ({ x: y?.z } in [{ x: 23 }]) ;

View file

@ -0,0 +1,69 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-obj-literal-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
for ({ x: {
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y = 42} in [{x: 42}]) ;

View file

@ -0,0 +1,69 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-obj-literal-optchain-prop-ref.case
// - src/dstr-assignment/syntax/for-in.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (For..in statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
for ({ x: {
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y} in [{x: 42}]) ;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-nested-memberexpr-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-of.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName Initializer) (For..of statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var x = {};
for ([x?.y = 42] of [[23]]) ;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-nested-memberexpr-optchain-prop-ref.case
// - src/dstr-assignment/syntax/for-of.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName) (For..of statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var x = {};
for ([x?.y] of [[23]]) ;

View file

@ -0,0 +1,69 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-put-obj-literal-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-of.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (For..of statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
for ([{
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y = 42] of [[23]]) ;

View file

@ -0,0 +1,69 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/array-elem-put-obj-literal-optchain-prop-ref.case
// - src/dstr-assignment/syntax/for-of.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (For..of statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
for ([{
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y] of [[23]]) ;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-memberexpr-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-of.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName Initializer) (For..of statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var y = {};
for ({ x: y?.z = 42 } of [{ x: 23 }]) ;

View file

@ -0,0 +1,66 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-memberexpr-optchain-prop-ref.case
// - src/dstr-assignment/syntax/for-of.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (MemberExpression OptionalChain .IdentifierName) (For..of statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
var y = {};
for ({ x: y?.z } of [{ x: 23 }]) ;

View file

@ -0,0 +1,69 @@
// |reftest| error:SyntaxError
// This file was procedurally generated from the following sources:
// - src/dstr-assignment/obj-prop-elem-target-obj-literal-optchain-prop-ref-init.case
// - src/dstr-assignment/syntax/for-of.template
/*---
description: It is a Syntax Error if LeftHandSideExpression of an DestructuringAssignmentTarget is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType(LeftHandSideExpression) is not simple Using Object (For..of statement)
esid: sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation
features: [optional-chaining, destructuring-binding]
flags: [generated]
negative:
phase: parse
type: SyntaxError
info: |
IterationStatement :
for ( LeftHandSideExpression of AssignmentExpression ) Statement
1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(« »,
AssignmentExpression, iterate).
2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement,
keyResult, assignment, labelSet).
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation
[...]
4. If destructuring is true and if lhsKind is assignment, then
a. Assert: lhs is a LeftHandSideExpression.
b. Let assignmentPattern be the parse of the source text corresponding to
lhs using AssignmentPattern as the goal symbol.
[...]
Syntax
AssignmentElement : DestructuringAssignmentTarget Initializer_opt
DestructuringAssignmentTarget : LeftHandSideExpression
Static Semantics: Early Errors
OptionalExpression:
MemberExpression OptionalChain
CallExpression OptionalChain
OptionalExpression OptionalChain
OptionalChain:
?. [ Expression ]
?. IdentifierName
?. Arguments
?. TemplateLiteral
OptionalChain [ Expression ]
OptionalChain .IdentifierName
OptionalChain Arguments
OptionalChain TemplateLiteral
DestructuringAssignmentTarget : LeftHandSideExpression
- It is a Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and IsValidSimpleAssignmentTarget(LeftHandSideExpression) is not true.
Static Semantics: IsValidSimpleAssignmentTarget
LeftHandSideExpression : OptionalExpression
1. Return false.
---*/
$DONOTEVALUATE();
for ({ x: {
set y(val) {
throw new Test262Error('The property should not be accessed.');
}
}?.y = 42} of [{x: 42}]) ;

Some files were not shown because too many files have changed in this diff Show more