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 <me@gus.host>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
Tobias Nießen 2022-04-25 23:46:42 +02:00 committed by GitHub
parent f54bf2839b
commit 4f9bc41f1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 40 additions and 1 deletions

View File

@ -245,6 +245,10 @@ function setupFetch() {
throw new ERR_WEBASSEMBLY_RESPONSE('body has already been used'); 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. // Pass all data from the response body to the WebAssembly compiler.
for await (const chunk of response.body) { for await (const chunk of response.body) {
streamState.push(chunk); streamState.push(chunk);

View File

@ -29,6 +29,7 @@ Local<Function> WasmStreamingObject::Initialize(Environment* env) {
t->InstanceTemplate()->SetInternalFieldCount( t->InstanceTemplate()->SetInternalFieldCount(
WasmStreamingObject::kInternalFieldCount); WasmStreamingObject::kInternalFieldCount);
env->SetProtoMethod(t, "setURL", SetURL);
env->SetProtoMethod(t, "push", Push); env->SetProtoMethod(t, "push", Push);
env->SetProtoMethod(t, "finish", Finish); env->SetProtoMethod(t, "finish", Finish);
env->SetProtoMethod(t, "abort", Abort); env->SetProtoMethod(t, "abort", Abort);
@ -75,6 +76,17 @@ void WasmStreamingObject::New(const FunctionCallbackInfo<Value>& args) {
new WasmStreamingObject(env, args.This()); new WasmStreamingObject(env, args.This());
} }
void WasmStreamingObject::SetURL(const FunctionCallbackInfo<Value>& 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<Value>& args) { void WasmStreamingObject::Push(const FunctionCallbackInfo<Value>& args) {
WasmStreamingObject* obj; WasmStreamingObject* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder()); ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());

View File

@ -33,6 +33,7 @@ class WasmStreamingObject final : public BaseObject {
private: private:
static void New(const v8::FunctionCallbackInfo<v8::Value>& args); static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetURL(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Push(const v8::FunctionCallbackInfo<v8::Value>& args); static void Push(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Finish(const v8::FunctionCallbackInfo<v8::Value>& args); static void Finish(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Abort(const v8::FunctionCallbackInfo<v8::Value>& args); static void Abort(const v8::FunctionCallbackInfo<v8::Value>& args);

BIN
test/fixtures/crash.wasm vendored Normal file

Binary file not shown.

1
test/fixtures/crash.wat vendored Normal file
View File

@ -0,0 +1 @@
(module (func (export "crash") unreachable))

View File

@ -19,7 +19,7 @@ async function testRequest(handler) {
const server = createServer((_, res) => handler(res)).unref().listen(0); const server = createServer((_, res) => handler(res)).unref().listen(0);
await events.once(server, 'listening'); await events.once(server, 'listening');
const { port } = server.address(); 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 // Runs the given function both with the promise itself and as a continuation
@ -223,4 +223,25 @@ function testCompileStreamingRejectionUsingFetch(responseCallback, rejection) {
name: 'TypeError', name: 'TypeError',
message: /terminated/ 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()); })().then(common.mustCall());