Stage 2 Draft / October 7, 2021

ECMAScript Explicit Resource Management

Introduction

This proposal introduces syntax and semantics around explicit resource management.

See the proposal repository for background material and discussion.

1 Notational Conventions

1.1 Algorithm Conventions

1.1.1 Runtime Semantics

1.1.1.1 DisposeIfAbrupt

Algorithm steps that say or are otherwise equivalent to:

  1. DisposeIfAbrupt(argument, disposable).

mean the same thing as:

  1. Assert: disposable is an object with a [[DisposableResourceStack]] internal slot or undefined.
  2. If argument is an abrupt completion, set argument to DisposeResources(disposable, argument).

Algorithm steps that say or are otherwise equivalent to:

  1. DisposeIfAbrupt(argument, disposable, errors).

mean the same thing as:

  1. Assert: disposable is an object with a [[DisposableResourceStack]] internal slot or undefined.
  2. Assert: errors is a List.
  3. If argument is an abrupt completion, set argument to DisposeResources(disposable, argument, errors).

Algorithm steps that say or are otherwise equivalent to:

  1. DisposeIfAbrupt(AbstractOperation(), disposable).

mean the same thing as:

  1. Assert: disposable is an object with a [[DisposableResourceStack]] internal slot or undefined.
  2. Let hygenicTemp be AbstractOperation().
  3. If hygenicTemp is an abrupt completion, set hygenicTemp to DisposeResources(disposable, hygenicTemp).

Where hygenicTemp is ephemeral and visible only in the steps pertaining to DisposeIfAbrupt.

Algorithm steps that say or are otherwise equivalent to:

  1. DisposeIfAbrupt(AbstractOperation(), disposable, errors).

mean the same thing as:

  1. Assert: disposable is an object with a [[DisposableResourceStack]] internal slot or undefined.
  2. Assert: errors is a List.
  3. Let hygenicTemp be AbstractOperation().
  4. If hygenicTemp is an abrupt completion, set hygenicTemp to DisposeResources(disposable, hygenicTemp, errors).

Where hygenicTemp is ephemeral and visible only in the steps pertaining to DisposeIfAbrupt.

Algorithm steps that say or are otherwise equivalent to:

  1. Let result be AbstractOperation(DisposeIfAbrupt(argument, disposable)).

mean the same thing as:

  1. Assert: disposable is an object with a [[DisposableResourceStack]] internal slot or undefined.
  2. If argument is an abrupt completion, set argument to DisposeResources(disposable, argument).
  3. Let result be AbstractOperation(argument).

Algorithm steps that say or are otherwise equivalent to:

  1. Let result be AbstractOperation(DisposeIfAbrupt(argument, disposable, errors)).

mean the same thing as:

  1. Assert: disposable is an object with a [[DisposableResourceStack]] internal slot or undefined.
  2. Assert: errors is a List.
  3. If argument is an abrupt completion, set argument to DisposeResources(disposable, argument, errors).
  4. Let result be AbstractOperation(argument).
Note

DisposeIfAbrupt can be combined with the ? and ! prefixes, so that for example

  1. Let result be ? DisposeIfAbrupt(value, disposable).

means the same thing as:

  1. Let result be DisposeIfAbrupt(value, disposable).
  2. ReturnIfAbrupt(result).

2 ECMAScript Data Types and Values

2.1 ECMAScript Language Types

2.1.1 The Symbol Type

2.1.1.1 Well-Known Symbols

Table 1: Well-known Symbols
Specification Name [[Description]] Value and Purpose
@@asyncDispose "Symbol.asyncDispose" A method that performs explicit resource cleanup on an object. Called by the semantics of the using await const statement while in an async function, async generator, or the top level of a Module.
@@dispose "Symbol.dispose" A method that performs explicit resource cleanup on an object. Called by the semantics of the using const statement.

2.2 ECMAScript Specification Types

2.2.1 The Reference Record Specification Type

2.2.1.1 InitializeReferencedBinding ( V, W, hint )

The abstract operation InitializeReferencedBinding takes arguments V and W, W, and hint. It performs the following steps when called:

  1. ReturnIfAbrupt(V).
  2. ReturnIfAbrupt(W).
  3. Assert: V is a Reference Record.
  4. Assert: IsUnresolvableReference(V) is false.
  5. Assert: hint is normal, sync, or async.
  6. Let base be V.[[Base]].
  7. Assert: base is an Environment Record.
  8. Return base.InitializeBinding(V.[[ReferencedName]], W, hint).

3 Abstract Operations

3.1 Operations on Disposable Objects

See Common Resource Management Interfaces (10.2.1).

3.1.1 DisposableResource Records

A DisposableResource is a Record value used to encapsulate a disposable object along with the method used to dispose the object. DisposableResource Records are produced by the CreateDisposableResource abstract operation.

DisposableResource Records have the fields listed in Table 2:

Table 2: DisposableResource Record Fields
Field Name Value Meaning
[[ResourceValue]] Any ECMAScript language value. The value to be disposed.
[[Hint]] sync or async. Indicates whether the resources was added by a using const statement (sync) or a using await const statement (async).
[[DisposeMethod]] A function object. A function object that will be called with [[ResourceValue]] as its this value when the resource disposed.

3.1.2 CreateDisposableResource ( V, hint )

The abstract operation CreateDisposableResource takes arguments V (an ECMAScript language value) and hint (either sync or async). It performs the following steps when called:

  1. Let disposeMethod be ? GetDisposeMethod(V, hint).
  2. Return the DisposableResource { [[ResourceValue]]: V, [[Hint]]: hint, [[DisposeMethod]]: disposeMethod }.

3.1.3 GetDisposeMethod ( V, hint )

The abstract operation GetDisposeMethod takes arguments V (an ECMAScript language value) and hint (either sync or async). It performs the following steps when called:

  1. If V is null or undefined, return undefined.
  2. If hint is async, then
    1. Let method be ? GetMethod(V, @@asyncDispose).
    2. If method is undefined, then
      1. Set method to ? GetMethod(V, @@dispose).
  3. Else,
    1. Let method be ? GetMethod(V, @@dispose).
  4. If method is undefined, throw a TypeError exception.
  5. Return method.

3.1.4 Dispose ( V, hint, method )

The abstract operation Dispose takes arguments V (an ECMAScript language value), hint (either sync or async), and method (a function object). It performs the following steps when called:

  1. If V is null or undefined, return undefined.
  2. Let result be ? Call(method, V).
  3. If hint is async and result is not undefined, then
    1. Perform ? Await(result).
  4. Return undefined.

3.1.5 AddDisposableResource ( disposable, V, hint )

The abstract operation AddDisposableResource takes arguments disposable (an object with a [[DisposableResourceStack]] internal slot), V (an ECMAScript language value), and hint (either sync or async). It performs the following steps when called:

  1. If V is null or undefined, return NormalCompletion(empty).
  2. Let resource be ? CreateDisposableResource(V, hint).
  3. Append resource to disposable.[[DisposableResourceStack]].
  4. Return NormalCompletion(empty).

3.1.6 DisposeResources ( disposable, completion [ , errors ] )

The abstract operation DisposeResources takes arguments disposable (an object with a [[DisposableResourceStack]] internal slot or undefined) and completion (a Completion Record) and optional argument errors (a List). It performs the following steps when called:

  1. If errors is not present, let errors be a new empty List.
  2. If completion.[[Type]] is throw, then
    1. Append completion.[[Value]] to errors.
  3. If disposable is not undefined, then
    1. For each resource of disposable.[[DisposableResourceStack]], in reverse list order, do
      1. Let result be Dispose(resource.[[ResourceValue]], resource.[[Hint]], resource.[[DisposeMethod]]).
      2. If result.[[Type]] is throw, then
        1. Append result.[[Value]] to errors.
  4. Let errorsLength be the number of elements in errors.
  5. If errorsLength > 1, then
    1. Let error be a newly created AggregateError object.
    2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: ! CreateArrayFromList(errors) }).
    3. Return ThrowCompletion(error).
  6. If errorsLength = 1, then
    1. Let error be the first element of errors.
    2. Return ThrowCompletion(error).
  7. Return completion.

4 Syntax-Directed Operations

4.1 Scope Analysis

4.1.1 Static Semantics: BoundNames

LexicalDeclaration : UsingConst BindingList ;
  1. Return the BoundNames of BindingList.
LexicalBinding : void Initializer
  1. Return a new empty List.

4.1.2 Static Semantics: IsConstantDeclaration

LexicalDeclaration : UsingConst BindingList ;
  1. Return true.

4.2 Miscellaneous

