alexcrichton opened PR #8310 from alexcrichton:no-trapping-imports
to bytecodealliance:main
:
By default previously all return types were wrapped in
wasmtime::Result<T>
to enable any import to return a trap to the wasm guest. This is a fair bit of boilerplate, however, and it's easy to accidentally turn a normal error into a trap. This is additionally somewhat of a power-user method as many imports probably don't end up wanting to trap.This commit adds a new configuration option to
bindgen!
:trappable_imports
, and switches the default tofalse
. The previous behavior can be recovered withtrappable_imports: true
.<!--
Please make sure you include the following information:
If this work has been discussed elsewhere, please include a link to that
conversation. If it was discussed in an issue, just mention "issue #...".Explain why this change is needed. If the details are in an issue already,
this can be brief.Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.htmlPlease ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->
alexcrichton requested fitzgen for a review on PR #8310.
alexcrichton requested wasmtime-core-reviewers for a review on PR #8310.
github-actions[bot] commented on PR #8310:
Subscribe to Label Action
cc @peterhuene
<details>
This issue or pull request has been labeled: "wasi", "wasmtime:api"Thus the following users have been cc'd because of the following labels:
- peterhuene: wasmtime:api
To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.
Learn more.
</details>
fitzgen requested elliottt for a review on PR #8310.
elliottt submitted PR review:
Nice!
alexcrichton merged PR #8310.
Hey guys, I'm relatively new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example? Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
Host side impl in Rust with trappable-imports it's like this
impl HostExample { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; let ret: String = match ext.hello() { Ok(s) => s, Err(err) => return Ok(Err(err)), }; Ok(Ok(s)) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm relatively new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
Host side impl in Rust with trappable-imports it's like this
impl HostExample { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; let ret: String = match ext.hello() { Ok(s) => s, Err(err) => return Ok(Err(err)), }; Ok(Ok(s)) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm relatively new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
Host side impl in Rust with trappable-imports it's like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; let ret: String = match ext.hello() { Ok(s) => s, Err(err) => return Ok(Err(err)), }; Ok(Ok(s)) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm relatively new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; let ret: String = match ext.hello() { Ok(s) => s, Err(err) => return Ok(Err(err)), }; Ok(Ok(s)) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm relatively new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm relatively new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; // let say Example::hello returns a Result<String, ErrorType> Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm relatively new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; // let say Example::hello returns Result<String, ErrorType> Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm fairly new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? let ext: &mut Example = self.table.get_mut(&self_)?; // let say Example::hello returns Result<String, ErrorType> Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm fairly new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? // When should I return a trap/normal result? my current understanding is if something is caused by wasmtime itself // (ResourceTable operation failed like here, then I should return a trap, else normal result. it my understanding correct/sane? let ext: &mut Example = self.table.get_mut(&self_)?; // let say Example::hello returns Result<String, ErrorType> Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm fairly new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? // When should I return a trap/normal result? my current understanding is if something is caused by wasmtime itself // (ResourceTable operation failed like here, then I should return a trap, else normal result. is my understanding correct/sane? let ext: &mut Example = self.table.get_mut(&self_)?; // let say Example::hello returns Result<String, ErrorType> Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm fairly new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example?
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? // When should I return a trap/normal result? My current understanding is if something is // caused by wasmtime itself (like ResourceTable operation failed here), then I should // return a trap, else normal result. Is my understanding correct/sane? let ext: &mut Example = self.table.get_mut(&self_)?; // let say Example::hello returns Result<String, ErrorType> Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
dearfl edited a comment on PR #8310:
Hey guys, I'm fairly new to wasm/wasmtime/wasi, can you provide some per function trappable_imports example? [Nevermind, I found some example in this PR.]
Also I'd like to ask how should one handle errors in a Host impl?
Suppose I have some wit definition like this:
resource example { hello: func() -> result<string, error-type>, }
The host side impl in Rust with trappable-imports is something like this
impl HostExample for ExampleContext { fn hello(&mut self, self_: Resource<Example>) -> wasmtime::Result<Result<String, ErrorType>> { // should I return a trap when resource table operations failed in here like this? // When should I return a trap/normal result? My current understanding is if something is // caused by wasmtime itself (like ResourceTable operation failed here), then I should // return a trap, else normal result. Is my understanding correct/sane? let ext: &mut Example = self.table.get_mut(&self_)?; // let say Example::hello returns Result<String, ErrorType> Ok(ext.hello()) } fn drop(&mut self, self_: Resource<Example>) -> wasmtime::Result<()> { // same here self.table.delete(self_)?; Ok(()) } }
alexcrichton commented on PR #8310:
Looks like you might have already found your answer @dearfl but I can try to fill in any possible gaps and/or confirm:
- Whether something should be a trap or a WIT-level error depends on whether the guest should be able to recover from it. Guests can't recover from traps but can recover from WIT-level errors.
- Table-related errors are best represented as traps since that indicates a bug in the host or resource limits being hit from the guest if an error is returned.
- You might be interested in the
trappable_error_type
configuration key to have a singleResult<T,E>
instead ofResult<Result<T, E>>
, but that's not required.In any case the code you have gisted above looks reasonable to me!
Looks like you might have already found your answer @dearfl but I can try to fill in any possible gaps and/or confirm:
* Whether something should be a trap or a WIT-level error depends on whether the guest should be able to recover from it. Guests can't recover from traps but can recover from WIT-level errors. * Table-related errors are best represented as traps since that indicates a bug in the host or resource limits being hit from the guest if an error is returned. * You might be interested in the `trappable_error_type` configuration key to have a single `Result<T,E>` instead of `Result<Result<T, E>>`, but that's not required.
In any case the code you have gisted above looks reasonable to me!
Thank you for your detailed reply.
Last updated: Dec 23 2024 at 12:05 UTC