GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_wasm_web_api.cc Lines: 73 86 84.9 %
Date: 2022-04-25 04:18:27 Branches: 25 46 54.3 %

Line Branch Exec Source
1
#include "node_wasm_web_api.h"
2
3
#include "memory_tracker-inl.h"
4
#include "node_errors.h"
5
6
namespace node {
7
namespace wasm_web_api {
8
9
using v8::ArrayBuffer;
10
using v8::ArrayBufferView;
11
using v8::Context;
12
using v8::Function;
13
using v8::FunctionCallbackInfo;
14
using v8::FunctionTemplate;
15
using v8::Local;
16
using v8::MaybeLocal;
17
using v8::Object;
18
using v8::Value;
19
using v8::WasmStreaming;
20
21
218
Local<Function> WasmStreamingObject::Initialize(Environment* env) {
22
218
  Local<Function> templ = env->wasm_streaming_object_constructor();
23
218
  if (!templ.IsEmpty()) {
24
211
    return templ;
25
  }
26
27
7
  Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
28
7
  t->Inherit(BaseObject::GetConstructorTemplate(env));
29
14
  t->InstanceTemplate()->SetInternalFieldCount(
30
      WasmStreamingObject::kInternalFieldCount);
31
32
7
  env->SetProtoMethod(t, "push", Push);
33
7
  env->SetProtoMethod(t, "finish", Finish);
34
7
  env->SetProtoMethod(t, "abort", Abort);
35
36
7
  auto function = t->GetFunction(env->context()).ToLocalChecked();
37
7
  env->set_wasm_streaming_object_constructor(function);
38
7
  return function;
39
}
40
41
void WasmStreamingObject::RegisterExternalReferences(
42
    ExternalReferenceRegistry* registry) {
43
  registry->Register(Push);
44
  registry->Register(Finish);
45
  registry->Register(Abort);
46
}
47
48
void WasmStreamingObject::MemoryInfo(MemoryTracker* tracker) const {
49
  // v8::WasmStreaming is opaque. We assume that the size of the WebAssembly
50
  // module that is being compiled is roughly what V8 allocates (as in, off by
51
  // only a small factor).
52
  tracker->TrackFieldWithSize("streaming", wasm_size_);
53
}
54
55
218
MaybeLocal<Object> WasmStreamingObject::Create(
56
    Environment* env, std::shared_ptr<WasmStreaming> streaming) {
57
218
  Local<Function> ctor = Initialize(env);
58
  Local<Object> obj;
59
436
  if (!ctor->NewInstance(env->context(), 0, nullptr).ToLocal(&obj)) {
60
    return MaybeLocal<Object>();
61
  }
62
63
218
  CHECK(streaming);
64
65
218
  WasmStreamingObject* ptr = Unwrap<WasmStreamingObject>(obj);
66
218
  CHECK_NOT_NULL(ptr);
67
218
  ptr->streaming_ = streaming;
68
218
  ptr->wasm_size_ = 0;
69
218
  return obj;
70
}
71
72
218
void WasmStreamingObject::New(const FunctionCallbackInfo<Value>& args) {
73
218
  CHECK(args.IsConstructCall());
74
218
  Environment* env = Environment::GetCurrent(args);
75
218
  new WasmStreamingObject(env, args.This());
76
218
}
77
78
346
void WasmStreamingObject::Push(const FunctionCallbackInfo<Value>& args) {
79
  WasmStreamingObject* obj;
80
346
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());
81
346
  CHECK(obj->streaming_);
82
83
346
  CHECK_EQ(args.Length(), 1);
84
346
  Local<Value> chunk = args[0];
85
86
  // The start of the memory section backing the ArrayBuffer(View), the offset
87
  // of the ArrayBuffer(View) within the memory section, and its size in bytes.
88
  const void* bytes;
89
  size_t offset;
90
  size_t size;
91
92
346
  if (LIKELY(chunk->IsArrayBufferView())) {
93
344
    Local<ArrayBufferView> view = chunk.As<ArrayBufferView>();
94
688
    bytes = view->Buffer()->GetBackingStore()->Data();
95
344
    offset = view->ByteOffset();
96
344
    size = view->ByteLength();
97
2
  } else if (LIKELY(chunk->IsArrayBuffer())) {
98
2
    Local<ArrayBuffer> buffer = chunk.As<ArrayBuffer>();
99
2
    bytes = buffer->GetBackingStore()->Data();
100
2
    offset = 0;
101
2
    size = buffer->ByteLength();
102
  } else {
103
    return node::THROW_ERR_INVALID_ARG_TYPE(
104
        Environment::GetCurrent(args),
105
        "chunk must be an ArrayBufferView or an ArrayBuffer");
106
  }
107
108
  // Forward the data to V8. Internally, V8 will make a copy.
109
346
  obj->streaming_->OnBytesReceived(static_cast<const uint8_t*>(bytes) + offset,
110
                                   size);
111
346
  obj->wasm_size_ += size;
112
}
113
114
144
void WasmStreamingObject::Finish(const FunctionCallbackInfo<Value>& args) {
115
  WasmStreamingObject* obj;
116
144
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());
117
144
  CHECK(obj->streaming_);
118
119
144
  CHECK_EQ(args.Length(), 0);
120
144
  obj->streaming_->Finish();
121
}
122
123
74
void WasmStreamingObject::Abort(const FunctionCallbackInfo<Value>& args) {
124
  WasmStreamingObject* obj;
125
74
  ASSIGN_OR_RETURN_UNWRAP(&obj, args.Holder());
126
74
  CHECK(obj->streaming_);
127
128
74
  CHECK_EQ(args.Length(), 1);
129
148
  obj->streaming_->Abort(args[0]);
130
}
131
132
218
void StartStreamingCompilation(const FunctionCallbackInfo<Value>& info) {
133
  // V8 passes an instance of v8::WasmStreaming to this callback, which we can
134
  // use to pass the WebAssembly module bytes to V8 as we receive them.
135
  // Unfortunately, our fetch() implementation is a JavaScript dependency, so it
136
  // is difficult to implement the required logic here. Instead, we create a
137
  // a WasmStreamingObject that encapsulates v8::WasmStreaming and that we can
138
  // pass to the JavaScript implementation. The JavaScript implementation can
139
  // then push() bytes from the Response and eventually either finish() or
140
  // abort() the operation.
141
142
  // Create the wrapper object.
143
  std::shared_ptr<WasmStreaming> streaming =
144
218
      WasmStreaming::Unpack(info.GetIsolate(), info.Data());
145
218
  Environment* env = Environment::GetCurrent(info);
146
  Local<Object> obj;
147
436
  if (!WasmStreamingObject::Create(env, streaming).ToLocal(&obj)) {
148
    // A JavaScript exception is pending. Let V8 deal with it.
149
    return;
150
  }
151
152
  // V8 always passes one argument to this callback.
153
218
  CHECK_EQ(info.Length(), 1);
154
155
  // Prepare the JavaScript implementation for invocation. We will pass the
156
  // WasmStreamingObject as the first argument, followed by the argument that we
157
  // received from V8, i.e., the first argument passed to compileStreaming (or
158
  // instantiateStreaming).
159
218
  Local<Function> impl = env->wasm_streaming_compilation_impl();
160
218
  CHECK(!impl.IsEmpty());
161
436
  Local<Value> args[] = {obj, info[0]};
162
163
  // Hand control to the JavaScript implementation. It should never throw an
164
  // error, but if it does, we leave it to the calling V8 code to handle that
165
  // gracefully. Otherwise, we assert that the JavaScript function does not
166
  // return anything.
167
  MaybeLocal<Value> maybe_ret =
168
436
      impl->Call(env->context(), info.This(), 2, args);
169
  Local<Value> ret;
170

654
  CHECK_IMPLIES(maybe_ret.ToLocal(&ret), ret->IsUndefined());
171
}
172
173
// Called once by JavaScript during initialization.
174
5613
void SetImplementation(const FunctionCallbackInfo<Value>& info) {
175
5613
  Environment* env = Environment::GetCurrent(info);
176
11226
  env->set_wasm_streaming_compilation_impl(info[0].As<Function>());
177
5613
}
178
179
5613
void Initialize(Local<Object> target,
180
                Local<Value>,
181
                Local<Context> context,
182
                void*) {
183
5613
  Environment* env = Environment::GetCurrent(context);
184
5613
  env->SetMethod(target, "setImplementation", SetImplementation);
185
5613
}
186
187
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
188
  registry->Register(SetImplementation);
189
}
190
191
}  // namespace wasm_web_api
192
}  // namespace node
193
194
5082
NODE_MODULE_CONTEXT_AWARE_INTERNAL(wasm_web_api, node::wasm_web_api::Initialize)
195
NODE_MODULE_EXTERNAL_REFERENCE(wasm_web_api,
196
                               node::wasm_web_api::RegisterExternalReferences)