diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index f285821ac4c0db..e2f3037ffd486c 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -5,7 +5,6 @@ const { ObjectDefineProperties, ObjectDefineProperty, ObjectGetOwnPropertyDescriptor, - PromiseResolve, SafeMap, SafeWeakMap, StringPrototypeStartsWith, @@ -26,9 +25,7 @@ const { const { Buffer } = require('buffer'); const { - ERR_INVALID_ARG_TYPE, ERR_MANIFEST_ASSERT_INTEGRITY, - ERR_WEBASSEMBLY_RESPONSE, } = require('internal/errors').codes; const assert = require('internal/assert'); @@ -222,49 +219,8 @@ function setupFetch() { }); // The WebAssembly Web API: https://webassembly.github.io/spec/web-api - internalBinding('wasm_web_api').setImplementation((streamState, source) => { - (async () => { - const response = await PromiseResolve(source); - if (!(response instanceof lazyUndici().Response)) { - throw new ERR_INVALID_ARG_TYPE( - 'source', ['Response', 'Promise resolving to Response'], response); - } - - const contentType = response.headers.get('Content-Type'); - if (contentType !== 'application/wasm') { - throw new ERR_WEBASSEMBLY_RESPONSE( - `has unsupported MIME type '${contentType}'`); - } - - if (!response.ok) { - throw new ERR_WEBASSEMBLY_RESPONSE( - `has status code ${response.status}`); - } - - if (response.bodyUsed !== false) { - 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. - const { body } = response; - if (body != null) { - for await (const chunk of body) { - streamState.push(chunk); - } - } - })().then(() => { - // No error occurred. Tell the implementation that the stream has ended. - streamState.finish(); - }, (err) => { - // An error occurred, either because the given object was not a valid - // and usable Response or because a network error occurred. - streamState.abort(err); - }); - }); + const { wasmStreamingCallback } = require('internal/wasm_web_api'); + internalBinding('wasm_web_api').setImplementation(wasmStreamingCallback); } // TODO(aduh95): move this to internal/bootstrap/browser when the CLI flag is diff --git a/lib/internal/wasm_web_api.js b/lib/internal/wasm_web_api.js new file mode 100644 index 00000000000000..084a223806334f --- /dev/null +++ b/lib/internal/wasm_web_api.js @@ -0,0 +1,66 @@ +'use strict'; + +const { + PromiseResolve, +} = primordials; +const { + ERR_INVALID_ARG_TYPE, + ERR_WEBASSEMBLY_RESPONSE, +} = require('internal/errors').codes; + +let undici; +function lazyUndici() { + return undici ??= require('internal/deps/undici/undici'); +} + +// This is essentially an implementation of a v8::WasmStreamingCallback, except +// that it is implemented in JavaScript because the fetch() implementation is +// difficult to use from C++. See lib/internal/bootstrap/pre_execution.js and +// src/node_wasm_web_api.cc that interact with this function. +function wasmStreamingCallback(streamState, source) { + (async () => { + const response = await PromiseResolve(source); + if (!(response instanceof lazyUndici().Response)) { + throw new ERR_INVALID_ARG_TYPE( + 'source', ['Response', 'Promise resolving to Response'], response); + } + + const contentType = response.headers.get('Content-Type'); + if (contentType !== 'application/wasm') { + throw new ERR_WEBASSEMBLY_RESPONSE( + `has unsupported MIME type '${contentType}'`); + } + + if (!response.ok) { + throw new ERR_WEBASSEMBLY_RESPONSE( + `has status code ${response.status}`); + } + + if (response.bodyUsed !== false) { + 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. + const { body } = response; + if (body != null) { + for await (const chunk of body) { + streamState.push(chunk); + } + } + })().then(() => { + // No error occurred. Tell the implementation that the stream has ended. + streamState.finish(); + }, (err) => { + // An error occurred, either because the given object was not a valid + // and usable Response or because a network error occurred. + streamState.abort(err); + }); +} + +module.exports = { + wasmStreamingCallback +}; diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index d5bf6e133ebc99..af3eaaeb3967a9 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -140,6 +140,7 @@ const expectedModules = new Set([ 'NativeModule internal/util/types', 'NativeModule internal/validators', 'NativeModule internal/vm/module', + 'NativeModule internal/wasm_web_api', 'NativeModule internal/webstreams/adapters', 'NativeModule internal/webstreams/compression', 'NativeModule internal/webstreams/encoding',