Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTobias Nießen <tniessen@tnie.de>2022-04-23 06:09:15 +0300
committerMichaël Zasso <targos@protonmail.com>2022-04-28 07:57:22 +0300
commitb0f7c4c8f928597f2118dde73c98ac0fba8f01f9 (patch)
tree05d1cf273c607c243275988b2236a69b9607729f /src
parentc6c1dc58335f08b52139a0692502486bcb8b6ea0 (diff)
lib,src: implement WebAssembly Web API
Refs: https://github.com/nodejs/node/pull/41749 Fixes: https://github.com/nodejs/node/issues/21130 PR-URL: https://github.com/nodejs/node/pull/42701 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/api/environment.cc9
-rw-r--r--src/env.h4
-rw-r--r--src/node_binding.cc1
-rw-r--r--src/node_wasm_web_api.cc196
-rw-r--r--src/node_wasm_web_api.h54
5 files changed, 263 insertions, 1 deletions
diff --git a/src/api/environment.cc b/src/api/environment.cc
index 97261256858..f3a8f49812d 100644
--- a/src/api/environment.cc
+++ b/src/api/environment.cc
@@ -3,8 +3,10 @@
#include "node_errors.h"
#include "node_internals.h"
#include "node_native_module_env.h"
+#include "node_options-inl.h"
#include "node_platform.h"
#include "node_v8_platform-inl.h"
+#include "node_wasm_web_api.h"
#include "uv.h"
#if HAVE_INSPECTOR
@@ -252,6 +254,13 @@ void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s) {
s.allow_wasm_code_generation_callback : AllowWasmCodeGenerationCallback;
isolate->SetAllowWasmCodeGenerationCallback(allow_wasm_codegen_cb);
+ Mutex::ScopedLock lock(node::per_process::cli_options_mutex);
+ if (per_process::cli_options->get_per_isolate_options()
+ ->get_per_env_options()
+ ->experimental_fetch) {
+ isolate->SetWasmStreamingCallback(wasm_web_api::StartStreamingCompilation);
+ }
+
if ((s.flags & SHOULD_NOT_SET_PROMISE_REJECTION_CALLBACK) == 0) {
auto* promise_reject_cb = s.promise_reject_callback ?
s.promise_reject_callback : PromiseRejectCallback;
diff --git a/src/env.h b/src/env.h
index bededfcb5de..7e35833e45b 100644
--- a/src/env.h
+++ b/src/env.h
@@ -550,7 +550,9 @@ constexpr size_t kFsStatsBufferLength =
V(tls_wrap_constructor_function, v8::Function) \
V(trace_category_state_function, v8::Function) \
V(udp_constructor_function, v8::Function) \
- V(url_constructor_function, v8::Function)
+ V(url_constructor_function, v8::Function) \
+ V(wasm_streaming_compilation_impl, v8::Function) \
+ V(wasm_streaming_object_constructor, v8::Function)
class Environment;
struct AllocatedBuffer;
diff --git a/src/node_binding.cc b/src/node_binding.cc
index 29b9ccdaed8..2991ee34746 100644
--- a/src/node_binding.cc
+++ b/src/node_binding.cc
@@ -87,6 +87,7 @@
V(uv) \
V(v8) \
V(wasi) \
+ V(wasm_web_api) \
V(watchdog) \
V(worker) \
V(zlib)
diff --git a/src/node_wasm_web_api.cc b/src/node_wasm_web_api.cc
new file mode 100644
index 00000000000..b23096120b1
--- /dev/null
+++ b/src/node_wasm_web_api.cc
@@ -0,0 +1,196 @@
+#include "node_wasm_web_api.h"
+
+#include "memory_tracker-inl.h"
+#include "node_errors.h"
+
+namespace node {
+namespace wasm_web_api {
+
+using v8::ArrayBuffer;
+using v8::ArrayBufferView;
+using v8::Context;
+using v8::Function;
+using v8::FunctionCallbackInfo;
+using v8::FunctionTemplate;
+using v8::Local;
+using v8::MaybeLocal;
+using v8::Object;
+using v8::Value;
+using v8::WasmStreaming;
+
+Local<Function> WasmStreamingObject::Initialize(Environment* env) {
+ Local<Function> templ = env->wasm_streaming_object_constructor();
+ if (!templ.IsEmpty()) {
+ return templ;
+ }
+
+ Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
+ t->Inherit(BaseObject::GetConstructorTemplate(env));
+ t->InstanceTemplate()->SetInternalFieldCount(
+ WasmStreamingObject::kInternalFieldCount);
+
+ env->SetProtoMethod(t, "push", Push);
+ env->SetProtoMethod(t, "finish", Finish);
+ env->SetProtoMethod(t, "abort", Abort);
+
+ auto function = t->GetFunction(env->context()).ToLocalChecked();
+ env->set_wasm_streaming_object_constructor(function);
+ return function;
+}
+
+void WasmStreamingObject::RegisterExternalReferences(
+ ExternalReferenceRegistry* registry) {
+ registry->Register(Push);
+ registry->Register(Finish);
+ registry->Register(Abort);
+}
+
+void WasmStreamingObject::MemoryInfo(MemoryTracker* tracker) const {
+ // v8::WasmStreaming is opaque. We assume that the size of the WebAssembly
+ // module that is being compiled is roughly what V8 allocates (as in, off by
+ // only a small factor).
+ tracker->TrackFieldWithSize("streaming", wasm_size_);
+}
+
+MaybeLocal<Object> WasmStreamingObject::Create(
+ Environment* env, std::shared_ptr<WasmStreaming> streaming) {
+ Local<Function> ctor = Initialize(env);
+ Local<Object> obj;
+ if (!ctor->NewInstance(env->context(), 0, nullptr).ToLocal(&obj)) {
+ return MaybeLocal<Object>();
+ }
+
+ CHECK(streaming);
+
+ WasmStreamingObject* ptr = Unwrap<WasmStreamingObject>(obj);
+ CHECK_NOT_NULL(ptr);
+ ptr->streaming_ = streaming;
+ ptr->wasm_size_ = 0;
+ return obj;
+}
+
+void WasmStreamingObject::New(const FunctionCallbackInfo<Value>& args) {
+ CHECK(args.IsConstructCall());
+ Environment* env = Environment::GetCurrent(args);
+ new WasmStreamingObject(env, args.This());
+}
+
+void WasmStreamingObject::Push(const FunctionCallbackInfo<Value>& args) {
+ WasmStreamingObject* obj;
+ ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());
+ CHECK(obj->streaming_);
+
+ CHECK_EQ(args.Length(), 1);
+ Local<Value> chunk = args[0];
+
+ // The start of the memory section backing the ArrayBuffer(View), the offset
+ // of the ArrayBuffer(View) within the memory section, and its size in bytes.
+ const void* bytes;
+ size_t offset;
+ size_t size;
+
+ if (LIKELY(chunk->IsArrayBufferView())) {
+ Local<ArrayBufferView> view = chunk.As<ArrayBufferView>();
+ bytes = view->Buffer()->GetBackingStore()->Data();
+ offset = view->ByteOffset();
+ size = view->ByteLength();
+ } else if (LIKELY(chunk->IsArrayBuffer())) {
+ Local<ArrayBuffer> buffer = chunk.As<ArrayBuffer>();
+ bytes = buffer->GetBackingStore()->Data();
+ offset = 0;
+ size = buffer->ByteLength();
+ } else {
+ return node::THROW_ERR_INVALID_ARG_TYPE(
+ Environment::GetCurrent(args),
+ "chunk must be an ArrayBufferView or an ArrayBuffer");
+ }
+
+ // Forward the data to V8. Internally, V8 will make a copy.
+ obj->streaming_->OnBytesReceived(static_cast<const uint8_t*>(bytes) + offset,
+ size);
+ obj->wasm_size_ += size;
+}
+
+void WasmStreamingObject::Finish(const FunctionCallbackInfo<Value>& args) {
+ WasmStreamingObject* obj;
+ ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());
+ CHECK(obj->streaming_);
+
+ CHECK_EQ(args.Length(), 0);
+ obj->streaming_->Finish();
+}
+
+void WasmStreamingObject::Abort(const FunctionCallbackInfo<Value>& args) {
+ WasmStreamingObject* obj;
+ ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());
+ CHECK(obj->streaming_);
+
+ CHECK_EQ(args.Length(), 1);
+ obj->streaming_->Abort(args[0]);
+}
+
+void StartStreamingCompilation(const FunctionCallbackInfo<Value>& info) {
+ // V8 passes an instance of v8::WasmStreaming to this callback, which we can
+ // use to pass the WebAssembly module bytes to V8 as we receive them.
+ // Unfortunately, our fetch() implementation is a JavaScript dependency, so it
+ // is difficult to implement the required logic here. Instead, we create a
+ // a WasmStreamingObject that encapsulates v8::WasmStreaming and that we can
+ // pass to the JavaScript implementation. The JavaScript implementation can
+ // then push() bytes from the Response and eventually either finish() or
+ // abort() the operation.
+
+ // Create the wrapper object.
+ std::shared_ptr<WasmStreaming> streaming =
+ WasmStreaming::Unpack(info.GetIsolate(), info.Data());
+ Environment* env = Environment::GetCurrent(info);
+ Local<Object> obj;
+ if (!WasmStreamingObject::Create(env, streaming).ToLocal(&obj)) {
+ // A JavaScript exception is pending. Let V8 deal with it.
+ return;
+ }
+
+ // V8 always passes one argument to this callback.
+ CHECK_EQ(info.Length(), 1);
+
+ // Prepare the JavaScript implementation for invocation. We will pass the
+ // WasmStreamingObject as the first argument, followed by the argument that we
+ // received from V8, i.e., the first argument passed to compileStreaming (or
+ // instantiateStreaming).
+ Local<Function> impl = env->wasm_streaming_compilation_impl();
+ CHECK(!impl.IsEmpty());
+ Local<Value> args[] = {obj, info[0]};
+
+ // Hand control to the JavaScript implementation. It should never throw an
+ // error, but if it does, we leave it to the calling V8 code to handle that
+ // gracefully. Otherwise, we assert that the JavaScript function does not
+ // return anything.
+ MaybeLocal<Value> maybe_ret =
+ impl->Call(env->context(), info.This(), 2, args);
+ Local<Value> ret;
+ CHECK_IMPLIES(maybe_ret.ToLocal(&ret), ret->IsUndefined());
+}
+
+// Called once by JavaScript during initialization.
+void SetImplementation(const FunctionCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ env->set_wasm_streaming_compilation_impl(info[0].As<Function>());
+}
+
+void Initialize(Local<Object> target,
+ Local<Value>,
+ Local<Context> context,
+ void*) {
+ Environment* env = Environment::GetCurrent(context);
+ env->SetMethod(target, "setImplementation", SetImplementation);
+}
+
+void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
+ registry->Register(SetImplementation);
+}
+
+} // namespace wasm_web_api
+} // namespace node
+
+NODE_MODULE_CONTEXT_AWARE_INTERNAL(wasm_web_api, node::wasm_web_api::Initialize)
+NODE_MODULE_EXTERNAL_REFERENCE(wasm_web_api,
+ node::wasm_web_api::RegisterExternalReferences)
diff --git a/src/node_wasm_web_api.h b/src/node_wasm_web_api.h
new file mode 100644
index 00000000000..9f5fe868167
--- /dev/null
+++ b/src/node_wasm_web_api.h
@@ -0,0 +1,54 @@
+#ifndef SRC_NODE_WASM_WEB_API_H_
+#define SRC_NODE_WASM_WEB_API_H_
+
+#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#include "base_object-inl.h"
+#include "v8.h"
+
+namespace node {
+namespace wasm_web_api {
+
+// Wrapper for interacting with a v8::WasmStreaming instance from JavaScript.
+class WasmStreamingObject final : public BaseObject {
+ public:
+ static v8::Local<v8::Function> Initialize(Environment* env);
+
+ static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
+
+ void MemoryInfo(MemoryTracker* tracker) const override;
+ SET_MEMORY_INFO_NAME(WasmStreamingObject)
+ SET_SELF_SIZE(WasmStreamingObject)
+
+ static v8::MaybeLocal<v8::Object> Create(
+ Environment* env, std::shared_ptr<v8::WasmStreaming> streaming);
+
+ private:
+ WasmStreamingObject(Environment* env, v8::Local<v8::Object> object)
+ : BaseObject(env, object) {
+ MakeWeak();
+ }
+
+ ~WasmStreamingObject() override {}
+
+ private:
+ static void New(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 Abort(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+ std::shared_ptr<v8::WasmStreaming> streaming_;
+ size_t wasm_size_;
+};
+
+// This is a v8::WasmStreamingCallback implementation that must be passed to
+// v8::Isolate::SetWasmStreamingCallback when setting up the isolate in order to
+// enable the WebAssembly.(compile|instantiate)Streaming APIs.
+void StartStreamingCompilation(const v8::FunctionCallbackInfo<v8::Value>& args);
+
+} // namespace wasm_web_api
+} // namespace node
+
+#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
+
+#endif // SRC_NODE_WASM_WEB_API_H_