From 87da6b0e023fe5a91d118552db8da0b311d0584e Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Wed, 14 May 2025 19:15:03 -0700 Subject: [PATCH 1/4] Editorial: have EvaluateBody return normal, not return, completions --- spec.html | 54 +++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/spec.html b/spec.html index adf13813ed..adbd14fc5f 100644 --- a/spec.html +++ b/spec.html @@ -13449,7 +13449,7 @@

1. Perform OrdinaryCallBindThis(_F_, _calleeContext_, _thisArgument_). 1. Let _result_ be Completion(OrdinaryCallEvaluateBody(_F_, _argumentsList_)). 1. [id="step-call-pop-context-stack"] Remove _calleeContext_ from the execution context stack and restore _callerContext_ as the running execution context. - 1. If _result_ is a return completion, return _result_.[[Value]]. + 1. If _result_ is a normal completion, return _result_.[[Value]]. 1. Assert: _result_ is a throw completion. 1. Return ? _result_. @@ -13521,7 +13521,7 @@

Runtime Semantics: EvaluateBody ( _functionObject_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a return completion or a throw completion + ): a normal completion or a throw completion

@@ -13547,13 +13547,13 @@

AsyncFunctionBody : FunctionBody - 1. Return ? EvaluateAsyncFunctionBody of |AsyncFunctionBody| with arguments _functionObject_ and _argumentsList_. + 1. Return EvaluateAsyncFunctionBody of |AsyncFunctionBody| with arguments _functionObject_ and _argumentsList_. AsyncConciseBody : ExpressionBody - 1. Return ? EvaluateAsyncConciseBody of |AsyncConciseBody| with arguments _functionObject_ and _argumentsList_. + 1. Return EvaluateAsyncConciseBody of |AsyncConciseBody| with arguments _functionObject_ and _argumentsList_. Initializer : @@ -13563,11 +13563,10 @@

1. Assert: _argumentsList_ is empty. 1. Assert: _functionObject_.[[ClassFieldInitializerName]] is not ~empty~. 1. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true*, then - 1. Let _value_ be ? NamedEvaluation of |Initializer| with argument _functionObject_.[[ClassFieldInitializerName]]. + 1. Return ? NamedEvaluation of |Initializer| with argument _functionObject_.[[ClassFieldInitializerName]]. 1. Else, 1. Let _rhs_ be ? Evaluation of |AssignmentExpression|. - 1. Let _value_ be ? GetValue(_rhs_). - 1. Return ReturnCompletion(_value_). + 1. Return ? GetValue(_rhs_).

Even though field initializers constitute a function boundary, calling FunctionDeclarationInstantiation does not have any observable effect and so is omitted.

@@ -13586,7 +13585,7 @@

OrdinaryCallEvaluateBody ( _F_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a return completion or a throw completion + ): a normal completion or a throw completion

@@ -13625,7 +13624,7 @@

1. Remove _calleeContext_ from the execution context stack and restore _callerContext_ as the running execution context. 1. If _result_ is a throw completion, then 1. Return ? _result_. - 1. Assert: _result_ is a return completion. + 1. Assert: _result_ is a normal completion. 1. If _result_.[[Value]] is an Object, return _result_.[[Value]]. 1. If _kind_ is ~base~, return _thisArgument_. 1. If _result_.[[Value]] is not *undefined*, throw a *TypeError* exception. @@ -23704,16 +23703,22 @@

Runtime Semantics: EvaluateFunctionBody ( _functionObject_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a return completion or a throw completion + ): a normal completion or a throw completion