4.2.1 Runtime Semantics: IteratorBindingInitialization

With parameters iteratorRecord and environment.

SingleNameBinding : BindingIdentifier Initializeropt
  1. Let bindingId be StringValue of BindingIdentifier.
  2. Let lhs be ? ResolveBinding(bindingId, environment).
  3. If iteratorRecord.[[Done]] is false, then
    1. Let next be IteratorStep(iteratorRecord).
    2. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
    3. ReturnIfAbrupt(next).
    4. If next is false, set iteratorRecord.[[Done]] to true.
    5. Else,
      1. Let v be IteratorValue(next).
      2. If v is an abrupt completion, set iteratorRecord.[[Done]] to true.
      3. ReturnIfAbrupt(v).
  4. If iteratorRecord.[[Done]] is true, let v be undefined.
  5. If Initializer is present and v is undefined, then
    1. If IsAnonymousFunctionDefinition(Initializer) is true, then
      1. Set v to the result of performing NamedEvaluation for Initializer with argument bindingId.
    2. Else,
      1. Let defaultValue be the result of evaluating Initializer.
      2. Set v to ? GetValue(defaultValue).
  6. If environment is undefined, return ? PutValue(lhs, v).
  7. Return InitializeReferencedBinding(lhs, v, normal).
BindingRestElement : ... BindingIdentifier
  1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment).
  2. Let A be ! ArrayCreate(0).
  3. Let n be 0.
  4. Repeat,
    1. If iteratorRecord.[[Done]] is false, then
      1. Let next be IteratorStep(iteratorRecord).
      2. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
      3. ReturnIfAbrupt(next).
      4. If next is false, set iteratorRecord.[[Done]] to true.
    2. If iteratorRecord.[[Done]] is true, then
      1. If environment is undefined, return ? PutValue(lhs, A).
      2. Return InitializeReferencedBinding(lhs, A, normal).
    3. Let nextValue be IteratorValue(next).
    4. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
    5. ReturnIfAbrupt(nextValue).
    6. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), nextValue).
    7. Set n to n + 1.

5 Executable Code and Execution Contexts

5.1 Environment Records

Environment Record is a specification type used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of ECMAScript code. Usually an Environment Record is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement. Each time such code is evaluated, a new Environment Record is created to record the identifier bindings that are created by that code.

Every Environment Record has an [[OuterEnv]] field, which is either null or a reference to an outer Environment Record. This is used to model the logical nesting of Environment Record values. The outer reference of an (inner) Environment Record is a reference to the Environment Record that logically surrounds the inner Environment Record. An outer Environment Record may, of course, have its own outer Environment Record. An Environment Record may serve as the outer environment for multiple inner Environment Records. For example, if a FunctionDeclaration contains two nested FunctionDeclarations then the Environment Records of each of the nested functions will have as their outer Environment Record the Environment Record of the current evaluation of the surrounding function.

Environment Records are purely specification mechanisms and need not correspond to any specific artefact of an ECMAScript implementation. It is impossible for an ECMAScript program to directly access or manipulate such values.

5.1.1 The Environment Record Type Hierarchy

The Environment Record abstract class includes the abstract specification methods defined in Table 3. These abstract methods have distinct concrete algorithms for each of the concrete subclasses.

Table 3: Abstract Methods of Environment Records
Method Purpose
HasBinding(N) Determine if an Environment Record has a binding for the String value N. Return true if it does and false if it does not.
CreateMutableBinding(N, D) Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name. If the Boolean argument D is true the binding may be subsequently deleted.
CreateImmutableBinding(N, S) Create a new but uninitialized immutable binding in an Environment Record. The String value N is the text of the bound name. If S is true then attempts to set it after it has been initialized will always throw an exception, regardless of the strict mode setting of operations that reference that binding.
InitializeBinding(N, V, hint) Set the value of an already existing but uninitialized binding in an Environment Record. The String value N is the text of the bound name. V is the value for the binding and is a value of any ECMAScript language type. hint indicates whether the binding came from a using const statement (sync), a using await const statement (async), or a regular variable declaration (normal).
SetMutableBinding(N, V, S) Set the value of an already existing mutable binding in an Environment Record. The String value N is the text of the bound name. V is the value for the binding and may be a value of any ECMAScript language type. S is a Boolean flag. If S is true and the binding cannot be set throw a TypeError exception.
GetBindingValue(N, S) Returns the value of an already existing binding from an Environment Record. The String value N is the text of the bound name. S is used to identify references originating in strict mode code or that otherwise require strict mode reference semantics. If S is true and the binding does not exist throw a ReferenceError exception. If the binding exists but is uninitialized a ReferenceError is thrown, regardless of the value of S.
DeleteBinding(N) Delete a binding from an Environment Record. The String value N is the text of the bound name. If a binding for N exists, remove the binding and return true. If the binding exists but cannot be removed return false. If the binding does not exist return true.
HasThisBinding() Determine if an Environment Record establishes a this binding. Return true if it does and false if it does not.
HasSuperBinding() Determine if an Environment Record establishes a super method binding. Return true if it does and false if it does not.
WithBaseObject() If this Environment Record is associated with a with statement, return the with object. Otherwise, return undefined.

5.1.1.1 Declarative Environment Records

Each declarative Environment Record is associated with an ECMAScript program scope containing variable, constant, let, class, module, import, and/or function declarations. A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope.

Every declarative Environment Record also has a [[DisposableResourceStack]] field, which is a List of DisposableResource Records. This list is a stack of resources tracked by the using const and using await const statements that must be disposed when the Evaluation step that constructed the Environment Record has completed.

The behaviour of the concrete specification methods for declarative Environment Records is defined by the following algorithms.

5.1.1.1.1 InitializeBinding ( N, V, hint )

The InitializeBinding concrete method of a declarative Environment Record envRec takes arguments N (a String) and, V (an ECMAScript language value), and hint (either normal, sync, or async). It is used to set the bound value of the current binding of the identifier whose name is the value of the argument N to the value of argument V. An uninitialized binding for N must already exist. It performs the following steps when called:

  1. Assert: envRec must have an uninitialized binding for N.
  2. If hint is not normal, perform ? AddDisposableResource(envRec, V, hint).
  3. Set the bound value for N in envRec to V.
  4. Record that the binding for N in envRec has been initialized.
  5. Return NormalCompletion(empty).

5.1.1.2 Object Environment Records

5.1.1.2.1 InitializeBinding ( N, V, hint )

The InitializeBinding concrete method of an object Environment Record envRec takes arguments N (a String) and, V (an ECMAScript language value), and hint (either normal, sync, or async). It is used to set the bound value of the current binding of the identifier whose name is the value of the argument N to the value of argument V. It performs the following steps when called:

  1. Assert: hint is normal.
  2. Return ? envRec.SetMutableBinding(N, V, false).
Note

In this specification, all uses of CreateMutableBinding for object Environment Records are immediately followed by a call to InitializeBinding for the same name. Hence, this specification does not explicitly track the initialization state of bindings in object Environment Records.

5.1.1.3 Global Environment Records

5.1.1.3.1 InitializeBinding ( N, V, hint )

The InitializeBinding concrete method of a global Environment Record envRec takes arguments N (a String) and, V (an ECMAScript language value), and hint (either normal, sync, or async). It is used to set the bound value of the current binding of the identifier whose name is the value of the argument N to the value of argument V. An uninitialized binding for N must already exist. It performs the following steps when called:

  1. Let DclRec be envRec.[[DeclarativeRecord]].
  2. If DclRec.HasBinding(N) is true, then
    1. Return DclRec.InitializeBinding(N, V, hint).
  3. Assert: If the binding exists, it must be in the object Environment Record.
  4. Assert: hint is normal.
  5. Let ObjRec be envRec.[[ObjectRecord]].
  6. Return ? ObjRec.InitializeBinding(N, V).

6 Ordinary and Exotic Objects Behaviors

6.1 ECMAScript Function Objects

6.1.1 [[Call]] ( thisArgument, argumentsList )

The [[Call]] internal method of an ECMAScript function object F takes arguments thisArgument (an ECMAScript language value) and argumentsList (a List of ECMAScript language values). It performs the following steps when called:

  1. Let callerContext be the running execution context.
  2. Let calleeContext be PrepareForOrdinaryCall(F, undefined).
  3. Assert: calleeContext is now the running execution context.
  4. If F.[[IsClassConstructor]] is true, then
    1. Let error be a newly created TypeError object.
    2. NOTE: error is created in calleeContext with F's associated Realm Record.
    3. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
    4. Return ThrowCompletion(error).
  5. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
  6. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
  7. Let env be the LexicalEnvironment of calleeContext.
  8. Set result to the result of performing DisposeResources(env, result).
  9. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
  10. If result.[[Type]] is return, return NormalCompletion(result.[[Value]]).
  11. ReturnIfAbrupt(result).
  12. Return NormalCompletion(undefined).
