From e422a12698508be728a421042a5f2cbd141c2b95 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Thu, 19 Sep 2024 16:07:47 -0700 Subject: [PATCH 01/15] Update to reflect upstream changes to index.bs --- document/js-api/index.bs | 225 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 210 insertions(+), 15 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 88cace6a4..39b506caa 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -36,6 +36,15 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT text: NativeError; url: sec-nativeerror-constructors text: TypeError; url: sec-native-error-types-used-in-this-standard-typeerror text: RangeError; url: sec-native-error-types-used-in-this-standard-rangeerror + type: dfn; + text: AbstractClosure; url: sec-abstract-closure + text: CreateBuiltinFunction; url: sec-createbuiltinfunction + text: PromiseCapabilityRecord; url: sec-promisecapability-records + text: EvaluateCall; url: sec-evaluatecall + text: ExecutionContext; url: sec-execution-contexts + text: IsPromise; url: sec-ispromise + text: PerformPromiseThen; url: sec-performpromisethen + text: Execution Context Stack; url: execution-context-stack type: dfn url: sec-returnifabrupt-shorthands text: ! @@ -101,6 +110,8 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df text: func_alloc; url: appendix/embedding.html#embed-func-alloc text: func_type; url: appendix/embedding.html#embed-func-type text: func_invoke; url: appendix/embedding.html#embed-func-invoke + text: evaluation_suspend; url: appendix/embedding.html#embed-evaluation-suspend + text: evaluation_resume; url: appendix/embedding.html#embed-evaluation-resume text: table_alloc; url: appendix/embedding.html#embed-table-alloc text: table_type; url: appendix/embedding.html#embed-table-type text: table_read; url: appendix/embedding.html#embed-table-read @@ -385,13 +396,18 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Let |o| be [=?=] [$Get$](|importObject|, |moduleName|). 1. If [=Type=](|o|) is not Object, throw a {{TypeError}} exception. 1. Let |v| be [=?=] [$Get$](|o|, |componentName|). - 1. If |externtype| is of the form [=external-type/func=] |functype|, - 1. If [$IsCallable$](|v|) is false, throw a {{LinkError}} exception. - 1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=], - 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. - 1. Otherwise, - 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. - 1. Let |index| be the number of external functions in |imports|. This value |index| is known as the index of the host function |funcaddr|. + 1. If |externtype| is of the form [=func=] |functype|, + 1. If [$IsCallable$](|v|) is true + 1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=], + 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. + 1. Otherwise, + 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. + 1. Otherwise, if |v| has a \[[wrappedFunction]] insternal slot + 1. Let |func| be the value of |v|'s \[[wrappedFunction]] internal slot. + 1. Assert: [$IsCallable$](|func|) is true + 1. [=Create a suspending function|create a suspending function=] from |func| and |functype|, and let |funcaddr| be the result. + 1. Otherwise, throw a {{LinkError}} exception. + 1. Let |index| be the number of external functions in |imports|. This value |index| is known as the index of the host function |funcaddr|. 1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|. 1. [=list/Append=] |externfunc| to |imports|. 1. If |externtype| is of the form [=external-type/global=] mut |valtype|, @@ -408,6 +424,10 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |valtype|, |value|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Otherwise, if |v| [=implements=] {{Global}}, + 1. Let |globaladdr| be |v|.\[[Global]]. + 1. Otherwise, + 1. Throw a {{LinkError}} exception. 1. Let |externglobal| be [=external value|global=] |globaladdr|. 1. [=list/Append=] |externglobal| to |imports|. 1. If |externtype| is of the form [=external-type/mem=] memtype, @@ -1126,12 +1146,9 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [
- To call an Exported Function with [=function address=] |funcaddr| and a [=list=] of JavaScript arguments |argValues|, perform the following steps: - - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let |functype| be [=func_type=](|store|, |funcaddr|). + To coerce JavaScript arguments from a |functype| and a [=list=] of JavaScript arguments |argValues|, perform the following steps 1. Let [|parameters|] → [|results|] be |functype|. - 1. If |parameters| or |results| contain [=v128=] or [=exnref=], throw a {{TypeError}}. + 1. If |parameters| or |results| contain [=v128=], throw a {{TypeError}}. Note: the above error is thrown each time the \[[Call]] method is invoked. 1. Let |args| be « ». @@ -1141,6 +1158,14 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ 1. Otherwise, let |arg| be undefined. 1. [=list/Append=] [=ToWebAssemblyValue=](|arg|, |t|) to |args|. 1. Set |i| to |i| + 1. + 1. return |args| +
+ +
+ To call an Exported Function with [=function address=] |funcaddr| and a [=list=] of JavaScript arguments |argValues|, perform the following steps: + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |functype| be [=func_type=](|store|, |funcaddr|). + 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|argValues|) 1. Let (|store|, |ret|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |ret| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. @@ -1169,13 +1194,23 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not
To run a host function from the JavaScript object |func|, type |functype|, and [=list=] of [=WebAssembly values=] |arguments|, perform the following steps: + 1. Let [|parameters|] → [|resultTypes|] be |functype|. + 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|) + 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). + 1. Return the result of performing [=coerce a JavaScript return=] on |resultTypes| and |ret|. +
- 1. Let [|parameters|] → [|results|] be |functype|. - 1. If |parameters| or |results| contain [=v128=] or [=exnref=], throw a {{TypeError}}. +
+ To coerce WebAssembly arguments from a [=list=] of |parameterTypes| and a [=list=] of JavaScript arguments |arguments|, perform the following steps + 1. If |parameterTypes| contain [=v128=], throw a {{TypeError}}. 1. Let |jsArguments| be « ». 1. [=list/iterate|For each=] |arg| of |arguments|, 1. [=list/Append=] [=!=] [=ToJSValue=](|arg|) to |jsArguments|. - 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). + 1. Return |jsArguments| +
+ +
+ To coerce a JavaScript return from a JavaScript |ret| and a list of |results| types, perform the following steps: 1. Let |resultsSize| be |results|'s [=list/size=]. 1. If |resultsSize| is 0, return « ». 1. Otherwise, if |resultsSize| is 1, return « [=?=] [=ToWebAssemblyValue=](|ret|, |results|[0]) ». @@ -1190,6 +1225,18 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Return |wasmValues|.
+
+ To coerce a JavaScript exception from a JavaScript exception |v|, perform the following steps: + 1. If |v| [=implements=] {{Exception}}, + 1. Let |type| be |v|.\[[Type]]. + 1. Let |payload| be |v|.\[[Payload]]. + 1. Otherwise, + 1. Let |type| be the [=JavaScript exception tag=]. + 1. Let |payload| be « ». + 1. Let |opaqueData| be [=ToWebAssemblyValue=](|v|, [=externref=]) + 1. Return the triple |type|, |payload| and |opaqueData|. +
+
To create a host function from the JavaScript object |func| and type |functype|, perform the following steps: @@ -1329,6 +1376,154 @@ The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript va
+

JavaScript Promise Integration

