This repository was archived by the owner on Apr 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
Expose the JavaScript Exception tag and allow importing it #269
Merged
Merged
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
587dafb
Add exceptional return to func_invoke in embedding doc
dschuff 15a47cf
review comments
dschuff 904a8fb
swap order of error/exception in func_invoke
dschuff fe0d00e
Expose the JavaScript Exception tag and allow importing it
dschuff ec302e1
review suggestions
dschuff fdf1b7c
Apply suggestions from code review
dschuff 997754d
fix typo
dschuff a276636
Merge branch 'main' into js-tag-import
dschuff 4799452
Merge branch 'main' into embedding-invoke
dschuff f57cdb8
add exception allocation
dschuff ccbee0f
udpate for exnref
dschuff 4d08a58
apply all suggestions other than exn_read
dschuff 74a92cb
add exn_read
dschuff b17459a
Update allocation, initialization, construction and call-exported-fn
dschuff fa07dea
fix linkage, syntax
dschuff ea5217b
fix typo
dschuff 1c958c2
fix vars
dschuff 46de060
Merge branch 'main' into exnref-js
dschuff 5b8380a
address comments
dschuff 4d03423
remove Tag parameter from getArg
dschuff d073cfe
remove 2 more spurious references to webassembly throw
dschuff b6a79f9
more properly allocate the JS exception tag, and wrap/unwrap JS excep…
dschuff 51338d7
apply review comments
dschuff dfdd0b9
inline throwing into 'create a host function' and define in terms of …
dschuff a80e7b3
Merge branch 'exnref-js' into js-tag-import
dschuff bc04bb4
update for exnref
dschuff 6c05eaa
remove leftover text
dschuff bece351
revert algorithm change
dschuff 41b08cf
add getter
dschuff d3c3ab6
Merge branch 'main' into js-tag-import
dschuff 8834860
Don't allow construction of a WebAssembly.Exception object with the J…
dschuff File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,6 +64,7 @@ urlPrefix: https://webassembly.github.io/exception-handling/core/; spec: WebAsse | |
text: ref.null | ||
text: ref.func | ||
text: ref.extern | ||
text: ref.exn | ||
text: function index; url: syntax/modules.html#syntax-funcidx | ||
text: function instance; url: exec/runtime.html#function-instances | ||
text: store_init; url: appendix/embedding.html#embed-store-init | ||
|
@@ -101,6 +102,12 @@ urlPrefix: https://webassembly.github.io/exception-handling/core/; spec: WebAsse | |
text: global address; url: exec/runtime.html#syntax-globaladdr | ||
text: extern address; url: exec/runtime.html#syntax-externaddr | ||
text: tag address; url: exec/runtime.html#syntax-tagaddr | ||
text: tag_alloc; url: appendix/embedding.html#embed-tag-alloc | ||
text: tag_type; url: appendix/embedding.html#embed-tag-type | ||
text: exception address; url: exec/runtime.html#syntax-exnaddr | ||
text: exn_alloc; url: appendix/embedding.html#embed-exn-alloc | ||
text: exn_read; url: appendix/embedding.html#embed-exn-read | ||
text: tag type; url: syntax/types.html#syntax-tagtype | ||
url: syntax/types.html#syntax-numtype | ||
text: i32 | ||
text: i64 | ||
|
@@ -145,6 +152,7 @@ urlPrefix: https://webassembly.github.io/exception-handling/core/; spec: WebAsse | |
text: address; url: exec/runtime.html#addresses | ||
text: signed_32; url: exec/numerics.html#aux-signed | ||
text: memory.grow; url: exec/instructions.html#exec-memory-grow | ||
text: throw_ref; url: exec/instructions.html#exec-throw-ref | ||
text: current frame; url: exec/conventions.html#exec-notation-textual | ||
text: module; for: frame; url: exec/runtime.html#syntax-frame | ||
text: memaddrs; for: moduleinst; url: exec/runtime.html#syntax-moduleinst | ||
|
@@ -238,6 +246,8 @@ Each [=agent=] is associated with the following [=ordered map=]s: | |
* The <dfn>Global object cache</dfn>, mapping [=global address=]es to {{Global}} objects. | ||
* The <dfn>Extern value cache</dfn>, mapping [=extern address=]es to values. | ||
* The <dfn>Tag object cache</dfn>, mapping [=tag addresses=] to {{Tag}} objects. | ||
* The <dfn>Exception object cache</dfn>, mapping [=exception address=]es to {{Exception}} objects. | ||
|
||
|
||
<h2 id="webassembly-namespace">The WebAssembly Namespace</h2> | ||
|
||
|
@@ -257,6 +267,8 @@ namespace WebAssembly { | |
|
||
Promise<Instance> instantiate( | ||
Module moduleObject, optional object importObject); | ||
|
||
readonly attribute Tag JSTag; | ||
}; | ||
</pre> | ||
|
||
|
@@ -495,6 +507,11 @@ The verification of WebAssembly type requirements is deferred to the | |
|
||
Note: A follow-on streaming API is documented in the <a href="https://webassembly.github.io/spec/web-api/index.html">WebAssembly Web API</a>. | ||
|
||
The getter of the <dfn attribute for="WebAssembly">JSTag</dfn> attribute of the {{WebAssembly}} Namespace, when invoked, performs the following steps: | ||
1. Let |JSTagAddr| be the result of [=get the JavaScript exception tag|getting the JavaScript exception tag=]. | ||
1. Let |JSTagObject| be the result of [=create a Tag object|creating a Tag object=] from |JSTagAddr|. | ||
1. Return |JSTagObject|. | ||
|
||
<h3 id="modules">Modules</h3> | ||
|
||
<pre class="idl"> | ||
|
@@ -760,7 +777,7 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address | |
The <dfn constructor for="Table">Table(|descriptor|, |value|)</dfn> constructor, when invoked, performs the following steps: | ||
1. Let |elementType| be [=ToValueType=](|descriptor|["element"]). | ||
1. If |elementType| is not a [=reftype=], | ||
1. [=Throw=] a {{TypeError}} exception. | ||
1. Throw a {{TypeError}} exception. | ||
1. Let |initial| be |descriptor|["initial"]. | ||
1. If |descriptor|["maximum"] [=map/exists=], let |maximum| be |descriptor|["maximum"]; otherwise, let |maximum| be empty. | ||
1. If |maximum| is not empty and |maximum| < |initial|, throw a {{RangeError}} exception. | ||
|
@@ -807,7 +824,7 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address | |
1. Let |store| be the [=surrounding agent=]'s [=associated store=]. | ||
1. Let (<var ignore>limits</var>, |elementType|) be [=table_type=](|store|, |tableaddr|). | ||
1. If |elementType| is [=exnref=], | ||
1. [=Throw=] a {{TypeError}} exception. | ||
1. Throw a {{TypeError}} exception. | ||
1. Let |result| be [=table_read=](|store|, |tableaddr|, |index|). | ||
1. If |result| is [=error=], throw a {{RangeError}} exception. | ||
1. Return [=ToJSValue=](|result|). | ||
|
@@ -819,7 +836,7 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address | |
1. Let |store| be the [=surrounding agent=]'s [=associated store=]. | ||
1. Let (<var ignore>limits</var>, |elementType|) be [=table_type=](|store|, |tableaddr|). | ||
1. If |elementType| is [=exnref=], | ||
1. [=Throw=] a {{TypeError}} exception. | ||
1. Throw a {{TypeError}} exception. | ||
1. If |value| is missing, | ||
1. Let |ref| be [=DefaultValue=](|elementType|). | ||
1. Otherwise, | ||
|
@@ -1011,15 +1028,16 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [ | |
1. [=list/Append=] [=ToWebAssemblyValue=](|arg|, |t|) to |args|. | ||
1. Set |i| to |i| + 1. | ||
1. Let (|store|, |ret|) be the result of [=func_invoke=](|store|, |funcaddr|, |args|). | ||
1. Note: The expectation is that [=func_invoke=] will be updated to return (|store|, <var ignore>val</var>* | [=error=] | (exception |exntag| |payload| |opaqueData|)). | ||
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 <a href="#errors">the WebAssembly error mapping</a>. | ||
1. If |ret| is exception |exntag| |payload| |opaqueData|, then | ||
1. If |opaqueData| is not [=ref.null=] [=externref=], | ||
1. Let « [=ref.extern=] |externaddr| » be |opaqueData|. | ||
1. Throw the result of [=retrieving an extern value=] from |externaddr|. | ||
1. Let |exception| be [=create an Exception object|a new Exception=] for |exntag| and |payload|. | ||
1. Throw |exception|. | ||
1. If |ret| is [=THROW=] [=ref.exn=] |exnaddr|, then | ||
1. Let (|tagaddr|, |payload|) be [=exn_read=](|store|, |exnaddr|). | ||
1. Let |jsTagAddr| be the result of [=get the JavaScript exception tag |getting the JavaScript exception tag=]. | ||
1. If |tagaddr| is equal to |jsTagAddr|, | ||
1. Throw the result of [=retrieving an extern value=] from |payload|[0]. | ||
1. Otherwise, | ||
1. Let |exception| be [=create an Exception object|a new Exception=] created from |exnaddr|. | ||
1. Throw |exception|. | ||
1. Let |outArity| be the [=list/size=] of |ret|. | ||
1. If |outArity| is 0, return undefined. | ||
1. Otherwise, if |outArity| is 1, return [=ToJSValue=](|ret|[0]). | ||
|
@@ -1048,7 +1066,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not | |
1. Otherwise, if |resultsSize| is 1, return « [=?=] [=ToWebAssemblyValue=](|ret|, |results|[0]) ». | ||
1. Otherwise, | ||
1. Let |method| be [=?=] [$GetMethod$](|ret|, {{@@iterator}}). | ||
1. If |method| is undefined, [=throw=] a {{TypeError}}. | ||
1. If |method| is undefined, throw a {{TypeError}}. | ||
1. Let |values| be [=?=] [$IterableToList$](|ret|, |method|). | ||
1. Let |wasmValues| be a new, empty [=list=]. | ||
1. If |values|'s [=list/size=] is not |resultsSize|, throw a {{TypeError}} exception. | ||
|
@@ -1071,18 +1089,18 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not | |
1. [=Clean up after running a callback=] with |stored settings|. | ||
1. [=Clean up after running script=] with |relevant settings|. | ||
1. Assert: |result|.\[[Type]] is <emu-const>throw</emu-const> or <emu-const>normal</emu-const>. | ||
1. Let |store| be the [=surrounding agent=]'s [=associated store=]. | ||
1. If |result|.\[[Type]] is <emu-const>throw</emu-const>, then: | ||
1. Let |v| be |result|.\[[Value]]. | ||
1. If |v| [=implements=] {{Exception}}, | ||
1. Let |type| be |v|.\[[Type]]. | ||
1. Let |payload| be |v|.\[[Payload]]. | ||
1. Let |address| be |v|.\[[Address]]. | ||
1. Otherwise, | ||
1. Let |type| be the [=JavaScript exception tag=]. | ||
1. Let |payload| be « ». | ||
1. Let |opaqueData| be [=ToWebAssemblyValue=](|v|, [=externref=]) | ||
1. [=WebAssembly/Throw=] with |type|, |payload| and |opaqueData|. | ||
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 wasm instructions ([=ref.exn=] |address|) ([=throw_ref=]) | ||
1. Otherwise, return |result|.\[[Value]]. | ||
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|. | ||
|
@@ -1171,10 +1189,6 @@ The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|) coerces a JavaScript va | |
|
||
<h3 id="tags">Tags</h3> | ||
|
||
The <dfn>tag_alloc</dfn>(|store|, |parameters|) algorithm creates a new [=tag address=] for |parameters| in |store| and returns the updated store and the [=tag address=]. | ||
|
||
The <dfn>tag_parameters</dfn>(|store|, |tagAddress|) algorithm returns the [=list=] of types for |tagAddress| in |store|. | ||
|
||
<h4 id="exceptions-types">Exception types</h4> | ||
|
||
<pre class="idl"> | ||
|
@@ -1235,7 +1249,7 @@ The <dfn constructor for="Tag" lt="Tag(type)">new Tag(|type|)</dfn> constructor | |
The <dfn method for="Tag">type()</dfn> method steps are: | ||
|
||
1. Let |store| be the [=surrounding agent=]'s [=associated store=]. | ||
1. Let |parameters| be [=tag_parameters=](|store|, **this**.\[[Address]]). | ||
1. Let [|parameters|] → [] be [=tag_type=](|store|, **this**.\[[Address]]). | ||
1. Let |idlParameters| be «». | ||
1. [=list/iterate|For each=] |type| of |parameters|, | ||
1. [=list/Append=] [$FromValueType$](|type|) to |idlParameters|. | ||
|
@@ -1255,7 +1269,7 @@ dictionary ExceptionOptions { | |
[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)] | ||
interface Exception { | ||
constructor(Tag exceptionTag, sequence<any> payload, optional ExceptionOptions options = {}); | ||
any getArg(Tag exceptionTag, [EnforceRange] unsigned long index); | ||
any getArg([EnforceRange] unsigned long index); | ||
boolean is(Tag exceptionTag); | ||
readonly attribute (DOMString or undefined) stack; | ||
}; | ||
|
@@ -1265,19 +1279,31 @@ An {{Exception}} value represents an exception. | |
|
||
<div algorithm> | ||
|
||
To <dfn>create an Exception object</dfn> from a [=tag address=] |tagAddress| and a [=list=] of | ||
WebAssembly values |payload|, perform the following steps: | ||
To <dfn>initialize an Exception object</dfn> |exn| from an [=Exception address=] |exnAddress|, perform the following steps: | ||
|
||
1. Let |map| be the [=surrounding agent=]'s associated [=Exception object cache=]. | ||
1. Assert: |map|[|exnAddress|] doesn't [=map/exist=]. | ||
1. Set |exn|.\[[Address]] to |exnAddress|. | ||
1. [=map/Set=] |map|[|exnAddress|] to |exn|. | ||
1. Let |store| be the [=surrounding agent=]'s [=associated store=]. | ||
1. Let |types| be [=tag_parameters=](|store|, |tagAddress|). | ||
1. Assert: |types|'s [=list/size=] is |payload|'s [=list/size=]. | ||
1. [=list/iterate|For each=] |value| and |resultType| of |payload| and |types|, paired linearly, | ||
1. Assert: |value|'s type matches |resultType|. | ||
1. Let |exception| be a [=new=] {{Exception}}. | ||
1. Set |exception|.\[[Type]] to |tagAddress|. | ||
1. Set |exception|.\[[Payload]] to |payload|. | ||
1. Set |exception|.\[[Stack]] to undefined. | ||
1. Return |exception|. | ||
1. Let (|tagaddr|, |payload|) be [=exn_read=](|store|, |exnAddress|). | ||
1. Set |exn|.\[[Type]] to |tagaddr|. | ||
1. Set |exn|.\[[Payload]] to |payload|. | ||
1. Set |exn|.\[[Stack]] to undefined. | ||
|
||
</div> | ||
|
||
<div algorithm> | ||
|
||
To <dfn>create an Exception object</dfn> from a [=exception address=] |exnAddress|, perform the following steps: | ||
|
||
1. Let |map| be the [=surrounding agent=]'s associated [=Exception object cache=]. | ||
1. If |map|[|exnAddress|] [=map/exists=], | ||
1. Return |map|[|exnAddress|]. | ||
1. Let |exn| be a [=new=] {{Exception}}. | ||
1. [=initialize an Exception object|Initialize=] |exn| from |exnAddress|. | ||
1. Return |exn|. | ||
|
||
|
||
</div> | ||
|
||
|
@@ -1288,28 +1314,28 @@ lt="Exception(exceptionTag, payload, options)">new Exception(|exceptionTag|, |pa | |
constructor steps are: | ||
|
||
1. Let |store| be the [=surrounding agent=]'s [=associated store=]. | ||
1. Let |types| be [=tag_parameters=](|store|, |exceptionTag|.\[[Address]]). | ||
1. Let [|types|] → [] be [=tag_type=](|store|, |exceptionTag|.\[[Address]]). | ||
1. If |types|'s [=list/size=] is not |payload|'s [=list/size=], | ||
1. Throw a {{TypeError}}. | ||
1. Let |wasmPayload| be « ». | ||
1. [=list/iterate|For each=] |value| and |resultType| of |payload| and |types|, paired linearly, | ||
1. [=list/Append=] ? [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmPayload|. | ||
1. Set **this**.\[[Type]] to |exceptionTag|.\[[Address]]. | ||
1. Set **this**.\[[Payload]] to |wasmPayload|. | ||
1. [=list/Append=] [=?=] [=ToWebAssemblyValue=](|value|, |resultType|) to |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, | ||
1. Set **this**.\[[Stack]] to either a {{DOMString}} representation of the current call stack or undefined. | ||
1. Otherwise, | ||
1. Set **this**.\[[Stack]] to undefined. | ||
|
||
|
||
</div> | ||
|
||
<div algorithm> | ||
|
||
The <dfn method for="Exception">getArg(|exceptionTag|, |index|)</dfn> method steps are: | ||
The <dfn method for="Exception">getArg(|index|)</dfn> method steps are: | ||
|
||
1. If **this**.\[[Type]] is not equal to |exceptionTag|.\[[Address]], | ||
1. Throw a {{TypeError}}. | ||
1. Let |payload| be **this**.\[[Payload]]. | ||
1. Let |store| be the [=surrounding agent=]'s [=associated store=]. | ||
1. Let (|tagaddr|, |payload|) be [=exn_read=](|store|, **this**.\[[Address]]). | ||
1. Assert: |tagaddr| is equal to **this**.\[[Type]]. | ||
1. If |index| ≥ |payload|'s [=list/size=], | ||
1. Throw a {{RangeError}}. | ||
1. Return [=ToJSValue=](|payload|[|index|]). | ||
|
@@ -1336,20 +1362,22 @@ The <dfn attribute for="Exception">stack</dfn> getter steps are: | |
|
||
<h4 id="js-exceptions">JavaScript exceptions</h4> | ||
|
||
The <dfn>JavaScript exception tag</dfn> is a [=tag address=] reserved by this | ||
specification to distinguish exceptions originating from JavaScript. | ||
The <dfn>JavaScript exception tag</dfn> is a [=tag address=] associated with | ||
the surrounding agent. It is allocated in the agent's [=associated store=] on | ||
first use and cached. It always has the [=tag type=] « [=externref=] » → « ». | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think what we should be doing (as the "spec-y" thing, and in order to preerve abstraction boundaries) is to invoke Btw, I think the embedding spec should also provide a |
||
For any [=associated store=] |store|, the result of | ||
[=tag_parameters=](|store|, [=JavaScript exception tag=]) must be « ». | ||
|
||
<div algorithm> | ||
|
||
To <dfn for=WebAssembly>throw</dfn> with a [=tag address=] |type|, a matching [=list=] of WebAssembly values |payload|, and an [=externref=] |opaqueData|, perform the following steps: | ||
To <dfn>get the JavaScript exception tag</dfn>, perform the following steps: | ||
|
||
1. Unwind the stack until reaching the *catching try block* given |type|. | ||
1. Invoke the catch block with |payload| and |opaqueData|. | ||
|
||
Note: This algorithm is expected to be moved into the core specification. | ||
1. If the [=surrounding agent=]'s associated [=JavaScript exception tag=] has been initialized, | ||
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|. | ||
1. Set the current agent's associated [=JavaScript exception tag=] to |tagAddress|. | ||
1. return |tagAddress|. | ||
|
||
</div> | ||
|
||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.