For the syntax, we use the `?.` token, with a lookahead at the level of the lexical grammar that allows to discriminate between `a?.b` (optional chaining) and `a?.3:0` (conditional operator, whose meaning cannot be changed due to backward compatibility constraints).
Normative additions are marked like this. In order to avoid distraction, we don’t mark mere editorial amendments.
+
+
List of significant editorial modifications:
+
+
Property Accessors: Factoring part of the property access operations in the new EvaluateDynamicPropertyAccess and EvaluateStaticPropertyAccess abstract operations.
+
Factoring out first step of EvaluateCall as in PR tc39/ecma262#963.
+
In new content, adoption of calling convention for directed operations of PR tc39/ecma262##955.
When processing an instance of the production CallExpression : CoverCallExpressionAndAsyncArrowHead the interpretation of |CoverCallExpressionAndAsyncArrowHead| is refined using the following grammar:
+
+ CallExpression : CoverCallExpressionAndAsyncArrowHead
+
+
+ 1. Return the result of parsing the lexical token stream matched by |CoverCallExpressionAndAsyncArrowHead| using |CallMemberExpression| as the goal symbol with its [Yield] and [Await] parameters set to the values used when parsing |CoverCallExpressionAndAsyncArrowHead|.
+
+
+
+
+
+
Static Semantics: Contains
+
With parameter _symbol_.
+
+ MemberExpression : MemberExpression `.` IdentifierName
+
+ 1. If |MemberExpression| 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*.
+
+ SuperProperty : `super` `.` IdentifierName
+
+ 1. If _symbol_ is the |ReservedWord| `super`, 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*.
+
+ CallExpression : CallExpression `.` IdentifierName
+
+ 1. If |CallExpression| 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*.
+
+
+ OptionalChain : OptionalChainingPunctuator 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*.
+
+
+
+
+
+
+
where <identifier-name-string> is the result of evaluating StringValue of |IdentifierName|.
+
+
+
+
+
Runtime Semantics: Evaluation
+ MemberExpression : MemberExpression `[` Expression `]`
+
+ 1. Let _baseReference_ be the result of evaluating |MemberExpression|.
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If the code matched by this |MemberExpression| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluateDynamicPropertyAccess(_baseValue_, |Expression|, _strict_).
+
+ MemberExpression : MemberExpression `.` IdentifierName
+
+ 1. Let _baseReference_ be the result of evaluating |MemberExpression|.
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If the code matched by this |MemberExpression| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluateStaticPropertyAccess(_baseValue_, |IdentifierName|, _strict_);
+
+ CallExpression : CallExpression `[` Expression `]`
+
+ 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 ? EvaluateDynamicPropertyAccess(_baseValue_, |Expression|, _strict_).
+
+
+ CallExpression : CallExpression `.` IdentifierName
+
+ 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 ? EvaluateStaticPropertyAccess(_baseValue_, |IdentifierName|, _strict_);
+
+
+
+
+
The abstract operation EvaluateDynamicPropertyAccess 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_.
+
+
+
The abstract operation EvaluateStaticPropertyAccess takes as arguments a value _baseValue_, a Parse Node _identifierName_, and a Boolean argument _strict_. It performs the following steps:
+
+ 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_.
+
+
+
+
+
+
+
+ CallExpression : CoverCallExpressionAndAsyncArrowHead
+
+ 1. Let _expr_ be CoveredCallExpression of |CoverCallExpressionAndAsyncArrowHead|.
+ 1. Let _memberExpr_ be the |MemberExpression| of _expr_.
+ 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, 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. ReturnIfAbrupt(_argList_).
+ 1. If _argList_ has no elements, return *undefined*.
+ 1. Let _evalText_ be the first element of _argList_.
+ 1. If the source code matching this |CallExpression| is strict mode code, let _strictCaller_ be *true*. Otherwise let _strictCaller_ be *false*.
+ 1. Let _evalRealm_ be the current Realm Record.
+ 1. Perform ? HostEnsureCanCompileStrings(_evalRealm_, _evalRealm_).
+ 1. Return ? PerformEval(_evalText_, _evalRealm_, _strictCaller_, *true*).
+ 1. Let _thisCall_ be this |CallExpression|.
+ 1. Let _tailCall_ be IsInTailPosition(_thisCall_).
+ 1. Return ? EvaluateCall(_func_, _ref_, _arguments_, _tailCall_).
+
+
A |CallExpression| evaluation that executes step 6.a.vii is a direct eval.
+ CallExpression : CallExpression Arguments
+
+ 1. Let _ref_ be the result of evaluating |CallExpression|.
+ 1. Let _func_ be ? GetValue(_ref_).
+ 1. Let _thisCall_ be this |CallExpression|.
+ 1. Let _tailCall_ be IsInTailPosition(_thisCall_).
+ 1. Return ? EvaluateCall(_func_, _ref_, |Arguments|, _tailCall_).
+
+
+
+
+
+
The abstract operation EvaluateCall takes as arguments a value _func_, a value _ref_, a Parse Node _arguments_, and a Boolean argument _tailPosition_. It performs the following steps:
+
+ 1. If Type(_ref_) is Reference, then
+ 1. If IsPropertyReference(_ref_) is *true*, then
+ 1. Let _thisValue_ be GetThisValue(_ref_).
+ 1. Else the base of _ref_ is an Environment Record,
+ 1. Let _refEnv_ be GetBase(_ref_).
+ 1. Let _thisValue_ be _refEnv_.WithBaseObject().
+ 1. Else Type(_ref_) is not Reference,
+ 1. Let _thisValue_ be *undefined*.
+ 1. Let _argList_ be ArgumentListEvaluation of _arguments_.
+ 1. ReturnIfAbrupt(_argList_).
+ 1. If Type(_func_) is not Object, throw a *TypeError* exception.
+ 1. If IsCallable(_func_) is *false*, throw a *TypeError* exception.
+ 1. If _tailPosition_ is *true*, perform PrepareForTailCall().
+ 1. Let _result_ be Call(_func_, _thisValue_, _argList_).
+ 1. Assert: If _tailPosition_ is *true*, the above call will not return here, but instead evaluation will continue as if the following return has already occurred.
+ 1. Assert: If _result_ is not an abrupt completion, then Type(_result_) is an ECMAScript language type.
+ 1. Return _result_.
+
+
+
+
+
+
+
+ An optional chain is a chain of property accesses and function calls introduced by a |OptionalChainingPunctuator|.
+
+
+
Runtime Semantics: Evaluation
+
+ OptionalChainingExpression :
+ MemberExpression OptionalChain
+ CallExpression OptionalChain
+ OptionalChainingExpression OptionalChain
+
+
+ 1. Let _baseExpression_ be the first child of this production (i.e., this |MemberExpression|, |CallExpression|, or |OptionalChainingExpression|).
+ 1. Let _baseReference_ be ? _baseExpression_.Evaluation().
+ 1. Let _baseValue_ be ? GetValue(_baseReference_).
+ 1. If _baseValue_ is *undefined* or *null*, then
+ 1. Return *undefined*.
+ 1. Let _optionalChain_ be this |OptionalChain|.
+ 1. Return ? _optionalChain_.ChainEvaluation(_baseValue_, _baseReference_).
+
+
+
+
Runtime Semantics: ChainEvaluation
+
With parameters _baseValue_ and _baseReference_.
+ OptionalChain : OptionalChainingPunctuator `[` Expression `]`
+
+ 1. If the code matched by this |OptionalChain| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluateDynamicPropertyAccess(_baseValue_, |Expression|, _strict_).
+
+ OptionalChain : OptionalChainingPunctuator IdentifierName
+
+ 1. If the code matched by this |OptionalChain| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluateStaticPropertyAccess(_baseValue_, |IdentifierName|, _strict_).
+
+ OptionalChain : OptionalChainingPunctuator Arguments
+
+ 1. If the code matched by this |OptionalChain| is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
+ 1. Return ? EvaluateCall(_baseValue_, _baseReference_, |Arguments|, _strict_).
+
+ OptionalChain : OptionalChain `[` Expression `]`
+
+ 1. Let _optionalChain_ be this |OptionalChain|.
+ 1. Let _newReference_ be ? _optionalChain_.ChainEvaluation(_baseValue_, _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 ? EvaluateDynamicPropertyAccess(_newValue_, |Expression|, _strict_).
+
+ OptionalChain : OptionalChain `.` IdentifierName
+
+ 1. Let _optionalChain_ be this |OptionalChain|.
+ 1. Let _newReference_ be ? _optionalChain_.ChainEvaluation(_baseValue_, _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 ? EvaluateStaticPropertyAccess(_newValue_, |IdentifierName|, _strict_).
+
+ OptionalChain : OptionalChain Arguments
+
+ 1. Let _optionalChain_ be this |OptionalChain|.
+ 1. Let _newReference_ be ? _optionalChain_.ChainEvaluation(_baseValue_, _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 ? EvaluateCall(_newValue_, _newReference_, |Arguments|, _strict_).
+
+
+
+
+
+
+
+