FunctionBody : FunctionStatementList 1. Perform ? FunctionDeclarationInstantiation(_functionObject_, _argumentsList_). - 1. Perform ? Evaluation of |FunctionStatementList|. + 1. Let _result_ be Completion(Evaluation of |FunctionStatementList|). 1. NOTE: If the previous step resulted in a normal completion, then evaluation finished by proceeding past the end of the |FunctionStatementList|. - 1. Return ReturnCompletion(*undefined*). + 1. If _result_ is a normal completion, then + 1. Return *undefined*. + 1. Else if _result_ is a return completion, then + 1. Return _result_.[[Value]]. + 1. Else, + 1. Assert: _result_ is a throw completion. + 1. Return ? _result_. @@ -23889,7 +23894,7 @@

Runtime Semantics: EvaluateConciseBody ( _functionObject_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a return completion or a throw completion + ): a normal completion or a throw completion

@@ -23932,8 +23937,7 @@

Runtime Semantics: Evaluation

ExpressionBody : AssignmentExpression 1. Let _exprRef_ be ? Evaluation of |AssignmentExpression|. - 1. Let _exprValue_ be ? GetValue(_exprRef_). - 1. Return ReturnCompletion(_exprValue_). + 1. Return ? GetValue(_exprRef_). @@ -24247,7 +24251,7 @@

Runtime Semantics: EvaluateGeneratorBody ( _functionObject_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a throw completion or a return completion + ): a normal completion or a return completion

@@ -24258,7 +24262,7 @@

1. Set _G_.[[GeneratorBrand]] to ~empty~. 1. Set _G_.[[GeneratorState]] to ~suspended-start~. 1. Perform GeneratorStart(_G_, |FunctionBody|). - 1. Return ReturnCompletion(_G_). + 1. Return _G_. @@ -24474,7 +24478,7 @@

Runtime Semantics: EvaluateAsyncGeneratorBody ( _functionObject_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a throw completion or a return completion + ): a normal completion or a throw completion

@@ -24487,7 +24491,7 @@

1. Set _generator_.[[GeneratorBrand]] to ~empty~. 1. Set _generator_.[[AsyncGeneratorState]] to ~suspended-start~. 1. Perform AsyncGeneratorStart(_generator_, |FunctionBody|). - 1. Return ReturnCompletion(_generator_). + 1. Return _generator_. @@ -25096,7 +25100,7 @@

Runtime Semantics: EvaluateClassStaticBlockBody ( _functionObject_: an ECMAScript function object, - ): a return completion or a throw completion + ): a normal completion or a throw completion

@@ -25105,7 +25109,7 @@

1. Assert: _functionObject_ is a synthetic function created by ClassStaticBlockDefinitionEvaluation step . 1. Perform ! FunctionDeclarationInstantiation(_functionObject_, « »). 1. Perform ? Evaluation of |ClassStaticBlockStatementList|. - 1. Return ReturnCompletion(*undefined*). + 1. Return *undefined*. @@ -25490,7 +25494,7 @@

Runtime Semantics: EvaluateAsyncFunctionBody ( _functionObject_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a return completion + ): a Promise

@@ -25504,7 +25508,7 @@

1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _completion_.[[Value]] »). 1. Else, 1. Perform AsyncFunctionStart(_promiseCapability_, |FunctionBody|). - 1. Return ReturnCompletion(_promiseCapability_.[[Promise]]). + 1. Return _promiseCapability_.[[Promise]]. @@ -25597,7 +25601,7 @@

Runtime Semantics: EvaluateAsyncConciseBody ( _functionObject_: an ECMAScript function object, _argumentsList_: a List of ECMAScript language values, - ): a return completion + ): a Promise

@@ -25611,7 +25615,7 @@

1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _completion_.[[Value]] »). 1. Else, 1. Perform AsyncFunctionStart(_promiseCapability_, |ExpressionBody|). - 1. Return ReturnCompletion(_promiseCapability_.[[Promise]]). + 1. Return _promiseCapability_.[[Promise]]. From b71353ee1c9d94e946d5ebffe78efb8abcd22865 Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Wed, 14 May 2025 19:24:59 -0700 Subject: [PATCH 2/4] Editorial: non-syntactic async/generator functions use NormalCompletion --- spec.html | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/spec.html b/spec.html index adbd14fc5f..b025d24b5b 100644 --- a/spec.html +++ b/spec.html @@ -49810,16 +49810,22 @@

