From f979933bc5be8847c024b77ab29a146b1bc2f879 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 24 Jul 2019 15:33:24 -0700 Subject: [PATCH] Normative: Add Optional Chaining (#1646) --- spec.html | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 12 deletions(-) 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