GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_wasm_web_api.cc Lines: 93 99 93.9 %
Date: 2022-05-22 04:15:48 Branches: 29 54 53.7 %

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

684
  CHECK_IMPLIES(maybe_ret.ToLocal(&ret), ret->IsUndefined());
185
}
186
187
// Called once by JavaScript during initialization.
188
5990
void SetImplementation(const FunctionCallbackInfo<Value>& info) {
189
5990
  Environment* env = Environment::GetCurrent(info);
190
11980
  env->set_wasm_streaming_compilation_impl(info[0].As<Function>());
191
5990
}
192
193
5990
void Initialize(Local<Object> target,
194
                Local<Value>,
195
                Local<Context> context,
196
                void*) {
197
5990
  Environment* env = Environment::GetCurrent(context);
198
5990
  env->SetMethod(target, "setImplementation", SetImplementation);
199
5990
}
200
201
5184
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
202
5184
  registry->Register(SetImplementation);
203
5184
  registry->Register(StartStreamingCompilation);
204
5184
  WasmStreamingObject::RegisterExternalReferences(registry);
205
5184
}
206
207
}  // namespace wasm_web_api
208
}  // namespace node
209
210
5252
NODE_MODULE_CONTEXT_AWARE_INTERNAL(wasm_web_api, node::wasm_web_api::Initialize)
211
5184
NODE_MODULE_EXTERNAL_REFERENCE(wasm_web_api,
212
                               node::wasm_web_api::RegisterExternalReferences)