From 5edd2771619ddc3cd2f6c76cc93ee64277aa17c2 Mon Sep 17 00:00:00 2001 From: Andreu Botella Date: Sun, 10 Oct 2021 07:03:23 -0700 Subject: [PATCH] feat: Show the URL of streaming WASM modules in stack traces (#12268) WebAssembly modules compiled through `WebAssembly.compile()` and similar non-streaming APIs don't have a URL associated to them, because they have been compiled from a buffer source. In stack traces, V8 will use a URL such as `wasm://wasm/d1c677ea`, with a hash of the module. However, wasm modules compiled through streaming APIs, like `WebAssembly.compileStreaming()`, do have a known URL, which can be obtained from the `Response` object passed into the streaming APIs. And as per the developer-facing display conventions in the WebAssembly Web API spec, this URL should be used in stack traces. This change implements that. --- cli/tests/integration/run_tests.rs | 9 ++++- cli/tests/testdata/unreachable.wasm | Bin 0 -> 42 bytes cli/tests/testdata/wasm_unreachable.js | 47 ++---------------------- cli/tests/testdata/wasm_unreachable.out | 4 +- cli/tests/testdata/wasm_url.js | 8 ++++ cli/tests/testdata/wasm_url.out | 3 ++ core/lib.deno_core.d.ts | 2 + core/ops_builtin.rs | 17 +++++++++ ext/fetch/26_fetch.js | 3 ++ 9 files changed, 46 insertions(+), 47 deletions(-) create mode 100644 cli/tests/testdata/unreachable.wasm create mode 100644 cli/tests/testdata/wasm_url.js create mode 100644 cli/tests/testdata/wasm_url.out diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs index 8c337b771c..f247aa93fb 100644 --- a/cli/tests/integration/run_tests.rs +++ b/cli/tests/integration/run_tests.rs @@ -856,11 +856,18 @@ itest!(wasm_async { }); itest!(wasm_unreachable { - args: "run wasm_unreachable.js", + args: "run --allow-read wasm_unreachable.js", output: "wasm_unreachable.out", exit_code: 1, }); +itest!(wasm_url { + args: "run --quiet --allow-net=localhost:4545 wasm_url.js", + output: "wasm_url.out", + exit_code: 1, + http_server: true, +}); + itest!(weakref { args: "run --quiet --reload weakref.ts", output: "weakref.ts.out", diff --git a/cli/tests/testdata/unreachable.wasm b/cli/tests/testdata/unreachable.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a4110ee393142cdbea567a320004b81f78850559 GIT binary patch literal 42 xcmZQbEY4+QU|?WmVN76PU}j=uVCQG#F3l@SO-#;6Ov*`RVBlh9WM*LC1^|Ym25A5Q literal 0 HcmV?d00001 diff --git a/cli/tests/testdata/wasm_unreachable.js b/cli/tests/testdata/wasm_unreachable.js index 991ebcec85..36aab0d84f 100644 --- a/cli/tests/testdata/wasm_unreachable.js +++ b/cli/tests/testdata/wasm_unreachable.js @@ -1,50 +1,9 @@ // WebAssembly module containing a single function with an unreachable instruction -const binary = Uint8Array.from([ - 0x00, - 0x61, - 0x73, - 0x6d, - 0x01, - 0x00, - 0x00, - 0x00, - 0x01, - 0x04, - 0x01, - 0x60, - 0x00, - 0x00, - 0x03, - 0x02, - 0x01, - 0x00, - 0x07, - 0x0f, - 0x01, - 0x0b, - 0x75, - 0x6e, - 0x72, - 0x65, - 0x61, - 0x63, - 0x68, - 0x61, - 0x62, - 0x6c, - 0x65, - 0x00, - 0x00, - 0x0a, - 0x05, - 0x01, - 0x03, - 0x00, - 0x00, - 0x0b, -]); +const binary = await Deno.readFile("./unreachable.wasm"); const module = new WebAssembly.Module(binary); const instance = new WebAssembly.Instance(module); +// Compare the stack trace with wasm_url.js, which compiles the WASM module with +// streaming APIs. instance.exports.unreachable(); diff --git a/cli/tests/testdata/wasm_unreachable.out b/cli/tests/testdata/wasm_unreachable.out index 28fe00de6c..cbb6b65194 100644 --- a/cli/tests/testdata/wasm_unreachable.out +++ b/cli/tests/testdata/wasm_unreachable.out @@ -1,3 +1,3 @@ -error: Uncaught RuntimeError: unreachable - at (wasm://wasm/[WILDCARD]) +error: Uncaught (in promise) RuntimeError: unreachable + at (wasm://wasm/d1c677ea:1:41) at [WILDCARD]/wasm_unreachable.js:[WILDCARD] diff --git a/cli/tests/testdata/wasm_url.js b/cli/tests/testdata/wasm_url.js new file mode 100644 index 0000000000..bb7ccd73df --- /dev/null +++ b/cli/tests/testdata/wasm_url.js @@ -0,0 +1,8 @@ +const module = await WebAssembly.compileStreaming( + fetch("http://localhost:4545/unreachable.wasm"), +); +const instance = new WebAssembly.Instance(module); + +// Compare the stack trace with wasm_unreachable.js, which compiles the WASM +// module with synchronous APIs. +instance.exports.unreachable(); diff --git a/cli/tests/testdata/wasm_url.out b/cli/tests/testdata/wasm_url.out new file mode 100644 index 0000000000..c88b51020a --- /dev/null +++ b/cli/tests/testdata/wasm_url.out @@ -0,0 +1,3 @@ +error: Uncaught (in promise) RuntimeError: unreachable + at (http://localhost:4545/unreachable.wasm:1:41) + at [WILDCARD]/wasm_url.js:[WILDCARD] diff --git a/core/lib.deno_core.d.ts b/core/lib.deno_core.d.ts index efa138d775..6db01df61e 100644 --- a/core/lib.deno_core.d.ts +++ b/core/lib.deno_core.d.ts @@ -63,6 +63,8 @@ declare namespace Deno { * compiler. Takes the rid and a `Uint8Array`. * - `op_wasm_streaming_abort`. Aborts the wasm compilation. Takes the rid * and an exception. Invalidates the resource. + * - `op_wasm_streaming_set_url`. Sets a source URL for the wasm module. + * Takes the rid and a string. * - To indicate the end of the resource, use `Deno.core.close()` with the * rid. */ diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs index 3920629603..bb04cddf44 100644 --- a/core/ops_builtin.rs +++ b/core/ops_builtin.rs @@ -26,6 +26,10 @@ pub(crate) fn init_builtins() -> Extension { ("op_resources", op_sync(op_resources)), ("op_wasm_streaming_feed", op_sync(op_wasm_streaming_feed)), ("op_wasm_streaming_abort", op_sync(op_wasm_streaming_abort)), + ( + "op_wasm_streaming_set_url", + op_sync(op_wasm_streaming_set_url), + ), ]) .build() } @@ -137,3 +141,16 @@ pub fn op_wasm_streaming_abort( Ok(()) } + +pub fn op_wasm_streaming_set_url( + state: &mut OpState, + rid: ResourceId, + url: String, +) -> Result<(), AnyError> { + let wasm_streaming = + state.resource_table.get::(rid)?; + + wasm_streaming.0.borrow_mut().set_url(&url); + + Ok(()) +} diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js index f74726242a..fcfa753182 100644 --- a/ext/fetch/26_fetch.js +++ b/ext/fetch/26_fetch.js @@ -526,6 +526,9 @@ throw new TypeError(`HTTP status code ${res.status}`); } + // Pass the resolved URL to v8. + core.opSync("op_wasm_streaming_set_url", rid, res.url); + // 2.6. // Rather than consuming the body as an ArrayBuffer, this passes each // chunk to the feed as soon as it's available.