Note

When calleeContext is removed from the execution context stack in step 9 it must not be destroyed if it is suspended and retained for later resumption by an accessible Generator.

7 ECMAScript Language: Statements and Declarations

7.1 Block

7.1.1 Runtime Semantics: Evaluation

Block : { StatementList }
  1. Let oldEnv be the running execution context's LexicalEnvironment.
  2. Let blockEnv be NewDeclarativeEnvironment(oldEnv).
  3. Perform BlockDeclarationInstantiation(StatementList, blockEnv).
  4. Set the running execution context's LexicalEnvironment to blockEnv.
  5. Let blockValue be the result of evaluating StatementList.
  6. Set blockValue to the result of performing DisposeResources(blockEnv, blockValue).
  7. Set the running execution context's LexicalEnvironment to oldEnv.
  8. Return blockValue.
Note

No matter how control leaves the Block the LexicalEnvironment is always restored to its former state.

7.2 Declarations and the Variable Statement

7.2.1 Let and Const Declarations

Syntax

LexicalDeclaration[In, Yield, Await] : LetOrConst BindingList[?In, ?Yield, ?Await, ~Using] ; UsingConst[?Await] BindingList[?In, ?Yield, ?Await, +Using] ; LetOrConst : let const UsingConst[Await] : using [no LineTerminator here] const [+Await] using [no LineTerminator here] await [no LineTerminator here] const BindingList[In, Yield, Await, Using] : LexicalBinding[?In, ?Yield, ?Await, ?Using] BindingList[?In, ?Yield, ?Await, ?Using] , LexicalBinding[?In, ?Yield, ?Await, ?Using] LexicalBinding[In, Yield, Await, Using] : BindingIdentifier[?Yield, ?Await] Initializer[?In, ?Yield, ?Await]opt BindingPattern[?Yield, ?Await] Initializer[?In, ?Yield, ?Await] [~Using] BindingPattern[?Yield, ?Await] Initializer[?In, ?Yield, ?Await] [+Using] void Initializer[?In, ?Yield, ?Await]

7.2.1.1 Static Semantics: Early Errors

LexicalDeclaration : UsingConst BindingList ; Note
Draft Note: This behavior is currently under discussion in #66. The above boundaries have been chosen for the following reasons:

7.2.1.2 Runtime Semantics: Evaluation

LexicalDeclaration : LetOrConst BindingList ;
  1. Let next be the result of evaluatingBindingEvaluation for BindingList with parameter normal.
  2. ReturnIfAbrupt(next).
  3. Return NormalCompletion(empty).
LexicalDeclaration : UsingConst BindingList ;
  1. If UsingConst Contains await, let hint be async.
  2. Otherwise, Let hint be sync.
  3. Let next be the result of BindingEvaluation for BindingList with parameter hint.
  4. ReturnIfAbrupt(next).
  5. Return NormalCompletion(empty).
BindingList : BindingList , LexicalBinding
  1. Let next be the result of evaluating BindingList.
  2. ReturnIfAbrupt(next).
  3. Return the result of evaluating LexicalBinding.
LexicalBinding : BindingIdentifier
  1. Let lhs be ResolveBinding(StringValue of BindingIdentifier).
  2. Return InitializeReferencedBinding(lhs, undefined).
Note

A static semantics rule ensures that this form of LexicalBinding never occurs in a const declaration.

LexicalBinding : BindingIdentifier Initializer
  1. Let bindingId be StringValue of BindingIdentifier.
  2. Let lhs be ResolveBinding(bindingId).
  3. If IsAnonymousFunctionDefinition(Initializer) is true, then
    1. Let value be NamedEvaluation of Initializer with argument bindingId.
  4. Else,
    1. Let rhs be the result of evaluating Initializer.
    2. Let value be ? GetValue(rhs).
  5. Return InitializeReferencedBinding(lhs, value).
LexicalBinding : BindingPattern Initializer
  1. Let rhs be the result of evaluating Initializer.
  2. Let value be ? GetValue(rhs).
  3. Let env be the running execution context's LexicalEnvironment.
  4. Return the result of performing BindingInitialization for BindingPattern using value and env as the arguments.

7.2.1.3 Runtime Semantics: BindingEvaluation

With parameter hint (either normal, sync, or async).

BindingList : BindingList , LexicalBinding
  1. Let next be the result of BindingEvaluation for BindingList with parameter hint.
  2. ReturnIfAbrupt(next).
  3. Return the result of BindingEvaluation for LexicalBinding with parameter hint.
LexicalBinding : BindingIdentifier
  1. Assert: hint is normal.
  2. Let lhs be ResolveBinding(StringValue of BindingIdentifier).
  3. Return InitializeReferencedBinding(lhs, undefined, normal).
Note

A static semantics rule ensures that this form of LexicalBinding never occurs in a const declaration.

LexicalBinding : BindingIdentifier Initializer
  1. Let bindingId be StringValue of BindingIdentifier.
  2. Let lhs be ResolveBinding(bindingId).
  3. If IsAnonymousFunctionDefinition(Initializer) is true, then
    1. Let value be NamedEvaluation of Initializer with argument bindingId.
  4. Else,
    1. Let rhs be the result of evaluating Initializer.
    2. Let value be ? GetValue(rhs).
  5. Return InitializeReferencedBinding(lhs, value, hint).
LexicalBinding : BindingPattern Initializer
  1. Assert: hint is normal.
  2. Let rhs be the result of evaluating Initializer.
  3. Let value be ? GetValue(rhs).
  4. Let env be the running execution context's LexicalEnvironment.
  5. Return the result of performing BindingInitialization for BindingPattern using value and env as the arguments.
LexicalBinding : void Initializer
  1. Assert: hint is sync or async.
  2. Let expr be the result of evaluating Initializer.
  3. Let value be ? GetValue(expr).
  4. Let env be the running execution context's LexicalEnvironment.
  5. Return AddDisposableResource(env, value, hint).

7.2.2 Destructuring Binding Patterns

7.2.2.1 Runtime Semantics: RestBindingInitialization

With parameters value, environment, and excludedNames.

BindingRestProperty : ... BindingIdentifier
  1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment).
  2. Let restObj be ! OrdinaryObjectCreate(%Object.prototype%).
  3. Perform ? CopyDataProperties(restObj, value, excludedNames).
  4. If environment is undefined, return PutValue(lhs, restObj).
  5. Return InitializeReferencedBinding(lhs, restObj, normal).

7.2.2.2 Runtime Semantics: KeyedBindingInitialization

With parameters value, environment, and propertyName.

Note

When undefined is passed for environment it indicates that a PutValue operation should be used to assign the initialization value. This is the case for formal parameter lists of non-strict functions. In that case the formal parameter bindings are preinitialized in order to deal with the possibility of multiple parameters with the same name.

SingleNameBinding : BindingIdentifier Initializeropt
  1. Let bindingId be StringValue of BindingIdentifier.
  2. Let lhs be ? ResolveBinding(bindingId, environment).
  3. Let v be ? GetV(value, propertyName).
  4. If Initializer is present and v is undefined, then
    1. If IsAnonymousFunctionDefinition(Initializer) is true, then
      1. Set v to the result of performing NamedEvaluation for Initializer with argument bindingId.
    2. Else,
      1. Let defaultValue be the result of evaluating Initializer.
      2. Set v to ? GetValue(defaultValue).
  5. If environment is undefined, return ? PutValue(lhs, v).
  6. Return InitializeReferencedBinding(lhs, v, normal).

7.3 Iteration Statements

7.3.1 Semantics

7.3.1.1 Runtime Semantics: LoopEvaluation

With parameter labelSet.