1. Let _acGenerator_ be the Generator component of _acGenContext_. 1. If _generatorBody_ is a Parse Node, then 1. Let _result_ be Completion(Evaluation of _generatorBody_). + 1. If _result_ is a normal completion, then + 1. NOTE: This implies that evaluation finished without reaching an explicit |ReturnStatement|. + 1. Set _result_ to NormalCompletion(*undefined*). + 1. Else if _result_ is a return completion, then + 1. Set _result_ to NormalCompletion(_result_.[[Value]]). + 1. Else, + 1. Assert: _result_ is a throw completion. 1. Else, 1. Assert: _generatorBody_ is an Abstract Closure with no parameters. 1. Let _result_ be Completion(_generatorBody_()). + 1. Assert: _result_ is a normal completion or a throw completion. 1. Assert: If we return here, the generator either threw an exception or performed either an implicit or explicit return. 1. Remove _acGenContext_ 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. 1. Set _acGenerator_.[[GeneratorState]] to ~completed~. 1. NOTE: Once a generator enters the ~completed~ state it never leaves it and its associated execution context is never resumed. Any execution state associated with _acGenerator_ can be discarded at this point. 1. If _result_ is a normal completion, then - 1. Let _resultValue_ be *undefined*. - 1. Else if _result_ is a return completion, then 1. Let _resultValue_ be _result_.[[Value]]. 1. Else, 1. Assert: _result_ is a throw completion. @@ -50174,14 +50180,20 @@

1. Let _acGenerator_ be the Generator component of _acGenContext_. 1. If _generatorBody_ is a Parse Node, then 1. Let _result_ be Completion(Evaluation of _generatorBody_). + 1. If _result_ is a normal completion, then + 1. NOTE: This implies that evaluation finished without reaching an explicit |ReturnStatement|. + 1. Set _result_ to NormalCompletion(*undefined*). + 1. Else if _result_ is a return completion, then + 1. Set _result_ to NormalCompletion(_result_.[[Value]]). + 1. Else, + 1. Assert: _result_ is a throw completion. 1. Else, 1. Assert: _generatorBody_ is an Abstract Closure with no parameters. 1. Let _result_ be Completion(_generatorBody_()). + 1. Assert: _result_ is a normal completion or a throw completion. 1. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return. 1. Remove _acGenContext_ 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. 1. Set _acGenerator_.[[AsyncGeneratorState]] to ~draining-queue~. - 1. If _result_ is a normal completion, set _result_ to NormalCompletion(*undefined*). - 1. If _result_ is a return completion, set _result_ to NormalCompletion(_result_.[[Value]]). 1. Perform AsyncGeneratorCompleteStep(_acGenerator_, _result_, *true*). 1. Perform AsyncGeneratorDrainQueue(_acGenerator_). 1. Return *undefined*. @@ -50565,15 +50577,21 @@

1. Let _acAsyncContext_ be the running execution context. 1. If _asyncBody_ is a Parse Node, then 1. Let _result_ be Completion(Evaluation of _asyncBody_). + 1. If _result_ is a normal completion, then + 1. NOTE: This implies that evaluation finished without reaching an explicit |ReturnStatement|. + 1. Set _result_ to NormalCompletion(*undefined*). + 1. Else if _result_ is a return completion, then + 1. Set _result_ to NormalCompletion(_result_.[[Value]]). + 1. Else, + 1. Assert: _result_ is a return completion. 1. Else, 1. Assert: _asyncBody_ is an Abstract Closure with no parameters. - 1. Let _result_ be _asyncBody_(). + 1. Let _result_ be Completion(_asyncBody_()). + 1. Assert: _result_ is a normal completion or a throw completion. 1. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. 1. Remove _acAsyncContext_ 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. 1. If _result_ is a normal completion, then - 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « *undefined* »). - 1. Else if _result_ is a return completion, then - 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _result_.[[Value]] »). + 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _result_.[[Value]] »). 1. Else, 1. Assert: _result_ is a throw completion. 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _result_.[[Value]] »). From 1438bed0bf88794e435ea745c95c51ddf43d0359 Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Fri, 23 May 2025 12:36:37 -0700 Subject: [PATCH 3/4] fixup! use NormalCompletion for built-in generators --- spec.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/spec.html b/spec.html index b025d24b5b..8d7c3f9a30 100644 --- a/spec.html +++ b/spec.html @@ -36365,7 +36365,7 @@

