From ad8269450a1f9b04b5a502789a6097f4346aae03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Mon, 25 Apr 2022 23:46:42 +0200 Subject: [PATCH] lib,src: use Response URL as WebAssembly location As per Section 3 of the WebAssembly Web API spec. PR-URL: https://github.com/nodejs/node/pull/42842 Refs: https://github.com/nodejs/node/pull/42701 Reviewed-By: Gus Caplan Reviewed-By: Antoine du Hamel --- lib/internal/bootstrap/pre_execution.js | 4 ++++ src/node_wasm_web_api.cc | 12 ++++++++++++ src/node_wasm_web_api.h | 1 + test/fixtures/crash.wasm | Bin 0 -> 36 bytes test/fixtures/crash.wat | 1 + test/parallel/test-wasm-web-api.js | 23 ++++++++++++++++++++++- 6 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/crash.wasm create mode 100644 test/fixtures/crash.wat diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 0b58803e63d243..de42598ea7c45a 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -245,6 +245,10 @@ function setupFetch() { throw new ERR_WEBASSEMBLY_RESPONSE('body has already been used'); } + if (response.url) { + streamState.setURL(response.url); + } + // Pass all data from the response body to the WebAssembly compiler. for await (const chunk of response.body) { streamState.push(chunk); diff --git a/src/node_wasm_web_api.cc b/src/node_wasm_web_api.cc index b23096120b1121..fcb845d08b0dfe 100644 --- a/src/node_wasm_web_api.cc +++ b/src/node_wasm_web_api.cc @@ -29,6 +29,7 @@ Local WasmStreamingObject::Initialize(Environment* env) { t->InstanceTemplate()->SetInternalFieldCount( WasmStreamingObject::kInternalFieldCount); + env->SetProtoMethod(t, "setURL", SetURL); env->SetProtoMethod(t, "push", Push); env->SetProtoMethod(t, "finish", Finish); env->SetProtoMethod(t, "abort", Abort); @@ -75,6 +76,17 @@ void WasmStreamingObject::New(const FunctionCallbackInfo& args) { new WasmStreamingObject(env, args.This()); } +void WasmStreamingObject::SetURL(const FunctionCallbackInfo& args) { + WasmStreamingObject* obj; + ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder()); + CHECK(obj->streaming_); + + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsString()); + Utf8Value url(Environment::GetCurrent(args)->isolate(), args[0]); + obj->streaming_->SetUrl(url.out(), url.length()); +} + void WasmStreamingObject::Push(const FunctionCallbackInfo& args) { WasmStreamingObject* obj; ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder()); diff --git a/src/node_wasm_web_api.h b/src/node_wasm_web_api.h index 9f5fe868167635..da584be1592cb4 100644 --- a/src/node_wasm_web_api.h +++ b/src/node_wasm_web_api.h @@ -33,6 +33,7 @@ class WasmStreamingObject final : public BaseObject { private: static void New(const v8::FunctionCallbackInfo& args); + static void SetURL(const v8::FunctionCallbackInfo& args); static void Push(const v8::FunctionCallbackInfo& args); static void Finish(const v8::FunctionCallbackInfo& args); static void Abort(const v8::FunctionCallbackInfo& args); diff --git a/test/fixtures/crash.wasm b/test/fixtures/crash.wasm new file mode 100644 index 0000000000000000000000000000000000000000..fdcc992885e505c33e5375e74db7503846e30b60 GIT binary patch literal 36 rcmZQbEY4+QU|?WmVN76PU}j=uVCQ6HO)g3-&R}5RVr67zVBiJ-P!9w2 literal 0 HcmV?d00001 diff --git a/test/fixtures/crash.wat b/test/fixtures/crash.wat new file mode 100644 index 00000000000000..70450453869cc6 --- /dev/null +++ b/test/fixtures/crash.wat @@ -0,0 +1 @@ +(module (func (export "crash") unreachable)) diff --git a/test/parallel/test-wasm-web-api.js b/test/parallel/test-wasm-web-api.js index 9576e13d669582..d4a81794f80eb3 100644 --- a/test/parallel/test-wasm-web-api.js +++ b/test/parallel/test-wasm-web-api.js @@ -19,7 +19,7 @@ async function testRequest(handler) { const server = createServer((_, res) => handler(res)).unref().listen(0); await events.once(server, 'listening'); const { port } = server.address(); - return fetch(`http://127.0.0.1:${port}/`); + return fetch(`http://127.0.0.1:${port}/foo.wasm`); } // Runs the given function both with the promise itself and as a continuation @@ -223,4 +223,25 @@ function testCompileStreamingRejectionUsingFetch(responseCallback, rejection) { name: 'TypeError', message: /terminated/ }); + + // Test "Developer-Facing Display Conventions" described in the WebAssembly + // Web API specification. + await testCompileStreaming(() => testRequest((res) => { + // Respond with a WebAssembly module that only exports a single function, + // which only contains an 'unreachable' instruction. + res.setHeader('Content-Type', 'application/wasm'); + res.end(fixtures.readSync('crash.wasm')); + }), async (modPromise) => { + // Call the WebAssembly function and check that the error stack contains the + // correct "WebAssembly location" as per the specification. + const mod = await modPromise; + const instance = new WebAssembly.Instance(mod); + assert.throws(() => instance.exports.crash(), (err) => { + const stack = err.stack.split(/\n/g); + assert.strictEqual(stack[0], 'RuntimeError: unreachable'); + assert.match(stack[1], + /^\s*at http:\/\/127\.0\.0\.1:\d+\/foo\.wasm:wasm-function\[0\]:0x22$/); + return true; + }); + }); })().then(common.mustCall());