IterationStatement : for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement
  1. Let oldEnv be the running execution context's LexicalEnvironment.
  2. Let loopEnv be NewDeclarativeEnvironment(oldEnv).
  3. Let isConst be IsConstantDeclaration of LexicalDeclaration.
  4. Let boundNames be the BoundNames of LexicalDeclaration.
  5. For each element dn of boundNames, do
    1. If isConst is true, then
      1. Perform ! loopEnv.CreateImmutableBinding(dn, true).
    2. Else,
      1. Perform ! loopEnv.CreateMutableBinding(dn, false).
  6. Set the running execution context's LexicalEnvironment to loopEnv.
  7. Let forDcl be the result of evaluating LexicalDeclaration.
  8. If forDcl is an abrupt completion, then
    1. Set forDcl to be DisposeResources(loopEnv, forDcl).
    2. Set the running execution context's LexicalEnvironment to oldEnv.
    3. Return Completion(forDcl).
  9. If isConst is false, let perIterationLets be boundNames; otherwise let perIterationLets be « ».
  10. Let bodyResult be ForBodyEvaluation(the first Expression, the second Expression, Statement, perIterationLets, labelSet).
  11. Set bodyResult to be DisposeResources(loopEnv, bodyResult).
  12. Set the running execution context's LexicalEnvironment to oldEnv.
  13. Return Completion(bodyResult).

7.3.2 The for Statement

7.3.2.1 ForBodyEvaluation ( test, increment, stmt, perIterationBindings, labelSet )

The abstract operation ForBodyEvaluation takes arguments test, increment, stmt, perIterationBindings, and labelSet. It performs the following steps when called:

  1. Let V be undefined.
  2. Perform ? CreatePerIterationEnvironment(perIterationBindings).
  3. Let thisIterationEnv be ? CreatePerIterationEnvironment(perIterationBindings).
  4. Repeat,
    1. If test is not [empty], then
      1. Let testRef be the result of evaluating test.
      2. Let testValue be ? GetValue(testRef).
      3. Let testValue be ? DisposeIfAbrupt(GetValue(testRef), thisIterationEnv).
      4. If ! ToBoolean(testValue) is false, return NormalCompletion(V).
      5. If ! ToBoolean(testValue) is false, return DisposeResources(thisIterationEnv, NormalCompletion(V)).
    2. Let result be the result of evaluating stmt.
    3. If LoopContinues(result, labelSet) is false, return Completion(UpdateEmpty(result, V)).
    4. If LoopContinues(result, labelSet) is false, return DisposeResources(thisIterationEnv, Completion(UpdateEmpty(result, V))).
    5. If result.[[Value]] is not empty, set V to result.[[Value]].
    6. Perform ? CreatePerIterationEnvironment(perIterationBindings).
    7. Perform ? DisposeResources(thisIterationEnv, undefined).
    8. Set thisIterationEnv to be ? CreatePerIterationEnvironment(perIterationBindings).
    9. If increment is not [empty], then
      1. Let incRef be the result of evaluating increment.
      2. Perform ? GetValue(incRef).
      3. Perform ? DisposeIfAbrupt(GetValue(incRef), thisIterationEnv).

7.3.2.2 CreatePerIterationEnvironment ( perIterationBindings )

The abstract operation CreatePerIterationEnvironment takes argument perIterationBindings. It performs the following steps when called:

  1. If perIterationBindings has any elements, then
    1. Let lastIterationEnv be the running execution context's LexicalEnvironment.
    2. Let outer be lastIterationEnv.[[OuterEnv]].
    3. Assert: outer is not null.
    4. Let thisIterationEnv be NewDeclarativeEnvironment(outer).
    5. For each element bn of perIterationBindings, do
      1. Perform ! thisIterationEnv.CreateMutableBinding(bn, false).
      2. Let lastValue be ? lastIterationEnv.GetBindingValue(bn, true).
      3. Perform thisIterationEnv.InitializeBinding(bn, lastValue).
    6. Set the running execution context's LexicalEnvironment to thisIterationEnv.
    7. Return thisIterationEnv.
  2. Return undefined.

7.3.3 The for-in, for-of, and for-await-of Statements

Syntax