String.prototype [ %Symbol.iterator% ] ( )

1. Let _resultString_ be the substring of _s_ from _position_ to _nextIndex_. 1. Set _position_ to _nextIndex_. 1. Perform ? GeneratorYield(CreateIteratorResultObject(_resultString_, *false*)). - 1. Return *undefined*. + 1. Return NormalCompletion(*undefined*). 1. Return CreateIteratorFromClosure(_closure_, *"%StringIteratorPrototype%"*, %StringIteratorPrototype%).

The value of the *"name"* property of this method is *"[Symbol.iterator]"*.

@@ -43312,7 +43312,7 @@

1. Perform ? GeneratorYield(CreateIteratorResultObject(_result_, *false*)). 1. NOTE: The number of elements in _entries_ may have increased while execution of this abstract operation was paused by GeneratorYield. 1. Set _numEntries_ to the number of elements in _entries_. - 1. Return *undefined*. + 1. Return NormalCompletion(*undefined*). 1. Return CreateIteratorFromClosure(_closure_, *"%MapIteratorPrototype%"*, %MapIteratorPrototype%). @@ -43942,7 +43942,7 @@

1. Perform ? GeneratorYield(CreateIteratorResultObject(_e_, *false*)). 1. NOTE: The number of elements in _entries_ may have increased while execution of this abstract operation was paused by GeneratorYield. 1. Set _numEntries_ to the number of elements in _entries_. - 1. Return *undefined*. + 1. Return NormalCompletion(*undefined*). 1. Return CreateIteratorFromClosure(_closure_, *"%SetIteratorPrototype%"*, %SetIteratorPrototype%). @@ -47826,10 +47826,10 @@

Iterator.prototype.drop ( _limit_ )

1. If _remaining_ ≠ +∞, then 1. Set _remaining_ to _remaining_ - 1. 1. Let _next_ be ? IteratorStep(_iterated_). - 1. If _next_ is ~done~, return ReturnCompletion(*undefined*). + 1. If _next_ is ~done~, return NormalCompletion(*undefined*). 1. Repeat, 1. Let _value_ be ? IteratorStepValue(_iterated_). - 1. If _value_ is ~done~, return ReturnCompletion(*undefined*). + 1. If _value_ is ~done~, return NormalCompletion(*undefined*). 1. Let _completion_ be Completion(Yield(_value_)). 1. IfAbruptCloseIterator(_completion_, _iterated_). 1. Let _result_ be CreateIteratorFromClosure(_closure_, *"Iterator Helper"*, %IteratorHelperPrototype%, « [[UnderlyingIterator]] »). @@ -47875,7 +47875,7 @@

Iterator.prototype.filter ( _predicate_ )

1. Let _counter_ be 0. 1. Repeat, 1. Let _value_ be ? IteratorStepValue(_iterated_). - 1. If _value_ is ~done~, return ReturnCompletion(*undefined*). + 1. If _value_ is ~done~, return NormalCompletion(*undefined*). 1. Let _selected_ be Completion(Call(_predicate_, *undefined*, « _value_, 𝔽(_counter_) »)). 1. IfAbruptCloseIterator(_selected_, _iterated_). 1. If ToBoolean(_selected_) is *true*, then @@ -47925,7 +47925,7 @@