+ +A {{Suspending}} object represents a JavaScript function whose calls via WebAssembly imports should be *suspended* when they return a Promise object. +Each {{Suspending}} object has a \[[wrappedFunction]] internal slot which holds a JavaScript function. + +In addition, the {{promising}} function takes as argument a WebAssembly function and returns a JavaScript function that returns a Promise that is resolved when the WebAssembly function completes. + +
+[Exposed=*]
+partial namespace WebAssembly {
+    Function promising(Function wasmFunc);
+};
+
+[LegacyNamespace=WebAssembly, Exposed=*]
+interface Suspending {
+    constructor(Function jsFun);
+};
+
+ +
+ The promising(|wasmFunc|) function, when invoked, performs the following steps: + 1. If [$IsCallable$](|wasmFunc|) is false, throw a {{TypeError}}. + 1. If |wasmFunc| does not have a \[[FunctionAddress]] internal slot, throw a {{TypeError}}. + 1. Let |builder| be a new [=AbstractClosure=] with parameters that captures |wasmFunc| that, when invoked, performs [=run a Promising function=]. + 1. Returns [=CreateBuiltinFunction=](|builder|,1,"",<<>>) +
+ +
+ The algorithm to run a Promising function from the JavaScript object |wasmFunc| and a [=list=] of [=WebAssembly values=] |arguments| consists of the following steps: + 1. Let |promise| be a new [=PromiseCapabilityRecord=]. + 1. Let |funcaddr| be the value of |wasmFunc|'s \[[FunctionAddress]] internal slot. + 1. Let |runner| be a new [=AbstractClosure=] of no arguments that captures |promise|, |funcaddr|, |arguments| that, when invoked, performs the following steps: + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |functype| be [=func_type=](|store|, |funcaddr|). + 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|arguments|) + 1. Let (|store|, |result|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). + 1. Assert: if control reaches here, we have done waiting for suspended imports + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. If |result| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. + 1. Otherwise, if |result| is of the form [=throw=] exnaddr, + 1. Perform [=EvaluateCall=] (|promise|.\[[Reject]],|result|.,false). + 1. Return UNDETERMINED + 1. Otherwise, assert |result| is a [=list=] of WebAssembly values + 1. Let |outArity| be the [=list/size=] of |result|. + 1. If |outArity| is 0, return undefined. + 1. Otherwise, if |outArity| is 1, let |jsReturnValue| be [=ToJSValue=](|result|[0]). + 1. Otherwise, + 1. Let |values| be « ». + 1. [=list/iterate|For each=] |r| of |result|, + 1. [=list/Append=] [=ToJSValue=](|r|) to |values|. + 1. let |jsReturnValue| be [$CreateArrayFromList$](|values|). + 1. Perform [=EvaluateCall=] (|promise|.\[[Resolve]],|jsReturnValue|,false) + 1. Return UNDETERMINED + 1. Let |con| be [=CreateBuiltinFunction=](|runner|,0,"",<<>>) + 1. Perform [$Call$](|con|, undefined, <<>>) + 1. Returns |promise| +
+ +Note: The extra |$Call$| in the above algorithm ensures that the creation of the Promise is separated from the fullfilling of that Promise. In effect, this allows suspension of the fullfillment to occur whilst allowing the creation of the Promise itself to continue. + +
+ The Suspending(|jsFun|) constructor, when invoked, performs the following steps: + 1. If [$IsCallable$](|jsFun|) is false, throw a {{TypeError}}. + 1. Let |suspendingProto| be \[[%WebAssembly.Suspending.prototype%]] + 1. Let |susp| be the result of [$OrdinaryObjectCreate$](|suspendingProto|) + 1. Assign the \[[wrappedFunction]] internal slot of |susp| to |jsFun| + 1. Return |susp| +
+ +
+To create a suspending function from a JavaScript function |func|, with type |functype| perform the following steps: + + 1. Assert: [$IsCallable$](|func|). + 1. Let |stored settings| be the incumbent settings object. + 1. Let |hostfunc| be a [=host function=] which performs the following steps when called with arguments |arguments|: + 1. Let |realm| be |func|'s [=associated Realm=]. + 1. Let |relevant settings| be |realm|'s [=realm/settings object=]. + 1. Let |async_context| be the [=surrounding agent=]'s [=running execution context=]. + 1. [=Prepare to run script=] with |relevant settings|. + 1. [=Prepare to run a callback=] with |stored settings|. + 1. Let [|parameters|] → [|results|] be |functype|. + 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|) + 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). + 1. [=Clean up after running a callback=] with |stored settings|. + 1. [=Clean up after running script=] with |relevant settings|. + 1. Assert: |ret|.\[[Type]] is throw or normal. + 1. If |ret|.\[[Type]] is throw, then: + 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] |ret|.\[[Value]]. + 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. + 1. Otherwise, if [=list/size=] of |ret| is 1 and [$IsPromise$](|ret|.\[[Value]][0]): + 1. Let |promise| be |ret|.\[[Value]][0]. + 1. Perform the [=Pause=] procedure, returning a new continuation |k|. + 1. Let |resolved| be an [=AbstractClosure=] with parameters (|v|) that captures |functype|, |k| and performs the following steps when called: + 1. Let |resultsSize| be |results|'s [=list/size=]. + 1. If |resultsSize| is not 1, throw a {{TypeError}}. + 1. Let |resultType| be [|results|].[0]. + 1. Let |returnValue| be [=ToWebAssemblyValue=](|v|, |resultType|). + 1. Perform the [=Enter=] procedure, passing it the captured continuation |k| the coerced |returnValue| and |async_context|. + 1. Return undefined. + 1. Let |onResolved| be [=CreateBuiltinFunction=](|resolved|,1,"",[]) + 1. Let |rejected| be an [=AbstractClosure=] with parameters (|e|) that captures |async_context| and performs the following steps when called: + 1. Perform the [=Reject=] procedure, passing it the captured continuation |k| the exception value |e| and |async_context|. + 1. Return undefined. + 1. Let |onRejected| be [=CreateBuiltinFunction=](|rejected|,1,"",[]) + 1. Perform [$PerformPromiseThen$](|promise|, |onResolved|, |onRejected|). + 1. Return undefined + 1. Return the result of performing [=coerce a JavaScript return=] on |results| and |ret|. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Return |funcaddr|. +
+ +
+An [=execution context=] can be marked Paused to signal that the computation associated with the [=execution context=] has been paused. +
+ +
+The Pause abstract operation takes a currently executing algorithm and pauses it. The most important parameter |ec| of this operation is an +[=Execution Context=] which must be the most recent entry in the [=Execution Context Stack=] and which defines which operation is being paused. +The result of pausing an operation is a continuation |k| and a newly surfaced [=Execution Context=] -- which must be the [=Execution Context=] immediately +beneath the one on the [=Execution Context Stack=] that is [=Paused|paused=]. + + 1. If |ec| is not the most recent entry on the [=Execution Context Stack=] then trap. + 1. Let |k| be a continuation that represents the remainder of the computation associated with |ec|. + 1. Remove |ec| from the [=Execution Context Stack=] and mark it as being [=Paused|paused=]. + 1. Assert that the [=Execution Context Stack=] is not empty. + 1. Return |k| +
+ +
+The Enter abstract operation takes a continuation object |k|, a value object |v| and an [=execution context=] |ec|, and resumes the operations defined by |k|. + 1. If |ec| is not marked as [=Paused|paused=] then trap. + 1. Mark |ec| as not [=Paused|paused=]. + 1. Push |ec| on to the [=Execution Context Stack=], making |ec| the current execution context. + 1. Resume the operations defined by |k|, passing |v| as the value of the last instruction being performed by |k| + 1. Return undefined +
+ +
+The Reject abstract operation takes a continuation object |k|, an exception object |e| and an [=execution context=] |ec|, and resumes the operations defined by |k| by throwing an exception to the [=Paused|paused=] computation. + 1. If |ec| is not marked as [=Paused|paused=] then trap. + 1. Mark |ec| as not [=Paused|paused=]. + 1. Push |ec| on to the [=Execution Context Stack=], making |ec| the current execution context. + 1. Let |type|, |payload| and |opaqueData| be the result of performing [=coerce a JavaScript exception=} on |e| + 1. Resume the operations defined by |k|, performing [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. +
+

Tags

From 8586e74b27e3a7ac506ba4774597fd26bd2b489d Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Tue, 24 Sep 2024 16:08:17 -0700 Subject: [PATCH 02/15] Clarified how execution contexts get their status. Also tightened up language obout requiring traps when promise/suspending not correctly lined up. --- document/js-api/index.bs | 66 +++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 39b506caa..b757f752d 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -306,6 +306,13 @@ Each [=agent=] is associated with the following [=ordered map=]s: * The Host value cache, mapping [=host address=]es to values. +

Execution Context Status Map

+ +Note: The Execution Context Status Map is used to enforce certain correspondences between JavaScript execution and WebAssembly execution; particularly in relation to the JavaScript Promise Integration API. + +Each [=agent=] is associated with the following [=ordered map=]: + * The Execution Context Status map is used to map [=execution context|execution contexts=] to [=stack status=] values. +

The WebAssembly Namespace

@@ -1378,11 +1385,22 @@ The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript va
 
 

JavaScript Promise Integration

+Note: The JavaScript Promise Integration API (JSPI) allows WebAssembly functions to suspend and resume their execution -- based on the behavior of JavaScript functions that return Promise objects. + A {{Suspending}} object represents a JavaScript function whose calls via WebAssembly imports should be *suspended* when they return a Promise object. Each {{Suspending}} object has a \[[wrappedFunction]] internal slot which holds a JavaScript function. In addition, the {{promising}} function takes as argument a WebAssembly function and returns a JavaScript function that returns a Promise that is resolved when the WebAssembly function completes. +Each [=agent=] maintains a [=Execution Context Status map=], mapping from [=execution context|execution contexts=] to a status symbol. If present, a status can be one of two stack status values: + + * active. This signals that the associated execution context is actively executing and has the potential to be [=paused=]. + * paused. This signals that the associated execution is not currently involved in computation and has been paused. + +If an execution context is not present in the status mapping, then it may not be [=Pause|paused=] or [=Enter|reentered=]. + +When a new agent is created, its status mapping is set to the empty map. +
 [Exposed=*]
 partial namespace WebAssembly {
@@ -1411,13 +1429,17 @@ interface Suspending {
         1. Let |store| be the [=surrounding agent=]'s [=associated store=].
         1. Let |functype| be [=func_type=](|store|, |funcaddr|).
         1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|arguments|)
+        1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=].
+        1. Let |ec| be the currently executing [=execution context=], i.e., the [=execution context=] that is at the top of the [=surrounding agent=]'s current [=execution context stack=].
+        1. Assert: |map| does not contain any entry for |ec|.
+        1. Add an entry mapping |ec| to [=active=] in |map|.
         1. Let (|store|, |result|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|).
         1. Assert: if control reaches here, we have done waiting for suspended imports
+        1. If the entry for |ec| in |map| is not [=active=] then trap. Otherwise, remove the entry for |ec| from [=map=].
         1. Set the [=surrounding agent=]'s [=associated store=] to |store|.
         1. If |result| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping.
         1. Otherwise, if |result| is of the form [=throw=] exnaddr,
             1. Perform [=EvaluateCall=] (|promise|.\[[Reject]],|result|.,false).
-            1. Return UNDETERMINED
         1. Otherwise, assert |result| is a [=list=] of WebAssembly values
             1. Let |outArity| be the [=list/size=] of |result|.
             1. If |outArity| is 0, return undefined.
@@ -1428,7 +1450,6 @@ interface Suspending {
                1. [=list/Append=] [=ToJSValue=](|r|) to |values|.
                1. let |jsReturnValue| be [$CreateArrayFromList$](|values|).
             1. Perform [=EvaluateCall=] (|promise|.\[[Resolve]],|jsReturnValue|,false)
-            1. Return UNDETERMINED
     1. Let |con| be [=CreateBuiltinFunction=](|runner|,0,"",<<>>)
     1. Perform [$Call$](|con|, undefined, <<>>)
     1. Returns |promise|
@@ -1467,21 +1488,18 @@ To create a suspending function from a JavaScript function |func|, wi
             1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|.
         1. Otherwise, if [=list/size=] of |ret| is 1 and [$IsPromise$](|ret|.\[[Value]][0]):
             1. Let |promise| be |ret|.\[[Value]][0].
-            1. Perform the [=Pause=] procedure, returning a new continuation |k|.
-            1. Let |resolved| be an [=AbstractClosure=] with parameters (|v|) that captures |functype|, |k| and performs the following steps when called:
+            1. Perform the [=Pause=] procedure on |async_context|, returning a new continuation |k|.
+            1. Let |resolved| be an [=AbstractClosure=] with parameters (|v|) that captures |functype|, |async_context| and |k| and performs the following steps when called:
                 1. Let |resultsSize| be |results|'s [=list/size=].
                 1. If |resultsSize| is not 1, throw a {{TypeError}}.
                 1. Let |resultType| be [|results|].[0].
                 1. Let |returnValue| be [=ToWebAssemblyValue=](|v|, |resultType|).
                 1. Perform the [=Enter=] procedure, passing it the captured continuation |k| the coerced |returnValue| and |async_context|.
-                1. Return undefined.
             1. Let |onResolved| be [=CreateBuiltinFunction=](|resolved|,1,"",[])
             1. Let |rejected| be an [=AbstractClosure=] with parameters (|e|) that captures |async_context| and performs the following steps when called:
                 1. Perform the [=Reject=] procedure, passing it the captured continuation |k| the exception value |e| and |async_context|.
-                1. Return undefined.
             1. Let |onRejected| be [=CreateBuiltinFunction=](|rejected|,1,"",[])
             1. Perform [$PerformPromiseThen$](|promise|, |onResolved|, |onRejected|). 
-            1. Return undefined
         1. Return the result of performing [=coerce a JavaScript return=] on |results| and |ret|.
     1. Let |store| be the [=surrounding agent=]'s [=associated store=].
     1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|).
@@ -1489,38 +1507,38 @@ To create a suspending function from a JavaScript function |func|, wi
     1. Return |funcaddr|.
 
-
-An [=execution context=] can be marked Paused to signal that the computation associated with the [=execution context=] has been paused. -
-
-The Pause abstract operation takes a currently executing algorithm and pauses it. The most important parameter |ec| of this operation is an -[=Execution Context=] which must be the most recent entry in the [=Execution Context Stack=] and which defines which operation is being paused. -The result of pausing an operation is a continuation |k| and a newly surfaced [=Execution Context=] -- which must be the [=Execution Context=] immediately -beneath the one on the [=Execution Context Stack=] that is [=Paused|paused=]. +The Pause abstract operation takes a currently executing algorithm and pauses it. The most important parameter |ec| of this operation is an [=Execution Context=] which must be the most recent entry in the [=Execution Context Stack=] and which defines which operation is being paused. + +The result of pausing an operation is a continuation |k| and a newly surfaced [=Execution Context=] -- which must be the [=Execution Context=] immediately beneath the one on the [=Execution Context Stack=] that is [=paused=]. 1. If |ec| is not the most recent entry on the [=Execution Context Stack=] then trap. + 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. + 1. If the entry for |ec| in |map| is not [=active=] then trap. 1. Let |k| be a continuation that represents the remainder of the computation associated with |ec|. - 1. Remove |ec| from the [=Execution Context Stack=] and mark it as being [=Paused|paused=]. + 1. Remove |ec| from the [=Execution Context Stack=]. + 1. Set the entry for |ec| in |map| to [=paused=]. 1. Assert that the [=Execution Context Stack=] is not empty. 1. Return |k|
The Enter abstract operation takes a continuation object |k|, a value object |v| and an [=execution context=] |ec|, and resumes the operations defined by |k|. - 1. If |ec| is not marked as [=Paused|paused=] then trap. - 1. Mark |ec| as not [=Paused|paused=]. - 1. Push |ec| on to the [=Execution Context Stack=], making |ec| the current execution context. + 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. + 1. If the entry for |ec| in |map| is not [=paused=] then trap. + 1. Set the entry for |ec| in |map| to [=active=]. + 1. Push |ec| on to the [=surrounding agent=]'s [=Execution Context Stack=], making |ec| the current execution context. 1. Resume the operations defined by |k|, passing |v| as the value of the last instruction being performed by |k| 1. Return undefined
-The Reject abstract operation takes a continuation object |k|, an exception object |e| and an [=execution context=] |ec|, and resumes the operations defined by |k| by throwing an exception to the [=Paused|paused=] computation. - 1. If |ec| is not marked as [=Paused|paused=] then trap. - 1. Mark |ec| as not [=Paused|paused=]. - 1. Push |ec| on to the [=Execution Context Stack=], making |ec| the current execution context. - 1. Let |type|, |payload| and |opaqueData| be the result of performing [=coerce a JavaScript exception=} on |e| +The Reject abstract operation takes a continuation object |k|, an exception object |e| and an [=execution context=] |ec|, and resumes the operations defined by |k| by throwing an exception to the [=paused=] computation. + 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. + 1. If the entry for |ec| in |map| is not [=paused=] then trap. + 1. Set the entry for |ec| in |map| to [=active=]. + 1. Push |ec| on to the [=surrounding agent=]'s [=Execution Context Stack=], making |ec| the current execution context. + 1. Let |type|, |payload| and |opaqueData| be the result of performing [=coerce a JavaScript exception=] on |e| 1. Resume the operations defined by |k|, performing [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|.
From 8de2a62e5be17c223b990ce285ffcfe608298d9f Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Wed, 25 Sep 2024 15:57:58 -0700 Subject: [PATCH 03/15] Apply suggestions from code review Some of the changes that follow from reviewer's remarks. Co-authored-by: Shu-yu Guo --- document/js-api/index.bs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index b757f752d..c96c194ae 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -409,9 +409,9 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. 1. Otherwise, 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. - 1. Otherwise, if |v| has a \[[wrappedFunction]] insternal slot + 1. Otherwise, if |v| has a \[[wrappedFunction]] internal slot 1. Let |func| be the value of |v|'s \[[wrappedFunction]] internal slot. - 1. Assert: [$IsCallable$](|func|) is true + 1. Assert: [$IsCallable$](|func|) is true. 1. [=Create a suspending function|create a suspending function=] from |func| and |functype|, and let |funcaddr| be the result. 1. Otherwise, throw a {{LinkError}} exception. 1. Let |index| be the number of external functions in |imports|. This value |index| is known as the index of the host function |funcaddr|. @@ -1165,7 +1165,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ 1. Otherwise, let |arg| be undefined. 1. [=list/Append=] [=ToWebAssemblyValue=](|arg|, |t|) to |args|. 1. Set |i| to |i| + 1. - 1. return |args| + 1. return |args|.
@@ -1418,14 +1418,14 @@ interface Suspending { 1. If [$IsCallable$](|wasmFunc|) is false, throw a {{TypeError}}. 1. If |wasmFunc| does not have a \[[FunctionAddress]] internal slot, throw a {{TypeError}}. 1. Let |builder| be a new [=AbstractClosure=] with parameters that captures |wasmFunc| that, when invoked, performs [=run a Promising function=]. - 1. Returns [=CreateBuiltinFunction=](|builder|,1,"",<<>>) + 1. Returns [=CreateBuiltinFunction=](|builder|,1,"", « ») .
The algorithm to run a Promising function from the JavaScript object |wasmFunc| and a [=list=] of [=WebAssembly values=] |arguments| consists of the following steps: 1. Let |promise| be a new [=PromiseCapabilityRecord=]. 1. Let |funcaddr| be the value of |wasmFunc|'s \[[FunctionAddress]] internal slot. - 1. Let |runner| be a new [=AbstractClosure=] of no arguments that captures |promise|, |funcaddr|, |arguments| that, when invoked, performs the following steps: + 1. Let |runner| be a new [=AbstractClosure=] with no arguments that captures |promise|, |funcaddr|, and |arguments| that performs the following steps when called: 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let |functype| be [=func_type=](|store|, |funcaddr|). 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|arguments|) @@ -1462,7 +1462,7 @@ Note: The extra |$Call$| in the above algorithm ensures that the creation of the 1. If [$IsCallable$](|jsFun|) is false, throw a {{TypeError}}. 1. Let |suspendingProto| be \[[%WebAssembly.Suspending.prototype%]] 1. Let |susp| be the result of [$OrdinaryObjectCreate$](|suspendingProto|) - 1. Assign the \[[wrappedFunction]] internal slot of |susp| to |jsFun| + 1. Assign the \[[WrappedFunction]] internal slot of |susp| to |jsFun|. 1. Return |susp|
@@ -1494,7 +1494,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. If |resultsSize| is not 1, throw a {{TypeError}}. 1. Let |resultType| be [|results|].[0]. 1. Let |returnValue| be [=ToWebAssemblyValue=](|v|, |resultType|). - 1. Perform the [=Enter=] procedure, passing it the captured continuation |k| the coerced |returnValue| and |async_context|. + 1. Perform the [=Enter=] procedure, passing it the captured continuation |k|, the coerced |returnValue|, and |async_context|. 1. Let |onResolved| be [=CreateBuiltinFunction=](|resolved|,1,"",[]) 1. Let |rejected| be an [=AbstractClosure=] with parameters (|e|) that captures |async_context| and performs the following steps when called: 1. Perform the [=Reject=] procedure, passing it the captured continuation |k| the exception value |e| and |async_context|. From 48430f5265e80ffc794a37ddefb21c1d5cd5862d Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Tue, 1 Oct 2024 15:00:52 -0700 Subject: [PATCH 04/15] Slight refactor of the suspend operation Make it clear that a 'long jump' in the meta-interpreter is being affected. --- document/js-api/index.bs | 58 ++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index c96c194ae..198b8b09a 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1425,36 +1425,41 @@ interface Suspending { The algorithm to run a Promising function from the JavaScript object |wasmFunc| and a [=list=] of [=WebAssembly values=] |arguments| consists of the following steps: 1. Let |promise| be a new [=PromiseCapabilityRecord=]. 1. Let |funcaddr| be the value of |wasmFunc|'s \[[FunctionAddress]] internal slot. - 1. Let |runner| be a new [=AbstractClosure=] with no arguments that captures |promise|, |funcaddr|, and |arguments| that performs the following steps when called: - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let |functype| be [=func_type=](|store|, |funcaddr|). - 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|arguments|) - 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. - 1. Let |ec| be the currently executing [=execution context=], i.e., the [=execution context=] that is at the top of the [=surrounding agent=]'s current [=execution context stack=]. - 1. Assert: |map| does not contain any entry for |ec|. - 1. Add an entry mapping |ec| to [=active=] in |map|. - 1. Let (|store|, |result|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). - 1. Assert: if control reaches here, we have done waiting for suspended imports - 1. If the entry for |ec| in |map| is not [=active=] then trap. Otherwise, remove the entry for |ec| from [=map=]. - 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. - 1. If |result| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. - 1. Otherwise, if |result| is of the form [=throw=] exnaddr, - 1. Perform [=EvaluateCall=] (|promise|.\[[Reject]],|result|.,false). - 1. Otherwise, assert |result| is a [=list=] of WebAssembly values - 1. Let |outArity| be the [=list/size=] of |result|. - 1. If |outArity| is 0, return undefined. - 1. Otherwise, if |outArity| is 1, let |jsReturnValue| be [=ToJSValue=](|result|[0]). - 1. Otherwise, - 1. Let |values| be « ». - 1. [=list/iterate|For each=] |r| of |result|, - 1. [=list/Append=] [=ToJSValue=](|r|) to |values|. - 1. let |jsReturnValue| be [$CreateArrayFromList$](|values|). - 1. Perform [=EvaluateCall=] (|promise|.\[[Resolve]],|jsReturnValue|,false) + 1. Let |runner| be a new [=AbstractClosure=] with no arguments that captures |promise|, |funcaddr|, and |arguments| that performs [=evaluate a Promising function=](|promise|,|funcaddr|,|arguments|) 1. Let |con| be [=CreateBuiltinFunction=](|runner|,0,"",<<>>) 1. Perform [$Call$](|con|, undefined, <<>>) 1. Returns |promise| +
+ The algorithm to evaluate a Promising function(|promise|, |funcaddr|, |arguments|) consists of the following steps: + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let |functype| be [=func_type=](|store|, |funcaddr|). + 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|arguments|) + 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. + 1. Let |ec| be the currently executing [=execution context=], i.e., the [=execution context=] that is at the top of the [=surrounding agent=]'s current [=execution context stack=]. + 1. Assert: |map| does not contain any entry for |ec|. + 1. Add an entry mapping |ec| to [=active=] in |map|. + 1. Let (|store|, |result|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). + 1. Assert: if control reaches here, we have done waiting for suspended imports + 1. If the entry for |ec| in |map| is not [=active=] then trap. Otherwise, remove the entry for |ec| from [=map=]. + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. If |result| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. + 1. Otherwise, if |result| is of the form [=throw=] exnaddr, + 1. Perform [=EvaluateCall=] (|promise|.\[[Reject]],|result|.,false). + 1. Otherwise, assert |result| is a [=list=] of WebAssembly values + 1. Let |outArity| be the [=list/size=] of |result|. + 1. If |outArity| is 0, return undefined. + 1. Otherwise, if |outArity| is 1, let |jsReturnValue| be [=ToJSValue=](|result|[0]). + 1. Otherwise, + 1. Let |values| be « ». + 1. [=list/iterate|For each=] |r| of |result|, + 1. [=list/Append=] [=ToJSValue=](|r|) to |values|. + 1. let |jsReturnValue| be [$CreateArrayFromList$](|values|). + 1. Perform [=EvaluateCall=] (|promise|.\[[Resolve]],|jsReturnValue|,false) + 1. Return undefined. +
+ Note: The extra |$Call$| in the above algorithm ensures that the creation of the Promise is separated from the fullfilling of that Promise. In effect, this allows suspension of the fullfillment to occur whilst allowing the creation of the Promise itself to continue.
@@ -1499,7 +1504,8 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let |rejected| be an [=AbstractClosure=] with parameters (|e|) that captures |async_context| and performs the following steps when called: 1. Perform the [=Reject=] procedure, passing it the captured continuation |k| the exception value |e| and |async_context|. 1. Let |onRejected| be [=CreateBuiltinFunction=](|rejected|,1,"",[]) - 1. Perform [$PerformPromiseThen$](|promise|, |onResolved|, |onRejected|). + 1. Perform [$PerformPromiseThen$](|promise|, |onResolved|, |onRejected|). + 1. Note: this terminates the innermost call to [=evaluate a Promising function=] algorithm. Control will return to the caller of that algorithm. 1. Return the result of performing [=coerce a JavaScript return=] on |results| and |ret|. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). From ab4b5d3a1a1d7c86d1c5412e19246ce001e5c6d7 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Thu, 3 Oct 2024 15:49:39 -0700 Subject: [PATCH 05/15] Revised, as suggested by reviewer. --- document/js-api/index.bs | 68 +++++++++------------------------------- 1 file changed, 15 insertions(+), 53 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 198b8b09a..97b3f6ec7 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1426,8 +1426,7 @@ interface Suspending { 1. Let |promise| be a new [=PromiseCapabilityRecord=]. 1. Let |funcaddr| be the value of |wasmFunc|'s \[[FunctionAddress]] internal slot. 1. Let |runner| be a new [=AbstractClosure=] with no arguments that captures |promise|, |funcaddr|, and |arguments| that performs [=evaluate a Promising function=](|promise|,|funcaddr|,|arguments|) - 1. Let |con| be [=CreateBuiltinFunction=](|runner|,0,"",<<>>) - 1. Perform [$Call$](|con|, undefined, <<>>) + 1. Perform [=?=][$AsyncFunctionStart$](|promise|,|runner|) 1. Returns |promise|
@@ -1457,7 +1456,7 @@ interface Suspending { 1. [=list/Append=] [=ToJSValue=](|r|) to |values|. 1. let |jsReturnValue| be [$CreateArrayFromList$](|values|). 1. Perform [=EvaluateCall=] (|promise|.\[[Resolve]],|jsReturnValue|,false) - 1. Return undefined. + 1. Return UNUSED. Note: The extra |$Call$| in the above algorithm ensures that the creation of the Promise is separated from the fullfilling of that Promise. In effect, this allows suspension of the fullfillment to occur whilst allowing the creation of the Promise itself to continue. @@ -1480,9 +1479,11 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let |realm| be |func|'s [=associated Realm=]. 1. Let |relevant settings| be |realm|'s [=realm/settings object=]. 1. Let |async_context| be the [=surrounding agent=]'s [=running execution context=]. + 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. + 1. If the entry for |async_context| in |map| is not [=active=] then trap. 1. [=Prepare to run script=] with |relevant settings|. 1. [=Prepare to run a callback=] with |stored settings|. - 1. Let [|parameters|] → [|results|] be |functype|. + 1. Let [|parameters|] → [|resultTypes|] be |functype|. 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|) 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). 1. [=Clean up after running a callback=] with |stored settings|. @@ -1493,62 +1494,23 @@ To create a suspending function from a JavaScript function |func|, wi 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. 1. Otherwise, if [=list/size=] of |ret| is 1 and [$IsPromise$](|ret|.\[[Value]][0]): 1. Let |promise| be |ret|.\[[Value]][0]. - 1. Perform the [=Pause=] procedure on |async_context|, returning a new continuation |k|. - 1. Let |resolved| be an [=AbstractClosure=] with parameters (|v|) that captures |functype|, |async_context| and |k| and performs the following steps when called: - 1. Let |resultsSize| be |results|'s [=list/size=]. - 1. If |resultsSize| is not 1, throw a {{TypeError}}. - 1. Let |resultType| be [|results|].[0]. - 1. Let |returnValue| be [=ToWebAssemblyValue=](|v|, |resultType|). - 1. Perform the [=Enter=] procedure, passing it the captured continuation |k|, the coerced |returnValue|, and |async_context|. - 1. Let |onResolved| be [=CreateBuiltinFunction=](|resolved|,1,"",[]) - 1. Let |rejected| be an [=AbstractClosure=] with parameters (|e|) that captures |async_context| and performs the following steps when called: - 1. Perform the [=Reject=] procedure, passing it the captured continuation |k| the exception value |e| and |async_context|. - 1. Let |onRejected| be [=CreateBuiltinFunction=](|rejected|,1,"",[]) - 1. Perform [$PerformPromiseThen$](|promise|, |onResolved|, |onRejected|). - 1. Note: this terminates the innermost call to [=evaluate a Promising function=] algorithm. Control will return to the caller of that algorithm. - 1. Return the result of performing [=coerce a JavaScript return=] on |results| and |ret|. + 1. Set the entry for |async_context| in |map| to [=paused=]. + 1. Let |awaitResult| be the result of performing [$Await$](|promise|) + 1. Note: this will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or an exceptional completion. + 1. If the entry for |async_context| in |map| is not [=paused=] then trap, otherwise, set the entry to [=active=]. + 1. If |awaitResult|.\[[Type]] is throw, then: + 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] |ret|.\[[Value]]. + 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. + 1. Otherwise, return the result of performing [=coerce a JavaScript return=] on |resultTypes| and |awaitResult|. + 1. Otherwise, return the result of performing [=coerce a JavaScript return=] on |resultTypes| and |ret|. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Return |funcaddr|. - - -
-The Pause abstract operation takes a currently executing algorithm and pauses it. The most important parameter |ec| of this operation is an [=Execution Context=] which must be the most recent entry in the [=Execution Context Stack=] and which defines which operation is being paused. -The result of pausing an operation is a continuation |k| and a newly surfaced [=Execution Context=] -- which must be the [=Execution Context=] immediately beneath the one on the [=Execution Context Stack=] that is [=paused=]. - - 1. If |ec| is not the most recent entry on the [=Execution Context Stack=] then trap. - 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. - 1. If the entry for |ec| in |map| is not [=active=] then trap. - 1. Let |k| be a continuation that represents the remainder of the computation associated with |ec|. - 1. Remove |ec| from the [=Execution Context Stack=]. - 1. Set the entry for |ec| in |map| to [=paused=]. - 1. Assert that the [=Execution Context Stack=] is not empty. - 1. Return |k| +Note that we only invoke [$Await$] on the result of calling the JavaScript function |func| if it has returned a Promise object.
-
-The Enter abstract operation takes a continuation object |k|, a value object |v| and an [=execution context=] |ec|, and resumes the operations defined by |k|. - 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. - 1. If the entry for |ec| in |map| is not [=paused=] then trap. - 1. Set the entry for |ec| in |map| to [=active=]. - 1. Push |ec| on to the [=surrounding agent=]'s [=Execution Context Stack=], making |ec| the current execution context. - 1. Resume the operations defined by |k|, passing |v| as the value of the last instruction being performed by |k| - 1. Return undefined -
- -
-The Reject abstract operation takes a continuation object |k|, an exception object |e| and an [=execution context=] |ec|, and resumes the operations defined by |k| by throwing an exception to the [=paused=] computation. - 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. - 1. If the entry for |ec| in |map| is not [=paused=] then trap. - 1. Set the entry for |ec| in |map| to [=active=]. - 1. Push |ec| on to the [=surrounding agent=]'s [=Execution Context Stack=], making |ec| the current execution context. - 1. Let |type|, |payload| and |opaqueData| be the result of performing [=coerce a JavaScript exception=] on |e| - 1. Resume the operations defined by |k|, performing [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. -
- -

Tags

The tag_alloc(|store|, |parameters|) algorithm creates a new [=tag address=] for |parameters| in |store| and returns the updated store and the [=tag address=]. From 6604a37149be5dbc4233577b9e74086a2cfcc5b4 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Thu, 3 Oct 2024 16:12:30 -0700 Subject: [PATCH 06/15] Fix some minor typos --- document/js-api/index.bs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 97b3f6ec7..9a20d051a 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1417,15 +1417,15 @@ interface Suspending { The promising(|wasmFunc|) function, when invoked, performs the following steps: 1. If [$IsCallable$](|wasmFunc|) is false, throw a {{TypeError}}. 1. If |wasmFunc| does not have a \[[FunctionAddress]] internal slot, throw a {{TypeError}}. - 1. Let |builder| be a new [=AbstractClosure=] with parameters that captures |wasmFunc| that, when invoked, performs [=run a Promising function=]. - 1. Returns [=CreateBuiltinFunction=](|builder|,1,"", « ») . + 1. Let |builder| be a new [=AbstractClosure=] with no parameters that captures |wasmFunc| and performs [=run a Promising function=] when called. + 1. Returns the result of [=CreateBuiltinFunction=](|builder|,1,"", « »).
The algorithm to run a Promising function from the JavaScript object |wasmFunc| and a [=list=] of [=WebAssembly values=] |arguments| consists of the following steps: 1. Let |promise| be a new [=PromiseCapabilityRecord=]. 1. Let |funcaddr| be the value of |wasmFunc|'s \[[FunctionAddress]] internal slot. - 1. Let |runner| be a new [=AbstractClosure=] with no arguments that captures |promise|, |funcaddr|, and |arguments| that performs [=evaluate a Promising function=](|promise|,|funcaddr|,|arguments|) + 1. Let |runner| be a new [=AbstractClosure=] with no parameters that captures |promise|, |funcaddr|, and |arguments| and performs [=evaluate a Promising function=](|promise|,|funcaddr|,|arguments|) when called. 1. Perform [=?=][$AsyncFunctionStart$](|promise|,|runner|) 1. Returns |promise|
@@ -1440,7 +1440,7 @@ interface Suspending { 1. Assert: |map| does not contain any entry for |ec|. 1. Add an entry mapping |ec| to [=active=] in |map|. 1. Let (|store|, |result|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). - 1. Assert: if control reaches here, we have done waiting for suspended imports + 1. Assert: if control reaches here, we have done waiting for suspended imports. 1. If the entry for |ec| in |map| is not [=active=] then trap. Otherwise, remove the entry for |ec| from [=map=]. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |result| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. @@ -1464,10 +1464,10 @@ Note: The extra |$Call$| in the above algorithm ensures that the creation of the
The Suspending(|jsFun|) constructor, when invoked, performs the following steps: 1. If [$IsCallable$](|jsFun|) is false, throw a {{TypeError}}. - 1. Let |suspendingProto| be \[[%WebAssembly.Suspending.prototype%]] - 1. Let |susp| be the result of [$OrdinaryObjectCreate$](|suspendingProto|) + 1. Let |suspendingProto| be \[[%WebAssembly.Suspending.prototype%]]. + 1. Let |susp| be the result of [$OrdinaryObjectCreate$](|suspendingProto|,«\[[WrappedFunction]]»). 1. Assign the \[[WrappedFunction]] internal slot of |susp| to |jsFun|. - 1. Return |susp| + 1. Return |susp|.
@@ -1484,7 +1484,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. [=Prepare to run script=] with |relevant settings|. 1. [=Prepare to run a callback=] with |stored settings|. 1. Let [|parameters|] → [|resultTypes|] be |functype|. - 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|) + 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|). 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). 1. [=Clean up after running a callback=] with |stored settings|. 1. [=Clean up after running script=] with |relevant settings|. @@ -1495,7 +1495,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. Otherwise, if [=list/size=] of |ret| is 1 and [$IsPromise$](|ret|.\[[Value]][0]): 1. Let |promise| be |ret|.\[[Value]][0]. 1. Set the entry for |async_context| in |map| to [=paused=]. - 1. Let |awaitResult| be the result of performing [$Await$](|promise|) + 1. Let |awaitResult| be the result of performing [$Await$](|promise|). 1. Note: this will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or an exceptional completion. 1. If the entry for |async_context| in |map| is not [=paused=] then trap, otherwise, set the entry to [=active=]. 1. If |awaitResult|.\[[Type]] is throw, then: From b95be4c6cadde96d0b3b625016c8e97dbc13497d Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Mon, 28 Oct 2024 16:20:08 -0700 Subject: [PATCH 07/15] Update index.bs Some minor tweaks in response to reviewer's remarks. --- document/js-api/index.bs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 9a20d051a..736593fbe 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -808,7 +808,7 @@ Immediately after a WebAssembly [=memory.grow=] instruction executes, perform th
1. If the top of the stack is not [=i32.const=] (−1), 1. Let |frame| be the [=current frame=]. - 1. Assert: due to validation, |frame|.[=frame/module=].[=moduleinst/memaddrs=][0] exists. + 1. Assert: Due to validation, |frame|.[=frame/module=].[=moduleinst/memaddrs=][0] exists. 1. Let |memaddr| be the memory address |frame|.[=frame/module=].[=moduleinst/memaddrs=][0]. 1. [=Refresh the memory buffer=] of |memaddr|.
@@ -1440,13 +1440,15 @@ interface Suspending { 1. Assert: |map| does not contain any entry for |ec|. 1. Add an entry mapping |ec| to [=active=] in |map|. 1. Let (|store|, |result|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). - 1. Assert: if control reaches here, we have done waiting for suspended imports. + 1. Assert: If control reaches here, we have done waiting for suspended imports. 1. If the entry for |ec| in |map| is not [=active=] then trap. Otherwise, remove the entry for |ec| from [=map=]. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |result| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. 1. Otherwise, if |result| is of the form [=throw=] exnaddr, - 1. Perform [=EvaluateCall=] (|promise|.\[[Reject]],|result|.,false). - 1. Otherwise, assert |result| is a [=list=] of WebAssembly values + 1. Perform [$Call$] (|promise|.\[[Reject]],|result|.,false). + 1. Note: The result of the above call is ignored. + 1. Otherwise, + 1. Assert: |result| is a [=list=] of WebAssembly values 1. Let |outArity| be the [=list/size=] of |result|. 1. If |outArity| is 0, return undefined. 1. Otherwise, if |outArity| is 1, let |jsReturnValue| be [=ToJSValue=](|result|[0]). @@ -1454,8 +1456,8 @@ interface Suspending { 1. Let |values| be « ». 1. [=list/iterate|For each=] |r| of |result|, 1. [=list/Append=] [=ToJSValue=](|r|) to |values|. - 1. let |jsReturnValue| be [$CreateArrayFromList$](|values|). - 1. Perform [=EvaluateCall=] (|promise|.\[[Resolve]],|jsReturnValue|,false) + 1. Let |jsReturnValue| be [$CreateArrayFromList$](|values|). + 1. Perform [$Call$] (|promise|.\[[Resolve]],|jsReturnValue|,false) 1. Return UNUSED.
@@ -1485,7 +1487,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. [=Prepare to run a callback=] with |stored settings|. 1. Let [|parameters|] → [|resultTypes|] be |functype|. 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|). - 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). + 1. Let |ret| be [$Completion$]([$Call$](|func|, undefined, |jsArguments|)). 1. [=Clean up after running a callback=] with |stored settings|. 1. [=Clean up after running script=] with |relevant settings|. 1. Assert: |ret|.\[[Type]] is throw or normal. @@ -1495,8 +1497,9 @@ To create a suspending function from a JavaScript function |func|, wi 1. Otherwise, if [=list/size=] of |ret| is 1 and [$IsPromise$](|ret|.\[[Value]][0]): 1. Let |promise| be |ret|.\[[Value]][0]. 1. Set the entry for |async_context| in |map| to [=paused=]. - 1. Let |awaitResult| be the result of performing [$Await$](|promise|). - 1. Note: this will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or an exceptional completion. + 1. Let |awaitResult| be the result of performing [$Completion$]([$Await$](|promise|)). + 1. Note: that we only invoke [$Await$] if the call to |func| has returned a Promise object. + 1. Note: this will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or a throw completion. 1. If the entry for |async_context| in |map| is not [=paused=] then trap, otherwise, set the entry to [=active=]. 1. If |awaitResult|.\[[Type]] is throw, then: 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] |ret|.\[[Value]]. @@ -1507,8 +1510,6 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Return |funcaddr|. - -Note that we only invoke [$Await$] on the result of calling the JavaScript function |func| if it has returned a Promise object.

