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

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