diff --git a/spec.html b/spec.html
index 267b59994d..8a2844ed24 100644
--- a/spec.html
+++ b/spec.html
@@ -10973,7 +10973,11 @@
Syntax
Punctuators
Syntax
- Punctuator :: one of
+ OptionalChainingPunctuator ::
+ `?.` [lookahead <! DecimalDigit]
+ OtherPunctuator
+
+ OtherPunctuator :: one of
`{` `(` `)` `[` `]`
`.` `...` `;` `,`
`<` `>` `<=` `>=`
@@ -10988,6 +10992,10 @@ Syntax
`=` `+=` `-=` `*=` `%=` `**=` `<<=` `>>=` `>>>=` `&=` `|=` `^=`
`=>`
+ Punctuator ::
+ OptionalChainingPunctuator
+ OtherPunctuator
+
DivPunctuator ::
`/`
`/=`
@@ -13387,9 +13395,25 @@ Syntax
ArgumentList[?Yield, ?Await] `,` AssignmentExpression[+In, ?Yield, ?Await]
ArgumentList[?Yield, ?Await] `,` `...` AssignmentExpression[+In, ?Yield, ?Await]
+ OptionalExpression[Yield, Await] :
+ MemberExpression[?Yield, ?Await] OptionalChain[?Yield, ?Await]
+ CallExpression[?Yield, ?Await] OptionalChain[?Yield, ?Await]
+ OptionalExpression[?Yield, ?Await] OptionalChain[?Yield, ?Await]
+
+ OptionalChain[Yield, Await] :
+ `?.` Arguments[?Yield, ?Await]
+ `?.` `[` Expression[+In, ?Yield, ?Await] `]`
+ `?.` IdentifierName
+ `?.` TemplateLiteral[?Yield, ?Await, +Tagged]
+ OptionalChain[?Yield, ?Await] Arguments[?Yield, ?Await]
+ OptionalChain[?Yield, ?Await] `[` Expression[+In, ?Yield, ?Await] `]`
+ OptionalChain[?Yield, ?Await] `.` IdentifierName
+ OptionalChain[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged]
+
LeftHandSideExpression[Yield, Await] :
NewExpression[?Yield, ?Await]
CallExpression[?Yield, ?Await]
+ OptionalExpression[?Yield, ?Await]
Supplemental Syntax
When processing an instance of the production CallExpression : CoverCallExpressionAndAsyncArrowHead the interpretation of |CoverCallExpressionAndAsyncArrowHead| is refined using the following grammar:
@@ -13401,6 +13425,33 @@ Supplemental Syntax
Static Semantics
+
+ Static Semantics: Early Errors
+
+ OptionalChain[Yield, Await] :
+ `?.` TemplateLiteral[?Yield, ?Await, +Tagged]
+ OptionalChain[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged]
+
+
+ -
+ It is a Syntax Error if any code matches this production.
+
+
+
+ This production exists in order to prevent automatic semicolon insertion rules () from being applied to the following code:
+
+ a?.b
+ `c`
+
+ so that it would be interpreted as two valid statements. The purpose is to maintain consistency with similar code without optional chaining operator:
+
+ a.b
+ `c`
+
+ which is a valid statement and where automatic semicolon insertion does not apply.
+
+
+
Static Semantics: CoveredCallExpression
@@ -13436,6 +13487,19 @@ Static Semantics: Contains
1. If _symbol_ is an |Identifier| and StringValue of _symbol_ is the same value as the StringValue of |IdentifierName|, return *true*.
1. Return *false*.
+ OptionalChain : `?.` IdentifierName
+
+ 1. If _symbol_ is a |ReservedWord|, return *false*.
+ 1. If _symbol_ is an |Identifier| and StringValue of _symbol_ is the same value as the StringValue of |IdentifierName|, return *true*.
+ 1. Return *false*.
+
+ OptionalChain : OptionalChain `.` IdentifierName
+
+ 1. If |OptionalChain| Contains _symbol_ is *true*, return *true*.
+ 1. If _symbol_ is a |ReservedWord|, return *false*.
+ 1. If _symbol_ is an |Identifier| and StringValue of _symbol_ is the same value as the StringValue of |IdentifierName|, return *true*.
+ 1. Return *false*.
+
@@ -13455,6 +13519,7 @@ Static Semantics: IsFunctionDefinition
LeftHandSideExpression :
CallExpression
+ OptionalExpression
1. Return *false*.
@@ -13483,6 +13548,7 @@ Static Semantics: IsDestructuring
LeftHandSideExpression :
CallExpression
+ OptionalExpression
1. Return *false*.
@@ -13506,6 +13572,7 @@ Static Semantics: IsIdentifierRef
LeftHandSideExpression :
CallExpression
+ OptionalExpression
1. Return *false*.
@@ -13545,6 +13612,9 @@ Static Semantics: AssignmentTargetType
NewTarget :
`new` `.` `target`
+
+ LeftHandSideExpression :
+ OptionalExpression
1. Return ~invalid~.
@@ -13592,29 +13662,53 @@ Runtime Semantics: Evaluation
1. Let _baseReference_ be the result of evaluating |MemberExpression|.
1. Let _baseValue_ be ? GetValue(_baseReference_).
- 1. Let _propertyNameReference_ be the result of evaluating |Expression|.
- 1. Let _propertyNameValue_ be ? GetValue(_propertyNameReference_).
- 1. Let _bv_ be ? RequireObjectCoercible(_baseValue_).
- 1. Let _propertyKey_ be ? ToPropertyKey(_propertyNameValue_).
1. If the code matched by this |MemberExpression| is strict mode code, let _strict_ be *true*; else let _strict_ be *false*.
- 1. Return a value of type Reference whose base value component is _bv_, whose referenced name component is _propertyKey_, and whose strict reference flag is _strict_.
+ 1. Return ? EvaluatePropertyAccessWithExpressionKey(_baseValue_, |Expression|, _strict_).
MemberExpression : MemberExpression `.` IdentifierName
1. Let _baseReference_ be the result of evaluating |MemberExpression|.
1. Let _baseValue_ be ? GetValue(_baseReference_).
- 1. Let _bv_ be ? RequireObjectCoercible(_baseValue_).
- 1. Let _propertyNameString_ be StringValue of |IdentifierName|.
1. If the code matched by this |MemberExpression| is strict mode code, let _strict_ be *true*; else let _strict_ be *false*.
- 1. Return a value of type Reference whose base value component is _bv_, whose referenced name component is _propertyNameString_, and whose strict reference flag is _strict_.
+ 1. Return ? EvaluatePropertyAccessWithIdentifierKey(_baseValue_, |IdentifierName|, _strict_).
CallExpression : CallExpression `[` Expression `]`
- Is evaluated in exactly the same manner as MemberExpression : MemberExpression `[` Expression `]` except that the contained |CallExpression| is evaluated in step 1.
+
+ 1. Let _baseReference_ be the result of evaluating |CallExpression|.
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If the code matched by this |CallExpression| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluatePropertyAccessWithExpressionKey(_baseValue_, |Expression|, _strict_).
+
CallExpression : CallExpression `.` IdentifierName
- Is evaluated in exactly the same manner as MemberExpression : MemberExpression `.` IdentifierName except that the contained |CallExpression| is evaluated in step 1.
+
+ 1. Let _baseReference_ be the result of evaluating |CallExpression|.
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If the code matched by this |CallExpression| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluatePropertyAccessWithIdentifierKey(_baseValue_, |IdentifierName|, _strict_).
+
+
+ Runtime Semantics: EvaluatePropertyAccessWithExpressionKey( _baseValue_, _expression_, _strict_ )
+ The abstract operation EvaluatePropertyAccessWithExpressionKey takes as arguments a value _baseValue_, a Parse Node _expression_, and a Boolean argument _strict_. It performs the following steps:
+
+ 1. Let _propertyNameReference_ be the result of evaluating _expression_.
+ 1. Let _propertyNameValue_ be ? GetValue(_propertyNameReference_).
+ 1. Let _bv_ be ? RequireObjectCoercible(_baseValue_).
+ 1. Let _propertyKey_ be ? ToPropertyKey(_propertyNameValue_).
+ 1. Return a value of type Reference whose base value component is _bv_, whose referenced name component is _propertyKey_, and whose strict reference flag is _strict_.
+
+
+ Runtime Semantics: EvaluatePropertyAccessWithIdentifierKey( _baseValue_, _identifierName_, _strict_ )
+ The abstract operation EvaluatePropertyAccessWithIdentifierKey takes as arguments a value _baseValue_, a Parse Node _identifierName_, and a Boolean argument _strict_. It performs the following steps:
+
+ 1. Assert: _identifierName_ is an |IdentifierName|
+ 1. Let _bv_ be ? RequireObjectCoercible(_baseValue_).
+ 1. Let _propertyNameString_ be StringValue of _identifierName_.
+ 1. Return a value of type Reference whose base value component is _bv_, whose referenced name component is _propertyNameString_, and whose strict reference flag is _strict_.
+
+
The `new` Operator
@@ -13659,7 +13753,7 @@ Runtime Semantics: Evaluation
1. Let _arguments_ be the |Arguments| of _expr_.
1. Let _ref_ be the result of evaluating _memberExpr_.
1. Let _func_ be ? GetValue(_ref_).
- 1. If Type(_ref_) is Reference and IsPropertyReference(_ref_) is *false* and GetReferencedName(_ref_) is *"eval"*, then
+ 1. If Type(_ref_) is Reference, IsPropertyReference(_ref_) is *false*, and GetReferencedName(_ref_) is *"eval"*, then
1. If SameValue(_func_, %eval%) is *true*, then
1. Let _argList_ be ? ArgumentListEvaluation of _arguments_.
1. If _argList_ has no elements, return *undefined*.
@@ -13822,6 +13916,92 @@ Runtime Semantics: ArgumentListEvaluation
+
+ Optional Chains
+ An optional chain is a chain of one or more property accesses and function calls, the first of which begins with the token `?.`.
+
+ Runtime Semantics: Evaluation
+
+ OptionalExpression :
+ MemberExpression OptionalChain
+
+
+ 1. Let _baseReference_ be the result of evaluating |MemberExpression|.
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If _baseValue_ is *undefined* or *null*, then
+ 1. Return *undefined*.
+ 1. Return the result of performing ChainEvaluation of |OptionalChain| with arguments _baseValue_ and _baseReference_.
+
+
+ OptionalExpression :
+ CallExpression OptionalChain
+
+
+ 1. Let _baseReference_ be the result of evaluating |CallExpression|.
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If _baseValue_ is *undefined* or *null*, then
+ 1. Return *undefined*.
+ 1. Return the result of performing ChainEvaluation of |OptionalChain| with arguments _baseValue_ and _baseReference_.
+
+
+ OptionalExpression :
+ OptionalExpression OptionalChain
+
+
+ 1. Let _baseReference_ be the result of evaluating |OptionalExpression|.
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If _baseValue_ is *undefined* or *null*, then
+ 1. Return *undefined*.
+ 1. Return the result of performing ChainEvaluation of |OptionalChain| with arguments _baseValue_ and _baseReference_.
+
+
+
+ Runtime Semantics: ChainEvaluation
+ With parameters _baseValue_ and _baseReference_.
+ OptionalChain : `?.` Arguments
+
+ 1. Let _thisChain_ be this |OptionalChain|.
+ 1. Let _tailCall_ be IsInTailPosition(_thisChain_).
+ 1. Return ? EvaluateCall(_baseValue_, _baseReference_, |Arguments|, _tailCall_).
+
+ OptionalChain : `?.` `[` Expression `]`
+
+ 1. If the code matched by this |OptionalChain| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluatePropertyAccessWithExpressionKey(_baseValue_, |Expression|, _strict_).
+
+ OptionalChain : `?.` IdentifierName
+
+ 1. If the code matched by this |OptionalChain| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluatePropertyAccessWithIdentifierKey(_baseValue_, |IdentifierName|, _strict_).
+
+ OptionalChain : OptionalChain Arguments
+
+ 1. Let _optionalChain_ be |OptionalChain|.
+ 1. Let _newReference_ be ? ChainEvaluation of _optionalChain_ with arguments _baseValue_ and _baseReference_.
+ 1. Let _newValue_ be ? GetValue(_newReference_).
+ 1. Let _thisChain_ be this |OptionalChain|.
+ 1. Let _tailCall_ be IsInTailPosition(_thisChain_).
+ 1. Return ? EvaluateCall(_newValue_, _newReference_, |Arguments|, _tailCall_).
+
+ OptionalChain : OptionalChain `[` Expression `]`
+
+ 1. Let _optionalChain_ be |OptionalChain|.
+ 1. Let _newReference_ be ? ChainEvaluation of _optionalChain_ with arguments _baseValue_ and _baseReference_.
+ 1. Let _newValue_ be ? GetValue(_newReference_).
+ 1. If the code matched by this |OptionalChain| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluatePropertyAccessWithExpressionKey(_newValue_, |Expression|, _strict_).
+
+ OptionalChain : OptionalChain `.` IdentifierName
+
+ 1. Let _optionalChain_ be |OptionalChain|.
+ 1. Let _newReference_ be ? ChainEvaluation of _optionalChain_ with arguments _baseValue_ and _baseReference_.
+ 1. Let _newValue_ be ? GetValue(_newReference_).
+ 1. If the code matched by this |OptionalChain| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluatePropertyAccessWithIdentifierKey(_newValue_, |IdentifierName|, _strict_).
+
+
+
+
Import Calls
@@ -21758,6 +21938,34 @@ Expression Rules
1. If this |CallExpression| is _call_, return *true*.
1. Return *false*.
+
+ OptionalExpression :
+ MemberExpression OptionalChain
+ CallExpression OptionalChain
+ OptionalExpression OptionalChain
+
+
+ 1. Return HasCallInTailPosition of |OptionalChain| with argument _call_.
+
+
+ OptionalChain :
+ `?.` `[` Expression `]`
+ `?.` IdentifierName
+ OptionalChain `[` Expression `]`
+ OptionalChain `.` IdentifierName
+
+
+ 1. Return *false*.
+
+
+ OptionalChain :
+ `?.` Arguments
+ OptionalChain Arguments
+
+
+ 1. If this |OptionalChain| is _call_, return *true*.
+ 1. Return *false*.
+
MemberExpression :
MemberExpression TemplateLiteral