Tags

From a94f54f095e81422203b425d02983ccd1d51358c Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Tue, 29 Oct 2024 15:16:30 -0700 Subject: [PATCH 08/15] Update document/js-api/index.bs Co-authored-by: Shu-yu Guo --- document/js-api/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 736593fbe..57612c43b 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1498,7 +1498,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let |promise| be |ret|.\[[Value]][0]. 1. Set the entry for |async_context| in |map| to [=paused=]. 1. Let |awaitResult| be the result of performing [$Completion$]([$Await$](|promise|)). - 1. Note: that we only invoke [$Await$] if the call to |func| has returned a Promise object. + 1. Note: That we only invoke [$Await$] if the call to |func| has returned a Promise object. 1. Note: this will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or a throw completion. 1. If the entry for |async_context| in |map| is not [=paused=] then trap, otherwise, set the entry to [=active=]. 1. If |awaitResult|.\[[Type]] is throw, then: From 9a2299f5daae75ba783cea9cbd4401af76eb8ac7 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Tue, 29 Oct 2024 15:16:38 -0700 Subject: [PATCH 09/15] Update document/js-api/index.bs Co-authored-by: Shu-yu Guo --- document/js-api/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 57612c43b..8d4fcfc90 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1499,7 +1499,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. Set the entry for |async_context| in |map| to [=paused=]. 1. Let |awaitResult| be the result of performing [$Completion$]([$Await$](|promise|)). 1. Note: That we only invoke [$Await$] if the call to |func| has returned a Promise object. - 1. Note: this will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or a throw completion. + 1. Note: This will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or a throw completion. 1. If the entry for |async_context| in |map| is not [=paused=] then trap, otherwise, set the entry to [=active=]. 1. If |awaitResult|.\[[Type]] is throw, then: 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] |ret|.\[[Value]]. From 54e77cb3e9d1ea984c765291af38d6a712fdda6a Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Tue, 29 Oct 2024 15:16:57 -0700 Subject: [PATCH 10/15] Update document/js-api/index.bs Co-authored-by: Shu-yu Guo --- document/js-api/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 8d4fcfc90..81de369bf 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -404,7 +404,7 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. If [=Type=](|o|) is not Object, throw a {{TypeError}} exception. 1. Let |v| be [=?=] [$Get$](|o|, |componentName|). 1. If |externtype| is of the form [=func=] |functype|, - 1. If [$IsCallable$](|v|) is true + 1. If [$IsCallable$](|v|) is true, 1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=], 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. 1. Otherwise, From 9aac0fc25e5303e375fdf6891debdebe15c0cd43 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Wed, 30 Oct 2024 10:25:46 -0700 Subject: [PATCH 11/15] Update index.bs use 'marker' suffix to better distinguish Suspending objects from functions --- document/js-api/index.bs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 81de369bf..a6eded510 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -409,8 +409,8 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. 1. Otherwise, 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. - 1. Otherwise, if |v| has a \[[wrappedFunction]] internal slot - 1. Let |func| be the value of |v|'s \[[wrappedFunction]] internal slot. + 1. Otherwise, if |v| has a \[[WrappedFunction]] internal slot + 1. Let |func| be the value of |v|'s \[[WrappedFunction]] internal slot. 1. Assert: [$IsCallable$](|func|) is true. 1. [=Create a suspending function|create a suspending function=] from |func| and |functype|, and let |funcaddr| be the result. 1. Otherwise, throw a {{LinkError}} exception. @@ -1387,8 +1387,8 @@ The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript va Note: The JavaScript Promise Integration API (JSPI) allows WebAssembly functions to suspend and resume their execution -- based on the behavior of JavaScript functions that return Promise objects. -A {{Suspending}} object represents a JavaScript function whose calls via WebAssembly imports should be *suspended* when they return a Promise object. -Each {{Suspending}} object has a \[[wrappedFunction]] internal slot which holds a JavaScript function. +A {{Suspending}} marker object represents a JavaScript function whose calls via WebAssembly imports should be *suspended* when they return a Promise object. +Each {{Suspending}} marker object has a \[[WrappedFunction]] internal slot which holds a JavaScript function. In addition, the {{promising}} function takes as argument a WebAssembly function and returns a JavaScript function that returns a Promise that is resolved when the WebAssembly function completes. From d89f2f78dfb53b93903828e3812fd12529c4e6f6 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Wed, 30 Oct 2024 16:17:32 -0700 Subject: [PATCH 12/15] Update index.bs Small changes, responding to remarks by reviewer. --- document/js-api/index.bs | 48 +++++++++++++++------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index a6eded510..42e6f138f 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -409,7 +409,7 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. 1. Otherwise, 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. - 1. Otherwise, if |v| has a \[[WrappedFunction]] internal slot + 1. Otherwise, if |v| has a \[[WrappedFunction]] internal slot, 1. Let |func| be the value of |v|'s \[[WrappedFunction]] internal slot. 1. Assert: [$IsCallable$](|func|) is true. 1. [=Create a suspending function|create a suspending function=] from |func| and |functype|, and let |funcaddr| be the result. @@ -431,10 +431,6 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |valtype|, |value|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. - 1. Otherwise, if |v| [=implements=] {{Global}}, - 1. Let |globaladdr| be |v|.\[[Global]]. - 1. Otherwise, - 1. Throw a {{LinkError}} exception. 1. Let |externglobal| be [=external value|global=] |globaladdr|. 1. [=list/Append=] |externglobal| to |imports|. 1. If |externtype| is of the form [=external-type/mem=] memtype, @@ -1155,8 +1151,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [
To coerce JavaScript arguments from a |functype| and a [=list=] of JavaScript arguments |argValues|, perform the following steps 1. Let [|parameters|] → [|results|] be |functype|. - 1. If |parameters| or |results| contain [=v128=], throw a {{TypeError}}. - + 1. If |parameters| or |results| contain [=v128=] or [=exnref=], throw a {{TypeError}}. Note: the above error is thrown each time the \[[Call]] method is invoked. 1. Let |args| be « ». 1. Let |i| be 0. @@ -1172,7 +1167,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ To call an Exported Function with [=function address=] |funcaddr| and a [=list=] of JavaScript arguments |argValues|, perform the following steps: 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let |functype| be [=func_type=](|store|, |funcaddr|). - 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|argValues|) + 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|argValues|). 1. Let (|store|, |ret|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |ret| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. @@ -1199,14 +1194,6 @@ Note: [=call an Exported Function|Calling an Exported Function=] executes in the Note: Exported Functions do not have a \[[Construct]] method and thus it is not possible to call one with the `new` operator. -
- To run a host function from the JavaScript object |func|, type |functype|, and [=list=] of [=WebAssembly values=] |arguments|, perform the following steps: - 1. Let [|parameters|] → [|resultTypes|] be |functype|. - 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|) - 1. Let |ret| be [=?=] [$Call$](|func|, undefined, |jsArguments|). - 1. Return the result of performing [=coerce a JavaScript return=] on |resultTypes| and |ret|. -
-
To coerce WebAssembly arguments from a [=list=] of |parameterTypes| and a [=list=] of JavaScript arguments |arguments|, perform the following steps 1. If |parameterTypes| contain [=v128=], throw a {{TypeError}}. @@ -1240,7 +1227,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Otherwise, 1. Let |type| be the [=JavaScript exception tag=]. 1. Let |payload| be « ». - 1. Let |opaqueData| be [=ToWebAssemblyValue=](|v|, [=externref=]) + 1. Let |opaqueData| be [=ToWebAssemblyValue=](|v|, [=externref=]). 1. Return the triple |type|, |payload| and |opaqueData|.
@@ -1254,11 +1241,15 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Let |relevant settings| be |realm|'s [=realm/settings object=]. 1. [=Prepare to run script=] with |relevant settings|. 1. [=Prepare to run a callback=] with |stored settings|. - 1. Let |result| be the result of [=run a host function|running a host function=] from |func|, |functype|, and |arguments|. + 1. Let [|parameters|] → [|resultTypes|] be |functype|. + 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|) + 1. Let |result| be the result of Completion([$Call$](|func|, undefined, |jsArguments|)). 1. [=Clean up after running a callback=] with |stored settings|. 1. [=Clean up after running script=] with |relevant settings|. 1. Assert: |result|.\[[Type]] is throw or normal. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. If |result|.\[[Type]] is normal, then: + 1. Return the result of performing [=coerce a JavaScript return=] on |resultTypes| and |ret|. 1. If |result|.\[[Type]] is throw, then: 1. Let |v| be |result|.\[[Value]]. 1. If |v| [=implements=] {{Exception}}, @@ -1269,7 +1260,6 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Let (|store|, |address|) be [=exn_alloc=](|store|, |type|, « |payload| »). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Execute the WebAssembly instructions ([=ref.exn=] |address|) ([=throw_ref=]). - 1. Otherwise, return |result|.\[[Value]]. 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Return |funcaddr|. @@ -1425,9 +1415,10 @@ interface Suspending { The algorithm to run a Promising function from the JavaScript object |wasmFunc| and a [=list=] of [=WebAssembly values=] |arguments| consists of the following steps: 1. Let |promise| be a new [=PromiseCapabilityRecord=]. 1. Let |funcaddr| be the value of |wasmFunc|'s \[[FunctionAddress]] internal slot. - 1. Let |runner| be a new [=AbstractClosure=] with no parameters that captures |promise|, |funcaddr|, and |arguments| and performs [=evaluate a Promising function=](|promise|,|funcaddr|,|arguments|) when called. - 1. Perform [=?=][$AsyncFunctionStart$](|promise|,|runner|) - 1. Returns |promise| + 1. Let |runner| be a new [=AbstractClosure=] with no parameters that captures |promise|, |funcaddr|, and |arguments| and performs the following steps when called: + 1. Perform [=evaluate a Promising function=](|promise|,|funcaddr|,|arguments|). + 1. Perform [=?=][$AsyncFunctionStart$](|promise|,|runner|). + 1. Return |promise|.
@@ -1443,10 +1434,9 @@ interface Suspending { 1. Assert: If control reaches here, we have done waiting for suspended imports. 1. If the entry for |ec| in |map| is not [=active=] then trap. Otherwise, remove the entry for |ec| from [=map=]. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. - 1. If |result| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. + 1. If |result| is [=error=], throw an exception. This exception must be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. 1. Otherwise, if |result| is of the form [=throw=] exnaddr, - 1. Perform [$Call$] (|promise|.\[[Reject]],|result|.,false). - 1. Note: The result of the above call is ignored. + 1. [=Reject=] |promise| with |result|. 1. Otherwise, 1. Assert: |result| is a [=list=] of WebAssembly values 1. Let |outArity| be the [=list/size=] of |result|. @@ -1457,18 +1447,16 @@ interface Suspending { 1. [=list/iterate|For each=] |r| of |result|, 1. [=list/Append=] [=ToJSValue=](|r|) to |values|. 1. Let |jsReturnValue| be [$CreateArrayFromList$](|values|). - 1. Perform [$Call$] (|promise|.\[[Resolve]],|jsReturnValue|,false) + 1. [=Resolve=] |promise| with |jsReturnValue|. 1. Return UNUSED.
-Note: The extra |$Call$| in the above algorithm ensures that the creation of the Promise is separated from the fullfilling of that Promise. In effect, this allows suspension of the fullfillment to occur whilst allowing the creation of the Promise itself to continue. -
The Suspending(|jsFun|) constructor, when invoked, performs the following steps: 1. If [$IsCallable$](|jsFun|) is false, throw a {{TypeError}}. 1. Let |suspendingProto| be \[[%WebAssembly.Suspending.prototype%]]. 1. Let |susp| be the result of [$OrdinaryObjectCreate$](|suspendingProto|,«\[[WrappedFunction]]»). - 1. Assign the \[[WrappedFunction]] internal slot of |susp| to |jsFun|. + 1. Set |susp|.\[[WrappedFunction]] to |jsFun|. 1. Return |susp|.
@@ -1498,7 +1486,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let |promise| be |ret|.\[[Value]][0]. 1. Set the entry for |async_context| in |map| to [=paused=]. 1. Let |awaitResult| be the result of performing [$Completion$]([$Await$](|promise|)). - 1. Note: That we only invoke [$Await$] if the call to |func| has returned a Promise object. + 1. Note: We only invoke [$Await$] if the call to |func| has returned a Promise object. 1. Note: This will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or a throw completion. 1. If the entry for |async_context| in |map| is not [=paused=] then trap, otherwise, set the entry to [=active=]. 1. If |awaitResult|.\[[Type]] is throw, then: From 9209ee793af20a4d14dd05adc2d3ad786daaf776 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Thu, 31 Oct 2024 10:45:48 -0700 Subject: [PATCH 13/15] Update index.bs Small tweak: Add an explanation for the role of the map Big tweak: Arrange to throw a JS RuntimeError rather than trap when JSPI usage not properly balanced. --- document/js-api/index.bs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 42e6f138f..235d9bda3 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1382,7 +1382,9 @@ Each {{Suspending}} marker object has a \[[WrappedFunction]] internal slot which In addition, the {{promising}} function takes as argument a WebAssembly function and returns a JavaScript function that returns a Promise that is resolved when the WebAssembly function completes. -Each [=agent=] maintains a [=Execution Context Status map=], mapping from [=execution context|execution contexts=] to a status symbol. If present, a status can be one of two stack status values: +Each [=agent=] maintains a [=Execution Context Status map=], mapping from [=execution context|execution contexts=] to a status symbol. The purpose of this map is to ensure that applications do not try to suspend JavaScript frames and also to ensure that calls to imports marked with a {{Suspending}} marker object are properly balanced by corresponding uses of {{WebAssembly.promising}}. + +If present, a status can be one of two stack status values: * active. This signals that the associated execution context is actively executing and has the potential to be [=paused=]. * paused. This signals that the associated execution is not currently involved in computation and has been paused. @@ -1432,7 +1434,7 @@ interface Suspending { 1. Add an entry mapping |ec| to [=active=] in |map|. 1. Let (|store|, |result|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). 1. Assert: If control reaches here, we have done waiting for suspended imports. - 1. If the entry for |ec| in |map| is not [=active=] then trap. Otherwise, remove the entry for |ec| from [=map=]. + 1. If the entry for |ec| in |map| is not [=active=] then throw a WebAssembly {{RuntimeError}} exception. Otherwise, remove the entry for |ec| from [=map=]. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |result| is [=error=], throw an exception. This exception must be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. 1. Otherwise, if |result| is of the form [=throw=] exnaddr, @@ -1470,7 +1472,9 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let |relevant settings| be |realm|'s [=realm/settings object=]. 1. Let |async_context| be the [=surrounding agent=]'s [=running execution context=]. 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. - 1. If the entry for |async_context| in |map| is not [=active=] then trap. + 1. If the entry for |async_context| in |map| is not [=active=], then: + 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] {{RuntimeError}}. + 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. 1. [=Prepare to run script=] with |relevant settings|. 1. [=Prepare to run a callback=] with |stored settings|. 1. Let [|parameters|] → [|resultTypes|] be |functype|. @@ -1488,7 +1492,10 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let |awaitResult| be the result of performing [$Completion$]([$Await$](|promise|)). 1. Note: We only invoke [$Await$] if the call to |func| has returned a Promise object. 1. Note: This will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or a throw completion. - 1. If the entry for |async_context| in |map| is not [=paused=] then trap, otherwise, set the entry to [=active=]. + 1. If the entry for |async_context| in |map| is not [=paused=] then: + 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] {{RuntimeError}}. + 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. + 1. Otherwise, set the entry to [=active=]. 1. If |awaitResult|.\[[Type]] is throw, then: 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] |ret|.\[[Value]]. 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. From 0b8f55730dca7d6dfeabad12d4b6471887c2a552 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Thu, 31 Oct 2024 11:41:43 -0700 Subject: [PATCH 14/15] Update index.bs Tidy up punctuation. --- document/js-api/index.bs | 43 +++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 235d9bda3..e62fd2643 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1135,7 +1135,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ 1. Let |map| be the [=surrounding agent=]'s associated [=Exported Function cache=]. 1. If |map|[|funcaddr|] [=map/exists=], 1. Return |map|[|funcaddr|]. - 1. Let |steps| be "[=call an Exported Function|call the Exported Function=] |funcaddr| with arguments." + 1. Let |steps| be "[=call an Exported Function|call the Exported Function=] |funcaddr| with arguments.". 1. Let |realm| be the [=current Realm=]. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let |functype| be [=func_type=](|store|, |funcaddr|). @@ -1171,7 +1171,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ 1. Let (|store|, |ret|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. If |ret| is [=error=], throw an exception. This exception should be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. - 1. If |ret| is [=THROW=] [=ref.exn=] |exnaddr|, then + 1. If |ret| is [=THROW=] [=ref.exn=] |exnaddr|, then: 1. Let |tagaddr| be [=exn_tag=](|store|, |exnaddr|). 1. Let |payload| be [=exn_read=](|store|, |exnaddr|). 1. Let |jsTagAddr| be the result of [=get the JavaScript exception tag |getting the JavaScript exception tag=]. @@ -1200,7 +1200,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Let |jsArguments| be « ». 1. [=list/iterate|For each=] |arg| of |arguments|, 1. [=list/Append=] [=!=] [=ToJSValue=](|arg|) to |jsArguments|. - 1. Return |jsArguments| + 1. Return |jsArguments|.
@@ -1242,7 +1242,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. [=Prepare to run script=] with |relevant settings|. 1. [=Prepare to run a callback=] with |stored settings|. 1. Let [|parameters|] → [|resultTypes|] be |functype|. - 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|) + 1. Let |jsArguments| be the result of [=coerce WebAssembly arguments=](|parameters|,|arguments|). 1. Let |result| be the result of Completion([$Call$](|func|, undefined, |jsArguments|)). 1. [=Clean up after running a callback=] with |stored settings|. 1. [=Clean up after running script=] with |relevant settings|. @@ -1251,20 +1251,24 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. If |result|.\[[Type]] is normal, then: 1. Return the result of performing [=coerce a JavaScript return=] on |resultTypes| and |ret|. 1. If |result|.\[[Type]] is throw, then: - 1. Let |v| be |result|.\[[Value]]. - 1. If |v| [=implements=] {{Exception}}, - 1. Let |address| be |v|.\[[Address]]. - 1. Otherwise, - 1. Let |type| be the result of [=get the JavaScript exception tag |getting the JavaScript exception tag=]. - 1. Let |payload| be [=!=] [=ToWebAssemblyValue=](|v|, [=externref=]). - 1. Let (|store|, |address|) be [=exn_alloc=](|store|, |type|, « |payload| »). - 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. - 1. Execute the WebAssembly instructions ([=ref.exn=] |address|) ([=throw_ref=]). + 1. Perform [=throw a JavaScript exception|throw the JavaScript exception=] |result|.\[[Value]]. 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Return |funcaddr|.
+
+ To throw a JavaScript exception from the JavaScript object |v|, perform the following steps: + 1. If |v| [=implements=] {{Exception}}, + 1. Let |address| be |v|.\[[Address]]. + 1. Otherwise, + 1. Let |type| be the result of [=get the JavaScript exception tag |getting the JavaScript exception tag=]. + 1. Let |payload| be [=!=] [=ToWebAssemblyValue=](|v|, [=externref=]). + 1. Let (|store|, |address|) be [=exn_alloc=](|store|, |type|, « |payload| »). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Execute the WebAssembly instructions ([=ref.exn=] |address|) ([=throw_ref=]). +
+
The algorithm ToJSValue(|w|) coerces a [=WebAssembly value=] to a JavaScript value by performing the following steps: @@ -1294,7 +1298,6 @@ The algorithm ToJSValue(|w|) coerces a [=WebAssembly value=] to a Jav 1. If |w| is of the form [=ref.host=] |hostaddr|, return the result of [=retrieving a host value=] from |hostaddr|. 1. If |w| is of the form [=ref.extern=] |ref|, return [=ToJSValue=](|ref|). - Note: Number values which are equal to NaN may have various observable NaN payloads; see [$NumericToRawBytes$] for details.
@@ -1327,7 +1330,7 @@ The algorithm ToWebAssemblyValue(|v|, |type|) coerces a JavaScript va 1. Let |n| be an implementation-defined integer such that [=canon=]32 ≤ |n| < 2[=signif=](32). 1. Let |f32| be [=nan=](n). 1. Otherwise, - 1. Let |f32| be |number| rounded to the nearest representable value using IEEE 754-2008 round to nearest, ties to even mode. [[IEEE-754]] + \1. 1. Return [=f32.const=] |f32|. 1. If |type| is [=f64=], 1. Let |number| be [=?=] [$ToNumber$](|v|). @@ -1427,7 +1430,7 @@ interface Suspending { The algorithm to evaluate a Promising function(|promise|, |funcaddr|, |arguments|) consists of the following steps: 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let |functype| be [=func_type=](|store|, |funcaddr|). - 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|arguments|) + 1. Let |args| be the result of [=coerce JavaScript arguments|coercing arguments=] (|functype|,|arguments|). 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. 1. Let |ec| be the currently executing [=execution context=], i.e., the [=execution context=] that is at the top of the [=surrounding agent=]'s current [=execution context stack=]. 1. Assert: |map| does not contain any entry for |ec|. @@ -1439,8 +1442,8 @@ interface Suspending { 1. If |result| is [=error=], throw an exception. This exception must be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. 1. Otherwise, if |result| is of the form [=throw=] exnaddr, 1. [=Reject=] |promise| with |result|. - 1. Otherwise, - 1. Assert: |result| is a [=list=] of WebAssembly values + 1. Otherwise, + 1. Assert: |result| is a [=list=] of WebAssembly values. 1. Let |outArity| be the [=list/size=] of |result|. 1. If |outArity| is 0, return undefined. 1. Otherwise, if |outArity| is 1, let |jsReturnValue| be [=ToJSValue=](|result|[0]). @@ -1738,7 +1741,7 @@ constructor steps are: 1. If |resultType| is [=v128=] or [=exnref=], 1. Throw a {{TypeError}}. 1. [=list/Append=] [=?=] [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmPayload|. -1. Let (|store|, |exceptionAddr|) be [=exn_alloc=](|store|, |exceptionTag|.\[[Address]], |wasmPayload|) +1. Let (|store|, |exceptionAddr|) be [=exn_alloc=](|store|, |exceptionTag|.\[[Address]], |wasmPayload|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. [=initialize an Exception object|Initialize=] **this** from |exceptionAddr|. 1. If |options|["traceStack"] is true, @@ -1794,7 +1797,7 @@ first use and cached. It always has the [=tag type=] « [=externref=] » → « To get the JavaScript exception tag, perform the following steps: 1. If the [=surrounding agent=]'s associated [=JavaScript exception tag=] has been initialized, - 1. return the [=surrounding agent=]'s associated [=JavaScript exception tag=] + 1. return the [=surrounding agent=]'s associated [=JavaScript exception tag=]. 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |tagAddress|) be [=tag_alloc=](|store|, « [=externref=] » → « »). 1. Set the current agent's [=associated store=] to |store|. From fe35784504d6a062ae48da4640090202a11b3706 Mon Sep 17 00:00:00 2001 From: Francis McCabe Date: Thu, 31 Oct 2024 13:27:49 -0700 Subject: [PATCH 15/15] Update index.bs Clean up throwing exceptions from JS (a little bit) --- document/js-api/index.bs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index e62fd2643..fb94460a0 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1251,7 +1251,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. If |result|.\[[Type]] is normal, then: 1. Return the result of performing [=coerce a JavaScript return=] on |resultTypes| and |ret|. 1. If |result|.\[[Type]] is throw, then: - 1. Perform [=throw a JavaScript exception|throw the JavaScript exception=] |result|.\[[Value]]. + 1. Perform [=throw a JavaScript exception|throw the JavaScript exception=] on |result|.\[[Value]]. 1. Let (|store|, |funcaddr|) be [=func_alloc=](|store|, |functype|, |hostfunc|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Return |funcaddr|. @@ -1439,7 +1439,7 @@ interface Suspending { 1. Assert: If control reaches here, we have done waiting for suspended imports. 1. If the entry for |ec| in |map| is not [=active=] then throw a WebAssembly {{RuntimeError}} exception. Otherwise, remove the entry for |ec| from [=map=]. 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. - 1. If |result| is [=error=], throw an exception. This exception must be a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. + 1. If |result| is [=error=], throw a WebAssembly {{RuntimeError}} exception, unless otherwise indicated by the WebAssembly error mapping. 1. Otherwise, if |result| is of the form [=throw=] exnaddr, 1. [=Reject=] |promise| with |result|. 1. Otherwise, @@ -1476,8 +1476,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. Let |async_context| be the [=surrounding agent=]'s [=running execution context=]. 1. Let |map| be the [=surrounding agent=]'s associated [=Execution Context Status map=]. 1. If the entry for |async_context| in |map| is not [=active=], then: - 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] {{RuntimeError}}. - 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. + 1. Perform [=throw a JavaScript exception=] with a {{RuntimeError}} exception. 1. [=Prepare to run script=] with |relevant settings|. 1. [=Prepare to run a callback=] with |stored settings|. 1. Let [|parameters|] → [|resultTypes|] be |functype|. @@ -1496,8 +1495,7 @@ To create a suspending function from a JavaScript function |func|, wi 1. Note: We only invoke [$Await$] if the call to |func| has returned a Promise object. 1. Note: This will suspend both this algorithm, and the WebAssembly function being invoked by the [=evaluate a Promising function=] algorithm. On return, |ret| will be either a normal completion or a throw completion. 1. If the entry for |async_context| in |map| is not [=paused=] then: - 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] {{RuntimeError}}. - 1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. + 1. Perform [=throw a JavaScript exception=] with a {{RuntimeError}}. 1. Otherwise, set the entry to [=active=]. 1. If |awaitResult|.\[[Type]] is throw, then: 1. Let |type|, |payload| and |opaqueData| be the result of [=coerce a JavaScript exception|coercing the JavaScript exception=] |ret|.\[[Value]].