ForInOfStatement[Yield, Await, Return] : for ( [lookahead ≠ let [] LeftHandSideExpression[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( var ForBinding[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( ForDeclaration[?Yield, ?Await] in Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( [lookahead ∉ { let, async of }] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( var ForBinding[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] for ( ForDeclaration[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] [+Await] for await ( [lookahead ≠ let] LeftHandSideExpression[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] [+Await] for await ( var ForBinding[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] [+Await] for await ( ForDeclaration[?Yield, ?Await] of AssignmentExpression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] ForDeclaration[Yield, Await] : LetOrConst ForBinding[?Yield, ?Await, ~Using] UsingConst[?Await] ForBinding[?Yield, ?Await, +Using] ForBinding[Yield, Await, Using] : BindingIdentifier[?Yield, ?Await] BindingPattern[?Yield, ?Await] [~Using]BindingPattern[?Yield, ?Await]

7.3.3.1 Static Semantics: Early Errors

ForInOfStatement : for ( ForDeclaration in Expression ) Statement

7.3.3.2 ForIn/OfHeadEvaluation ( uninitializedBoundNames, expr, iterationKind )

The abstract operation ForIn/OfHeadEvaluation takes arguments uninitializedBoundNames, expr, and iterationKind (either enumerate, iterate, or async-iterate). It performs the following steps when called:

  1. Let oldEnv be the running execution context's LexicalEnvironment.
  2. If uninitializedBoundNames is not an empty List, then
    1. Assert: uninitializedBoundNames has no duplicate entries.
    2. Let newEnv be NewDeclarativeEnvironment(oldEnv).
    3. For each String name of uninitializedBoundNames, do
      1. Perform ! newEnv.CreateMutableBinding(name, false).
    4. Set the running execution context's LexicalEnvironment to newEnv.
  3. Else,
    1. Set newEnv to be undefined.
  4. Let exprRef be the result of evaluating expr.
  5. Set exprRef to be DisposeResources(newEnv, exprRef).
  6. Set the running execution context's LexicalEnvironment to oldEnv.
  7. Let exprValue be ? GetValue(exprRef).
  8. If iterationKind is enumerate, then
    1. If exprValue is undefined or null, then
      1. Return Completion { [[Type]]: break, [[Value]]: empty, [[Target]]: empty }.
    2. Let obj be ! ToObject(exprValue).
    3. Let iterator be ? EnumerateObjectProperties(obj).
    4. Let nextMethod be ! GetV(iterator, "next").
    5. Return the Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
  9. Else,
    1. Assert: iterationKind is iterate or async-iterate.
    2. If iterationKind is async-iterate, let iteratorHint be async.
    3. Else, let iteratorHint be sync.
    4. Return ? GetIterator(exprValue, iteratorHint).

7.3.3.3 ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind, lhsKind, labelSet [ , iteratorKind ] )

The abstract operation ForIn/OfBodyEvaluation takes arguments lhs, stmt, iteratorRecord, iterationKind, lhsKind (either assignment, varBinding or lexicalBinding), and labelSet and optional argument iteratorKind (either sync or async). It performs the following steps when called:

  1. If iteratorKind is not present, set iteratorKind to sync.
  2. Let oldEnv be the running execution context's LexicalEnvironment.
  3. Let V be undefined.
  4. Let destructuring be IsDestructuring of lhs.
  5. If destructuring is true and if lhsKind is assignment, then
    1. Assert: lhs is a LeftHandSideExpression.
    2. Let assignmentPattern be the AssignmentPattern that is covered by lhs.
  6. Repeat,
    1. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
    2. If iteratorKind is async, set nextResult to ? Await(nextResult).
    3. If Type(nextResult) is not Object, throw a TypeError exception.
    4. Let done be ? IteratorComplete(nextResult).
    5. If done is true, return NormalCompletion(V).
    6. Let nextValue be ? IteratorValue(nextResult).
    7. If lhsKind is either assignment or varBinding, then
      1. If destructuring is false, then
        1. Let lhsRef be the result of evaluating lhs. (It may be evaluated repeatedly.)
      2. Let iterationEnv be undefined.
    8. Else,
      1. Assert: lhsKind is lexicalBinding.
      2. Assert: lhs is a ForDeclaration.
      3. Let iterationEnv be NewDeclarativeEnvironment(oldEnv).
      4. Perform ForDeclarationBindingInstantiation for lhs passing iterationEnv as the argument.
      5. Set the running execution context's LexicalEnvironment to iterationEnv.
      6. If destructuring is false, then
        1. Assert: lhs binds a single name.
        2. Let lhsName be the sole element of BoundNames of lhs.
        3. Let lhsRef be ! ResolveBinding(lhsName).
    9. If destructuring is false, then
      1. If lhsRef is an abrupt completion, then
        1. Let status be lhsRef.
      2. Else if lhsKind is lexicalBinding, then
        1. Let status be InitializeReferencedBinding(lhsRef, nextValue).
      3. Else,
        1. Let status be PutValue(lhsRef, nextValue).
    10. Else,
      1. If lhsKind is assignment, then
        1. Let status be DestructuringAssignmentEvaluation of assignmentPattern with argument nextValue.
      2. Else if lhsKind is varBinding, then
        1. Assert: lhs is a ForBinding.
        2. Let status be BindingInitialization of lhs with arguments nextValue and undefined.
      3. Else,
        1. Assert: lhsKind is lexicalBinding.
        2. Assert: lhs is a ForDeclaration.
        3. Let status be ForDeclarationBindingInitialization of lhs with arguments nextValue and iterationEnv.
    11. If status is an abrupt completion, then
      1. Set status to be DisposeResources(iterationEnv, status)
      2. Set the running execution context's LexicalEnvironment to oldEnv.
      3. If iteratorKind is async, return ? AsyncIteratorClose(iteratorRecord, status).
      4. If iterationKind is enumerate, then
        1. Return status.
      5. Else,
        1. Assert: iterationKind is iterate.
        2. Return ? IteratorClose(iteratorRecord, status).
    12. Let result be the result of evaluating stmt.
    13. Set result to be DisposeResources(iterationEnv, result).
    14. Set the running execution context's LexicalEnvironment to oldEnv.
    15. If LoopContinues(result, labelSet) is false, then
      1. If iterationKind is enumerate, then
        1. Return Completion(UpdateEmpty(result, V)).
      2. Else,
        1. Assert: iterationKind is iterate.
        2. Set status to UpdateEmpty(result, V).
        3. If iteratorKind is async, return ? AsyncIteratorClose(iteratorRecord, status).
        4. Return ? IteratorClose(iteratorRecord, status).
    16. If result.[[Value]] is not empty, set V to result.[[Value]].

7.4 The switch Statement

7.4.1 Runtime Semantics: Evaluation

SwitchStatement : switch ( Expression ) CaseBlock
  1. Let exprRef be the result of evaluating Expression.
  2. Let switchValue be ? GetValue(exprRef).
  3. Let oldEnv be the running execution context's LexicalEnvironment.
  4. Let blockEnv be NewDeclarativeEnvironment(oldEnv).
  5. Perform BlockDeclarationInstantiation(CaseBlock, blockEnv).
  6. Set the running execution context's LexicalEnvironment to blockEnv.
  7. Let R be CaseBlockEvaluation of CaseBlock with argument switchValue.
  8. Let env be blockEnv's LexicalEnvironment.
  9. Set result to DisposeResources(env, result).
  10. Set the running execution context's LexicalEnvironment to oldEnv.
  11. Return R.
Note

No matter how control leaves the SwitchStatement the LexicalEnvironment is always restored to its former state.

8 ECMAScript Language: Functions and Classes

8.1 Function Definitions

8.1.1 Runtime Semantics: EvaluateFunctionBody

With parameters functionObject and argumentsList (a List).

FunctionBody : FunctionStatementList
  1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList).
  2. Return the result of evaluating FunctionStatementList.
  3. Let result be result of evaluating FunctionStatementList.
  4. Let env be the running execution context's LexicalEnvironment.
  5. Return DisposeResources(env, result).

8.2 Arrow Function Definitions

8.2.1 Runtime Semantics: EvaluateConciseBody

With parameters functionObject and argumentsList (a List).

ConciseBody : ExpressionBody
  1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList).
  2. Return the result of evaluating ExpressionBody.
  3. Let result be result of evaluating ExpressionBody.
  4. Let env be the running execution context's LexicalEnvironment.
  5. Return DisposeResources(env, result).

8.3 Class Definitions

8.3.1 Runtime Semantics: EvaluateClassStaticBlockBody

With parameter functionObject.

ClassStaticBlockBody : ClassStaticBlockStatementList
  1. Perform ? FunctionDeclarationInstantiation(functionObject, « »).
  2. Return the result of evaluating ClassStaticBlockStatementList.
  3. Let result be result of evaluating ClassStaticBlockStatementList.
  4. Let env be the running execution context's LexicalEnvironment.
  5. Return DisposeResources(env, result).

8.4 Tail Position Calls

8.4.1 Static Semantics: IsInTailPosition ( call )

The abstract operation IsInTailPosition takes argument call (a Parse Node). It performs the following steps when called:

  1. If the source code matching call is non-strict code, return false.
  2. If call is not contained within a FunctionBody, ConciseBody, or AsyncConciseBody, return false.
  3. Let body be the FunctionBody, ConciseBody, or AsyncConciseBody that most closely contains call.
  4. If body is the FunctionBody of a GeneratorBody, return false.
  5. If body is the FunctionBody of an AsyncFunctionBody, return false.
  6. If body is the FunctionBody of an AsyncGeneratorBody, return false.
  7. If body is an AsyncConciseBody, return false.
  8. Return the result ofLet has be HasCallInTailPosition of body with argument call.
  9. If has is found, return true.
  10. Otherwise, return false.
Note

Tail Position calls are only defined in strict mode code because of a common non-standard language extension (see 9.2.4) that enables observation of the chain of caller contexts.

8.4.2 Static Semantics: HasCallInTailPosition

With parameter call.

Note

call is a Parse Node that represents a specific range of source text. When the following algorithms compare call to another Parse Node, it is a test of whether they represent the same source text.

8.4.2.1 Statement Rules

StatementList : StatementList StatementListItem
  1. Let has be HasCallInTailPosition of StatementList with argument call.
  2. If has is truefound, return truefound.
  3. If has is contains-using-const, return contains-using-const.
  4. Return HasCallInTailPosition of StatementListItem with argument call.
Note 1
Draft Note: We allow contains-using-const to propagate out of a StatementList so that we can check whether a CaseBlock contains a UsingConst in one of its clauses. We also stop immediately upon encountering contains-using-const as any UsingConst in the statement list means that there are additional evaluation steps that will occur following a nested ReturnStatement.
FunctionStatementList : [empty] StatementListItem : Declaration Declaration : HoistableDeclaration ClassDeclaration LexicalDeclaration : LetOrConst BindingList ; Statement : VariableStatement EmptyStatement ExpressionStatement ContinueStatement BreakStatement ThrowStatement DebuggerStatement Block : { } ReturnStatement : return ; LabelledItem : FunctionDeclaration ForInOfStatement : for ( LeftHandSideExpression of AssignmentExpression ) Statement for ( var ForBinding of AssignmentExpression ) Statement for ( ForDeclaration of AssignmentExpression ) Statement CaseBlock : { }
  1. Return falsenot-found.
LexicalDeclaration : UsingConst BindingList ;
  1. Return contains-using-const.
Block : StatementList
  1. Let has be HasCallInTailPosition of StatementList with argument call.
  2. If has is found, return found.
  3. Return not-found.
Note 2
Draft Note: We do not propagate contains-using-const here as a Block is an explicit block scope. Any nested UsingConst declarations will have already been disposed before execution continues. We also stop immediately upon encountering contains-using-const as any UsingConst in the statement list means that there are additional evaluation steps that will occur following a nested ReturnStatement.
IfStatement : if ( Expression ) Statement else Statement
  1. Let has be HasCallInTailPosition of the first Statement with argument call.
  2. If has is truefound, return truefound.
  3. Assert: has is not-found.
  4. ReturnSet has to HasCallInTailPosition of the second Statement with argument call.
  5. If has is found, return found.
  6. Assert: has is not-found.
  7. Return not-found.
IfStatement : if ( Expression ) Statement DoWhileStatement : do Statement while ( Expression ) ; WhileStatement : while ( Expression ) Statement ForStatement : for ( Expressionopt ; Expressionopt ; Expressionopt ) Statement for ( var VariableDeclarationList ; Expressionopt ; Expressionopt ) Statement for ( LexicalDeclaration Expressionopt ; Expressionopt ) Statement ForInOfStatement : for ( LeftHandSideExpression in Expression ) Statement for ( var ForBinding in Expression ) Statement for ( ForDeclaration in Expression ) Statement WithStatement : with ( Expression ) Statement
  1. Return HasCallInTailPosition of Statement with argument call.
LabelledStatement : LabelIdentifier : LabelledItem
  1. Return HasCallInTailPosition of LabelledItem with argument call.
ReturnStatement : return Expression ;
  1. Return HasCallInTailPosition of Expression with argument call.
SwitchStatement : switch ( Expression ) CaseBlock
  1. Return HasCallInTailPosition of CaseBlock with argument call.
CaseBlock : { CaseClausesopt }
  1. Let has be not-found.
  2. If CaseClauses is present, set has to HasCallInTailPosition of the first CaseClauses with argument call.
  3. If has is contains-using-const, return not-found.
  4. Return has.
CaseBlock : { CaseClausesopt DefaultClause CaseClausesopt }
  1. Let hasfirstHas be falsenot-found.
  2. If the first CaseClauses is present, let has beset firstHas to HasCallInTailPosition of the first CaseClauses with argument call.
  3. If has is true, return true.
  4. If firstHas is contains-using-const, return not-found.
  5. Let hasdefaultHas be HasCallInTailPosition of DefaultClause with argument call.
  6. If has is true, return true.
  7. If defaultHas is contains-using-const, return not-found.
  8. Let secondHas be not-found.
  9. If the second CaseClauses is present, let has beset secondHas to HasCallInTailPosition of the second CaseClauses with argument call.
  10. If secondHas is contains-using-const, return not-found.
  11. If firstHas is found, return found.
  12. If defaultHas is found, return found.
  13. If secondHas is found, return found.
  14. Return hasnot-found.
Note 3
Draft Note: We do not propagate contains-using-const here as a CaseBlock is an explicit block scope. Any nested UsingConst declarations will have already been disposed before execution continues.
CaseClauses : CaseClauses CaseClause
  1. Let has be HasCallInTailPosition of CaseClauses with argument call.
  2. If has is truefound, return truefound.
  3. If has is contains-using-const, return contains-using-const.
  4. Return HasCallInTailPosition of CaseClause with argument call.
CaseClause : case Expression : StatementListopt DefaultClause : default : StatementListopt
  1. If StatementList is present, return HasCallInTailPosition of StatementList with argument call.
TryStatement : try Block Catch
  1. Return HasCallInTailPosition of Catch with argument call.
TryStatement : try Block Finally TryStatement : try Block Catch Finally
  1. Return HasCallInTailPosition of Finally with argument call.
Catch : catch ( CatchParameter ) Block
  1. Return HasCallInTailPosition of Block with argument call.

8.4.2.2 Expression Rules

Note

A potential tail position call that is immediately followed by return GetValue of the call result is also a possible tail position call. A function call cannot return a Reference Record, so such a GetValue operation will always return the same value as the actual function call result.

AssignmentExpression : YieldExpression ArrowFunction AsyncArrowFunction LeftHandSideExpression = AssignmentExpression LeftHandSideExpression AssignmentOperator AssignmentExpression LeftHandSideExpression &&= AssignmentExpression LeftHandSideExpression ||= AssignmentExpression LeftHandSideExpression ??= AssignmentExpression BitwiseANDExpression : BitwiseANDExpression & EqualityExpression BitwiseXORExpression : BitwiseXORExpression ^ BitwiseANDExpression BitwiseORExpression : BitwiseORExpression | BitwiseXORExpression EqualityExpression : EqualityExpression == RelationalExpression EqualityExpression != RelationalExpression EqualityExpression === RelationalExpression EqualityExpression !== RelationalExpression RelationalExpression : RelationalExpression < ShiftExpression RelationalExpression > ShiftExpression RelationalExpression <= ShiftExpression RelationalExpression >= ShiftExpression RelationalExpression instanceof ShiftExpression RelationalExpression in ShiftExpression PrivateIdentifier in ShiftExpression ShiftExpression : ShiftExpression << AdditiveExpression ShiftExpression >> AdditiveExpression ShiftExpression >>> AdditiveExpression AdditiveExpression : AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression MultiplicativeExpression : MultiplicativeExpression MultiplicativeOperator ExponentiationExpression ExponentiationExpression : UpdateExpression ** ExponentiationExpression UpdateExpression : LeftHandSideExpression ++ LeftHandSideExpression -- ++ UnaryExpression -- UnaryExpression UnaryExpression : delete UnaryExpression void UnaryExpression typeof UnaryExpression + UnaryExpression - UnaryExpression ~ UnaryExpression ! UnaryExpression AwaitExpression CallExpression : SuperCall CallExpression [ Expression ] CallExpression . IdentifierName CallExpression . PrivateIdentifier NewExpression : new NewExpression MemberExpression : MemberExpression [ Expression ] MemberExpression . IdentifierName SuperProperty MetaProperty new MemberExpression Arguments MemberExpression . PrivateIdentifier PrimaryExpression : this IdentifierReference Literal ArrayLiteral ObjectLiteral FunctionExpression ClassExpression GeneratorExpression AsyncFunctionExpression AsyncGeneratorExpression RegularExpressionLiteral TemplateLiteral
  1. Return falsenot-found.
Expression : AssignmentExpression Expression , AssignmentExpression
  1. Return HasCallInTailPosition of AssignmentExpression with argument call.
ConditionalExpression : ShortCircuitExpression ? AssignmentExpression : AssignmentExpression
  1. Let has be HasCallInTailPosition of the first AssignmentExpression with argument call.
  2. If has is truefound, return truefound.
  3. Return HasCallInTailPosition of the second AssignmentExpression with argument call.
LogicalANDExpression : LogicalANDExpression && BitwiseORExpression
  1. Return HasCallInTailPosition of BitwiseORExpression with argument call.
LogicalORExpression : LogicalORExpression || LogicalANDExpression
  1. Return HasCallInTailPosition of LogicalANDExpression with argument call.
CoalesceExpression : CoalesceExpressionHead ?? BitwiseORExpression
  1. Return HasCallInTailPosition of BitwiseORExpression with argument call.
CallExpression : CoverCallExpressionAndAsyncArrowHead CallExpression Arguments CallExpression TemplateLiteral
  1. If this CallExpression is call, return truefound.
  2. Return falsenot-found.
OptionalExpression : MemberExpression OptionalChain CallExpression OptionalChain OptionalExpression OptionalChain
  1. Return HasCallInTailPosition of OptionalChain with argument call.
OptionalChain : ?. [ Expression ] ?. IdentifierName ?. PrivateIdentifier OptionalChain [ Expression ] OptionalChain . IdentifierName OptionalChain . PrivateIdentifier
  1. Return falsenot-found.
OptionalChain : ?. Arguments OptionalChain Arguments
  1. If this OptionalChain is call, return truefound.
  2. Return falsenot-found.
MemberExpression : MemberExpression TemplateLiteral
  1. If this MemberExpression is call, return truefound.
  2. Return falsenot-found.
PrimaryExpression : CoverParenthesizedExpressionAndArrowParameterList
  1. Let expr be the ParenthesizedExpression that is covered by CoverParenthesizedExpressionAndArrowParameterList.
  2. Return HasCallInTailPosition of expr with argument call.
ParenthesizedExpression : ( Expression )
  1. Return HasCallInTailPosition of Expression with argument call.

9 ECMAScript Language: Scripts and Modules

9.1 Modules

9.1.1 Module Semantics

9.1.1.1 Source Text Module Records

9.1.1.1.1 ExecuteModule ( ) Concrete Method

The ExecuteModule concrete method of a Source Text Module Record module takes no arguments. It performs the following steps when called:

  1. Suspend the currently running execution context.
  2. Let moduleContext be module.[[Context]].
  3. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.
  4. Let result be the result of evaluating module.[[ECMAScriptCode]].
  5. Let env be moduleContext's LexicalEnvironment.
  6. Set result to DisposeResources(env, result).
  7. Suspend moduleContext and remove it from the execution context stack.
  8. Resume the context that is now on the top of the execution context stack as the running execution context.
  9. Return Completion(result).

9.1.1.2 Exports

Syntax

ExportDeclaration : export ExportFromClause FromClause ; export NamedExports ; export VariableStatement[~Yield, +Await] export Declaration[~Yield, +Await] export [lookahead ∉ { using }] Declaration[~Yield, +Await] export default HoistableDeclaration[~Yield, +Await, +Default] export default ClassDeclaration[~Yield, +Await, +Default] export default [lookahead ∉ { function, async [no LineTerminator here] function, class }] AssignmentExpression[+In, ~Yield, +Await] ; ExportFromClause : * * as IdentifierName NamedExports NamedExports : { } { ExportsList } { ExportsList , } ExportsList : ExportSpecifier ExportsList , ExportSpecifier ExportSpecifier : IdentifierName IdentifierName as IdentifierName

10 Control Abstraction Objects

10.1 Iteration

10.1.1 The %IteratorPrototype% Object

10.1.1.1 %IteratorPrototype% [ @@dispose ] ( )

The following steps are taken:

  1. Let O be the this value.
  2. Let return be ? GetMethod(O, "return").
  3. If return is not undefined, then
    1. Perform ? Call(return, O, « »).
  4. Return NormalCompletion(empty).

10.1.2 The %AsyncIteratorPrototype% Object

10.1.2.1 %AsyncIteratorPrototype% [ @@asyncDispose ] ( )

The following steps are taken:

  1. Let O be the this value.
  2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  3. Let return be GetMethod(O, "return").
  4. IfAbruptRejectPromise(return, promiseCapability).
  5. If return is undefined, then
    1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »).
  6. Else,
    1. Let result be Call(return, O, « undefined »).
    2. IfAbruptRejectPromise(result, promiseCapability).
  7. Return promiseCapability.[[Promise]].

10.2 Resource Management

10.2.1 Common Resource Management Interfaces

An interface is a set of property keys whose associated values match a specific specification. Any object that provides all the properties as described by an interface's specification conforms to that interface. An interface is not represented by a distinct object. There may be many separately implemented objects that conform to any interface. An individual object may conform to multiple interfaces.

10.2.1.1 The Disposable Interface

The Disposable interface includes the property described in Table 4:

Table 4: Disposable Interface Required Properties
Property Value Requirements
@@dispose A function.

Invoking this method notifies the Disposable object that the caller does not intend to continue to use this object. This method should perform any necessary logic to perform explicit clean-up of the resource including, but not limited to, file system handled, streams, host objects, etc. When an exception is thrown from this method, it typically means that the resource could not be explicitly freed.

If called more than once on the same object, the function should not throw an exception. However, this requirement is not enforced.

When using a Disposable object, it is good practice to create the instance with a using const statement, as the resource will be automatically disposed when the Block, Script, or Module containing the statement has been evaluated.

10.2.1.2 The AsyncDisposable Interface

The AsyncDisposable interface includes the property described in Table 5:

Table 5: AsyncDisposable Interface Required Properties
Property Value Requirements
@@asyncDispose A function that returns a promise.

Invoking this method notifies the AsyncDisposable object that the caller does not intend to continue to use this object. This method should perform any necessary logic to perform explicit clean-up of the resource including, but not limited to, file system handled, streams, host objects, etc. When an exception is thrown from this method, it typically means that the resource could not be explicitly freed. An AsyncDisposable object is not considered "disposed" until the resulting Promise has been fulfilled.

If called more than once on the same object, the function should not throw an exception. However, this requirement is not enforced.

When using an AsyncDisposable object, it is good practice to create the instance with a using await const statement, as the resource will be automatically disposed when the Block, Script, or Module containing the statement has been evaluated.

10.2.2 Disposable Objects

A Disposable is an object that can be used to contain one or more resources that should be disposed together.

Any Disposable object is in one of two mutually exclusive states: disposed or pending:

  • A disposable d is pending if d[Symbol.dispose]() has yet to be invoked for d.
  • A disposable d is disposed if d[Symbol.dispose]() has already been invoked once for d.

10.2.2.1 The Disposable Constructor

The Disposable constructor:

  • is %Disposable%.
  • is the initial value of the "Disposable" property of the global object.
  • creates and initializes a new Disposable when called as a constructor.
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified Disposable behaviour must include a super call to the Disposable constructor to create and initialize the subclass instance with the internal state necessary to support the Disposable and Disposable.prototype built-in methods.

10.2.2.1.1 Disposable ( onDispose )

When the Disposable function is called with argument onDispose, the following steps are taken:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. If IsCallable(onDispose) is false, throw a TypeError exception.
  3. Let disposableResource be the DisposableResource { [[ResourceValue]]: null, [[Hint]]: sync, [[DisposeMethod]]: onDispose }.
  4. Let disposable be ? OrdinaryCreateFromConstructor(NewTarget, "%Disposable.prototype%", « [[DisposableState]], [[DisposableResourceStack]] »).
  5. Set disposable.[[DisposableState]] to pending.
  6. Set disposable.[[DisposableResourceStack]] to a List containing disposableResource.
  7. Return disposable.

10.2.2.2 Properties of the Disposable Constructor

The Disposable constructor:

  • Has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:

10.2.2.2.1 Disposable.from ( iterable )

The from function returns a new disposable that can be used to dispose of each resource in the provided iterable, in reverse order. Exceptions thrown when the resources are disposed are caught and rethrown as an AggregateError.

  1. Let C be the this value.
  2. Let disposable be ? OrdinaryCreateFromConstructor(C, "%Disposable.prototype%", « [[DisposableState]], [[DisposableResourceStack]] »).
  3. Set disposable.[[DisposableState]] to pending.
  4. Set disposable.[[DisposableResourceStack]] to a new empty List.
  5. Let iteratorRecord be ? GetIterator(iterable).
  6. Let errors be a new empty List.
  7. Let next be true.
  8. Repeat, while next is not false,
    1. Set next to ? DisposeIfAbrupt(IteratorStep(iteratorRecord), disposable, errors).
    2. If next is not false, then
      1. Let nextValue be ? DisposeIfAbrupt(IteratorValue(next), disposable, errors).
      2. Let resource be AddDisposableResource(disposable, nextValue, sync).
      3. If resource.[[Type]] is throw, then
        1. Append resource.[[Value]] to errors.
  9. If errors is not empty, then
    1. let result be DisposeResources(disposable, NormalCompletion(undefined), errors).
    2. Assert: result.[[Type]] is throw. !. Return result.
  10. Return disposable.
Note

Errors that occur during the calls to IteratorStep or IteratorValue result in immediate termination of iteration and all collected resources are immediately disposed.

Errors that occur during the call to AddDisposableResource are collected and iteration continues until completion. At the end of iteration, all collected resources are disposed and an exception is thrown.

10.2.2.3 Properties of the Disposable Prototype Object

The Disposable prototype object:

  • is %Disposable.prototype%.
  • has a [[Prototype]] internal slot whose value is %Object.prototype%.
  • is an ordinary object.
  • does not have a [[DisposableState]] internal slot or any of the other internal slots of Disposable instances.

10.2.2.3.1 Disposable.prototype [ @@dispose ] ()

When the @@dispose method is called, the following steps are taken:

  1. Let disposable be the this value.
  2. If disposable does not have a [[DisposableState]] internal slot, throw a TypeError exception.
  3. If disposable.[[DisposableState]] is disposed, return undefined.
  4. Set disposable.[[DisposableState]] to disposed.
  5. Return DisposeResources(disposable, NormalCompletion(undefined)).

10.2.2.3.2 Disposable.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "Disposable".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

10.2.2.4 Properties of Disposable Instances

Disposable instances are ordinary objects that inherit properties from the Disposable prototype object (the intrinsic %Disposable.prototype%). Disposable instances are initially created with internal slots described in Table 6.

Table 6: Internal Slots of Disposable Instances
Internal Slot Description
[[DisposableState]] One of pending or disposed. Governs how a disposable will react to incoming calls to its @@dispose method.
[[DisposableResourceStack]] A List of DisposableResource records.

10.2.3 AsyncDisposable Objects

An AsyncDisposable is an object that can be used to contain one or more resources that should be disposed together.

Any AsyncDisposable object is in one of two mutually exclusive states: disposed or pending:

  • An async-disposable d is pending if d[Symbol.asyncDispose]() has yet to be invoked for d.
  • An async-disposable d is disposed if d[Symbol.asyncDispose]() has already been invoked once for d.

10.2.3.1 The AsyncDisposable Constructor

The AsyncDisposable constructor:

  • is %AsyncDisposable%.
  • is the initial value of the "AsyncDisposable" property of the global object.
  • creates and initializes a new AsyncDisposable when called as a constructor.
  • is not intended to be called as a function and will throw an exception when called in that manner.
  • may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified AsyncDisposable behaviour must include a super call to the AsyncDisposable constructor to create and initialize the subclass instance with the internal state necessary to support the AsyncDisposable and AsyncDisposable.prototype built-in methods.

10.2.3.1.1 AsyncDisposable ( onAsyncDispose )

When the AsyncDisposable function is called with argument onAsyncDispose, the following steps are taken:

  1. If NewTarget is undefined, throw a TypeError exception.
  2. If IsCallable(onAsyncDispose) is false, throw a TypeError exception.
  3. Let disposableRec be the DisposableResource { [[ResourceValue]]: null, [[Hint]]: async, [[DisposeMethod]]: onAsyncDispose }.
  4. Let disposable be ? OrdinaryCreateFromConstructor(NewTarget, "%Disposable.prototype%", « [[DisposableState]], [[DisposableResourceStack]] »).
  5. Set disposable.[[DisposableState]] to pending.
  6. Set disposable.[[DisposableResourceStack]] to a List containing disposableRec.
  7. Return disposable.

10.2.3.2 Properties of the AsyncDisposable Constructor

The AsyncDisposable constructor:

  • Has a [[Prototype]] internal slot whose value is %Function.prototype%.
  • has the following properties:

10.2.3.2.1 AsyncDisposable.from ( iterable )

The from function returns a promise for a new async disposable that can be used to dispose of each resource in the provided iterable, in reverse order. Exceptions thrown when the resources are disposed are caught and rethrown as an AggregateError.

  1. Let C be the this value.
  2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  3. Let asyncDisposable be ? OrdinaryCreateFromConstructor(C, "%Disposable.prototype%", « [[DisposableState]], [[DisposableResourceStack]] »).
  4. Set asyncDisposable.[[DisposableState]] to pending.
  5. Set asyncDisposable.[[DisposableResourceStack]] to a new empty List.
  6. Let iteratorRecord be GetIterator(iterable).
  7. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
  8. Let errors be a new empty List.
  9. Let next be true.
  10. Repeat, while next is not false,
    1. Set next to DisposeIfAbrupt(IteratorStep(iteratorRecord), asyncDisposable).
    2. IfAbruptRejectPromise(next, promiseCapability).
    3. If next is not false, then
      1. Let nextValue be DisposeIfAbrupt(IteratorValue(next), asyncDisposable).
      2. IfAbruptRejectPromise(nextValue, promiseCapability).
      3. Let resource be AddDisposableResource(asyncDisposable, nextValue, sync).
      4. If resource.[[Type]] is throw, then
        1. Append resource.[[Value]] to errors.
  11. If errors is not empty, then
    1. Let result be DisposeResources(asyncDisposable, NormalCompletion(undefined), errors).
    2. Assert: result.[[Type]] is throw.
    3. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).
    4. Return promiseCapability.[[Promise]].
  12. Otherwise,
    1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « asyncDisposable »).
    2. Return promiseCapability.[[Promise]].
Note

Errors that occur during the calls to IteratorStep or IteratorValue result in immediate termination of iteration and all collected resources are immediately disposed.

Errors that occur during the call to AddDisposableResource are collected and iteration continues until completion. At the end of iteration, all collected resources are disposed and an exception is thrown.

10.2.3.3 Properties of the AsyncDisposable Prototype Object

The AsyncDisposable prototype object:

  • is %AsyncDisposable.prototype%.
  • has a [[Prototype]] internal slot whose value is %Object.prototype%.
  • is an ordinary object.
  • does not have a [[DisposableState]] internal slot or any of the other internal slots of AsyncDisposable instances.

10.2.3.3.1 AsyncDisposable.prototype [ @@asyncDispose ] ()

When the @@asyncDispose method is called, the following steps are taken:

  1. Let asyncDisposable be the this value.
  2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  3. If asyncDisposable does not have a [[DisposableState]] internal slot, then
    1. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
    2. Return promiseCapability.[[Promise]].
  4. If asyncDisposable.[[DisposableState]] is disposed, then
    1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « *undefined »).
    2. Return promiseCapability.[[Promise]].
  5. Set asyncDisposable.[[DisposableState]] to disposed.
  6. Let result be DisposeResources(asyncDisposable, NormalCompletion(undefined)).
  7. IfAbruptRejectPromise(result, promiseCapability).
  8. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result »).
  9. Return promiseCapability.[[Promise]].

10.2.3.3.2 AsyncDisposable.prototype [ @@toStringTag ]

The initial value of the @@toStringTag property is the String value "AsyncDisposable".

This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

10.2.3.4 Properties of AsyncDisposable Instances

AsyncDisposable instances are ordinary objects that inherit properties from the AsyncDisposable prototype object (the intrinsic %AsyncDisposable.prototype%). AsyncDisposable instances are initially created with internal slots described in Table 7.

Table 7: Internal Slots of AsyncDisposable Instances
Internal Slot Description
[[DisposableState]] One of pending or disposed. Governs how a disposable will react to incoming calls to its @@dispose method.
[[DisposableResourceStack]] A List of DisposableResource records.

10.3 Generator Objects

10.3.1 Generator Abstract Operations

10.3.1.1 GeneratorStart ( generator, generatorBody )

The abstract operation GeneratorStart takes arguments generator and generatorBody (a Parse Node or an Abstract Closure with no parameters). It performs the following steps when called:

  1. Assert: The value of generator.[[GeneratorState]] is undefined.
  2. Let genContext be the running execution context.
  3. Set the Generator component of genContext to generator.
  4. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context the following steps will be performed:
    1. If generatorBody is a Parse Node, then
      1. Let result be the result of evaluating generatorBody.
    2. Else,
      1. Assert: generatorBody is an Abstract Closure with no parameters.
      2. Let result be generatorBody().
    3. Assert: If we return here, the generator either threw an exception or performed either an implicit or explicit return.
    4. Remove genContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
    5. Set generator.[[GeneratorState]] to completed.
    6. Once a generator enters the completed state it never leaves it and its associated execution context is never resumed. Any execution state associated with generator can be discarded at this point.
    7. Let env be genContext's LexicalEnvironment.
    8. Set result to DisposeResources(env, result).
    9. If result.[[Type]] is normal, let resultValue be undefined.
    10. Else if result.[[Type]] is return, let resultValue be result.[[Value]].
    11. Else,
      1. Assert: result.[[Type]] is throw.
      2. Return Completion(result).
    12. Return CreateIterResultObject(resultValue, true).
  5. Set generator.[[GeneratorContext]] to genContext.
  6. Set generator.[[GeneratorState]] to suspendedStart.
  7. Return NormalCompletion(undefined).

10.4 AsyncGenerator Objects

10.4.1 AsyncGenerator Abstract Operations

10.4.1.1 AsyncGeneratorStart ( generator, generatorBody )

The abstract operation AsyncGeneratorStart takes arguments generator and generatorBody (a Parse Node or an Abstract Closure with no parameters). It performs the following steps when called:

  1. Assert: generator is an AsyncGenerator instance.
  2. Assert: generator.[[AsyncGeneratorState]] is undefined.
  3. Let genContext be the running execution context.
  4. Set the Generator component of genContext to generator.
  5. Set the code evaluation state of genContext such that when evaluation is resumed for that execution context the following steps will be performed:
    1. If generatorBody is a Parse Node, then
      1. Let result be the result of evaluating generatorBody.
    2. Else,
      1. Assert: generatorBody is an Abstract Closure with no parameters.
      2. Let result be generatorBody().
    3. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return.
    4. Remove genContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
    5. Set generator.[[AsyncGeneratorState]] to completed.
    6. Let env be genContext's LexicalEnvironment.
    7. Set result to DisposeResources(env, result).
    8. If result is a normal completion, let resultValue be undefined.
    9. Else,
      1. Let resultValue be result.[[Value]].
      2. If result.[[Type]] is not return, then
        1. Return ! AsyncGeneratorReject(generator, resultValue).
    10. Return ! AsyncGeneratorResolve(generator, resultValue, true).
  6. Set generator.[[AsyncGeneratorContext]] to genContext.
  7. Set generator.[[AsyncGeneratorState]] to suspendedStart.
  8. Set generator.[[AsyncGeneratorQueue]] to a new empty List.
  9. Return undefined.

10.5 AsyncFunction Objects

10.5.1 Async Functions Abstract Operations

10.5.1.1 AsyncFunctionStart ( promiseCapability, asyncFunctionBody )

The abstract operation AsyncFunctionStart takes arguments promiseCapability (a PromiseCapability Record) and asyncFunctionBody. It performs the following steps when called:

  1. Let runningContext be the running execution context.
  2. Let asyncContext be a copy of runningContext.
  3. NOTE: Copying the execution state is required for the step below to resume its execution. It is ill-defined to resume a currently executing context.
  4. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed:
    1. Let result be the result of evaluating asyncFunctionBody.
    2. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done.
    3. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
    4. Let env be asyncContext's LexicalEnvironment.
    5. Set result to DisposeResources(env, result).
    6. If result.[[Type]] is normal, then
      1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »).
    7. Else if result.[[Type]] is return, then
      1. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »).
    8. Else,
      1. Assert: result.[[Type]] is throw.
      2. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).
    9. Return.
  5. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
  6. Resume the suspended evaluation of asyncContext. Let result be the value returned by the resumed computation.
  7. Assert: When we return here, asyncContext has already been removed from the execution context stack and runningContext is the currently running execution context.
  8. Assert: result is a normal completion with a value of undefined. The possible sources of completion values are Await or, if the async function doesn't await anything, step 4.i above.
  9. Return.

A Copyright & Software License

Copyright Notice

© 2021 Ron Buckton, Ecma International

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.