Iterator.prototype.flatMap ( _mapper_ )

1. Let _counter_ be 0. 1. Repeat, 1. Let _value_ be ? IteratorStepValue(_iterated_). - 1. If _value_ is ~done~, return ReturnCompletion(*undefined*). + 1. If _value_ is ~done~, return NormalCompletion(*undefined*). 1. Let _mapped_ be Completion(Call(_mapper_, *undefined*, « _value_, 𝔽(_counter_) »)). 1. IfAbruptCloseIterator(_mapped_, _iterated_). 1. Let _innerIterator_ be Completion(GetIteratorFlattenable(_mapped_, ~reject-primitives~)). @@ -47985,7 +47985,7 @@

Iterator.prototype.map ( _mapper_ )

1. Let _counter_ be 0. 1. Repeat, 1. Let _value_ be ? IteratorStepValue(_iterated_). - 1. If _value_ is ~done~, return ReturnCompletion(*undefined*). + 1. If _value_ is ~done~, return NormalCompletion(*undefined*). 1. Let _mapped_ be Completion(Call(_mapper_, *undefined*, « _value_, 𝔽(_counter_) »)). 1. IfAbruptCloseIterator(_mapped_, _iterated_). 1. Let _completion_ be Completion(Yield(_mapped_)). @@ -48068,11 +48068,11 @@

Iterator.prototype.take ( _limit_ )

1. Let _remaining_ be _integerLimit_. 1. Repeat, 1. If _remaining_ = 0, then - 1. Return ? IteratorClose(_iterated_, ReturnCompletion(*undefined*)). + 1. Return ? IteratorClose(_iterated_, NormalCompletion(*undefined*)). 1. If _remaining_ ≠ +∞, then 1. Set _remaining_ to _remaining_ - 1. 1. Let _value_ be ? IteratorStepValue(_iterated_). - 1. If _value_ is ~done~, return ReturnCompletion(*undefined*). + 1. If _value_ is ~done~, return NormalCompletion(*undefined*). 1. Let _completion_ be Completion(Yield(_value_)). 1. IfAbruptCloseIterator(_completion_, _iterated_). 1. Let _result_ be CreateIteratorFromClosure(_closure_, *"Iterator Helper"*, %IteratorHelperPrototype%, « [[UnderlyingIterator]] »). From e50aaf22c36883b34a64ec0ce6243a8d8239cd93 Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Fri, 23 May 2025 12:42:33 -0700 Subject: [PATCH 4/4] fixup! concise bodies still need to return ReturnCompletion AsyncBlockStart needs to get a return completion when evaluating concise async arrows so that it knows to use the value instead of *undefined*, so we need to return a return completion in the evaluation semantics for ExpressionBody and then in the non-async case unwrap that to a normal completion in EvaluateConciseBody (which is not used in the async case). --- spec.html | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spec.html b/spec.html index 8d7c3f9a30..dac852f4eb 100644 --- a/spec.html +++ b/spec.html @@ -23901,7 +23901,12 @@

ConciseBody : ExpressionBody 1. Perform ? FunctionDeclarationInstantiation(_functionObject_, _argumentsList_). - 1. Return ? Evaluation of |ExpressionBody|. + 1. Let _result_ be Completion(Evaluation of |ExpressionBody|). + 1. If _result_ is a return completion, then + 1. Return _result_.[[Value]]. + 1. Else, + 1. Assert: _result_ is a throw completion. + 1. Return ? _result_. @@ -23937,7 +23942,8 @@

Runtime Semantics: Evaluation

ExpressionBody : AssignmentExpression 1. Let _exprRef_ be ? Evaluation of |AssignmentExpression|. - 1. Return ? GetValue(_exprRef_). + 1. Let _exprValue_ be ? GetValue(_exprRef_). + 1. Return ReturnCompletion(_exprValue_).