Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Update explainer on stack trace support in JS API #197

Merged
merged 5 commits into from
Feb 10, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 29 additions & 12 deletions proposals/exception-handling/Exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,6 @@ within `()` after `delegate`s are the label operands in immediate values.

### JS API

#### Stack traces

When an exception is thrown, the runtime will pop the stack across function
calls until a corresponding, enclosing try block is found. Some runtimes,
especially web VMs may also associate a stack trace that can be used to report
uncaught exceptions. However, the details of this are left to the embedder.

#### Traps

The `catch`/`catch_all` instruction catches exceptions generated by the `throw`
Expand Down Expand Up @@ -455,27 +448,51 @@ access to the data fields of a `Exception` if a matching tag is given. This last
check ensures that without access to a WebAssembly module's exported exception
tag, the associated data fields cannot be read.

The `Exception` constructor can take an optional `ExceptionOptions` argument,
which can optionally contain `traceStack` entry. When `traceStack` is `true`,
web VMs can attach a stack trace string to `Exception.stack` field, as in
JavaScript's `Error` class. While `Exception` is not a subclass of JavaScript's
`Error` and it can be used to represent normal control flow constructs,
`traceStack` field can be set when we use it to represent errors. The format of
stack trace strings conform to the [WebAssembly stack trace
conventions](https://webassembly.github.io/spec/web-api/index.html#conventions).
When `ExceptionOption` is not provided or it does not contain `traceStack`
entry, `traceStack` is considered `false` by default.

To preserve stack trace info when crossing the JS to Wasm boundary, `Exception`
can internally contain an optional `externref` value containing a stack trace
string, which is propagated when caught by `catch` and rethrown by `rethrow`.
Comment on lines +462 to +464
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, is this normative text or is it just an implementation note? IIUC, "internally" means that the reference is not observable in Wasm. Does it even matter then whether it is an externref?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect it is less error-prone to acknowledge the extra data that needs to be preserved in the core spec, rather than monkey-patching it from the outside. I'm not sure if that means we need to spell out that it's an externref, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ms2ger, I'm not sure how we could do that without actually devolving into spec'ing how JS stack traces work – presumably, the trace also needs to be extended in certain situations. Giving the handwavy-ness of stack traces in JS itself, we can only get that wrong. I'd rather not go there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rossberg Do you not prefer taking the optional stack trace on/off option in WebAssembly.Exception constructor, or you are OK with that part but doesn't want to say anything about how it is achieved and we delegate that entirely to toolchains?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aheejin, the latter. Or at least I would keep the "how" slightly more abstract.

Suggested change
To preserve stack trace info when crossing the JS to Wasm boundary, `Exception`
can internally contain an optional `externref` value containing a stack trace
string, which is propagated when caught by `catch` and rethrown by `rethrow`.
To preserve stack trace info when crossing the JS to Wasm boundary, exceptions
can internally contain a stack trace, which is propagated when caught by `catch` and rethrown by `rethrow`.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #203.


More formally, the added interfaces look like the following:

```WebIDL
dictionary TagType {
required sequence<ValueType> parameters;
};

[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
interface Tag {
constructor(TagType type);
TagType type();
};

dictionary ExceptionOptions {
boolean traceStack = false;
};

[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
interface Exception {
constructor(Tag tag, sequence<any> payload);
constructor(Tag tag, sequence<any> payload, optional ExceptionOptions options);
any getArg(Tag tag, unsigned long index);
boolean is(Tag tag);
readonly attribute (DOMString or undefined) stack;
};
```

Where `type TagType = {parameters: ValueType[]}`, following the format of the
type reflection proposal (`TagType` corresponds to a `FunctionType` without a
`results` property). `TagType` could be extended in the future for other
proposals that require a richer type specification.
`TagType` corresponds to a `FunctionType` in [the type reflection
proposal](https://github.com/WebAssembly/js-types/blob/main/proposals/js-types/Overview.md),
without a `results` property). `TagType` could be extended in the future for
other proposals that require a richer type specification.

## Changes to the text format

Expand Down