1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/wasm/wasm-js.h"
6
7 #include <cinttypes>
8 #include <cstring>
9
10 #include "src/api/api-inl.h"
11 #include "src/api/api-natives.h"
12 #include "src/ast/ast.h"
13 #include "src/base/logging.h"
14 #include "src/base/overflowing-math.h"
15 #include "src/common/assert-scope.h"
16 #include "src/execution/execution.h"
17 #include "src/execution/frames-inl.h"
18 #include "src/execution/isolate.h"
19 #include "src/handles/handles.h"
20 #include "src/heap/factory.h"
21 #include "src/init/v8.h"
22 #include "src/objects/js-collection-inl.h"
23 #include "src/objects/js-promise-inl.h"
24 #include "src/objects/objects-inl.h"
25 #include "src/objects/templates.h"
26 #include "src/parsing/parse-info.h"
27 #include "src/tasks/task-utils.h"
28 #include "src/trap-handler/trap-handler.h"
29 #include "src/wasm/function-compiler.h"
30 #include "src/wasm/streaming-decoder.h"
31 #include "src/wasm/value-type.h"
32 #include "src/wasm/wasm-debug.h"
33 #include "src/wasm/wasm-engine.h"
34 #include "src/wasm/wasm-limits.h"
35 #include "src/wasm/wasm-objects-inl.h"
36 #include "src/wasm/wasm-serialization.h"
37 #include "src/wasm/wasm-value.h"
38
39 using v8::internal::wasm::ErrorThrower;
40 using v8::internal::wasm::ScheduledErrorThrower;
41
42 namespace v8 {
43
44 class WasmStreaming::WasmStreamingImpl {
45 public:
WasmStreamingImpl(Isolate * isolate,const char * api_method_name,std::shared_ptr<internal::wasm::CompilationResultResolver> resolver)46 WasmStreamingImpl(
47 Isolate* isolate, const char* api_method_name,
48 std::shared_ptr<internal::wasm::CompilationResultResolver> resolver)
49 : isolate_(isolate), resolver_(std::move(resolver)) {
50 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate_);
51 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
52 streaming_decoder_ = i_isolate->wasm_engine()->StartStreamingCompilation(
53 i_isolate, enabled_features, handle(i_isolate->context(), i_isolate),
54 api_method_name, resolver_);
55 }
56
OnBytesReceived(const uint8_t * bytes,size_t size)57 void OnBytesReceived(const uint8_t* bytes, size_t size) {
58 streaming_decoder_->OnBytesReceived(i::VectorOf(bytes, size));
59 }
Finish()60 void Finish() { streaming_decoder_->Finish(); }
61
Abort(MaybeLocal<Value> exception)62 void Abort(MaybeLocal<Value> exception) {
63 i::HandleScope scope(reinterpret_cast<i::Isolate*>(isolate_));
64 streaming_decoder_->Abort();
65
66 // If no exception value is provided, we do not reject the promise. This can
67 // happen when streaming compilation gets aborted when no script execution
68 // is allowed anymore, e.g. when a browser tab gets refreshed.
69 if (exception.IsEmpty()) return;
70
71 resolver_->OnCompilationFailed(
72 Utils::OpenHandle(*exception.ToLocalChecked()));
73 }
74
SetCompiledModuleBytes(const uint8_t * bytes,size_t size)75 bool SetCompiledModuleBytes(const uint8_t* bytes, size_t size) {
76 if (!i::wasm::IsSupportedVersion({bytes, size})) return false;
77 return streaming_decoder_->SetCompiledModuleBytes({bytes, size});
78 }
79
SetClient(std::shared_ptr<Client> client)80 void SetClient(std::shared_ptr<Client> client) {
81 streaming_decoder_->SetModuleCompiledCallback(
82 [client, streaming_decoder = streaming_decoder_](
83 const std::shared_ptr<i::wasm::NativeModule>& native_module) {
84 i::Vector<const char> url = streaming_decoder->url();
85 auto compiled_wasm_module =
86 CompiledWasmModule(native_module, url.begin(), url.size());
87 client->OnModuleCompiled(compiled_wasm_module);
88 });
89 }
90
SetUrl(internal::Vector<const char> url)91 void SetUrl(internal::Vector<const char> url) {
92 streaming_decoder_->SetUrl(url);
93 }
94
95 private:
96 Isolate* const isolate_;
97 std::shared_ptr<internal::wasm::StreamingDecoder> streaming_decoder_;
98 std::shared_ptr<internal::wasm::CompilationResultResolver> resolver_;
99 };
100
WasmStreaming(std::unique_ptr<WasmStreamingImpl> impl)101 WasmStreaming::WasmStreaming(std::unique_ptr<WasmStreamingImpl> impl)
102 : impl_(std::move(impl)) {
103 TRACE_EVENT0("v8.wasm", "wasm.InitializeStreaming");
104 }
105
106 // The destructor is defined here because we have a unique_ptr with forward
107 // declaration.
108 WasmStreaming::~WasmStreaming() = default;
109
OnBytesReceived(const uint8_t * bytes,size_t size)110 void WasmStreaming::OnBytesReceived(const uint8_t* bytes, size_t size) {
111 TRACE_EVENT1("v8.wasm", "wasm.OnBytesReceived", "bytes", size);
112 impl_->OnBytesReceived(bytes, size);
113 }
114
Finish()115 void WasmStreaming::Finish() {
116 TRACE_EVENT0("v8.wasm", "wasm.FinishStreaming");
117 impl_->Finish();
118 }
119
Abort(MaybeLocal<Value> exception)120 void WasmStreaming::Abort(MaybeLocal<Value> exception) {
121 TRACE_EVENT0("v8.wasm", "wasm.AbortStreaming");
122 impl_->Abort(exception);
123 }
124
SetCompiledModuleBytes(const uint8_t * bytes,size_t size)125 bool WasmStreaming::SetCompiledModuleBytes(const uint8_t* bytes, size_t size) {
126 TRACE_EVENT0("v8.wasm", "wasm.SetCompiledModuleBytes");
127 return impl_->SetCompiledModuleBytes(bytes, size);
128 }
129
SetClient(std::shared_ptr<Client> client)130 void WasmStreaming::SetClient(std::shared_ptr<Client> client) {
131 TRACE_EVENT0("v8.wasm", "wasm.WasmStreaming.SetClient");
132 impl_->SetClient(client);
133 }
134
SetUrl(const char * url,size_t length)135 void WasmStreaming::SetUrl(const char* url, size_t length) {
136 TRACE_EVENT0("v8.wasm", "wasm.SetUrl");
137 impl_->SetUrl(internal::VectorOf(url, length));
138 }
139
140 // static
Unpack(Isolate * isolate,Local<Value> value)141 std::shared_ptr<WasmStreaming> WasmStreaming::Unpack(Isolate* isolate,
142 Local<Value> value) {
143 TRACE_EVENT0("v8.wasm", "wasm.WasmStreaming.Unpack");
144 i::HandleScope scope(reinterpret_cast<i::Isolate*>(isolate));
145 auto managed =
146 i::Handle<i::Managed<WasmStreaming>>::cast(Utils::OpenHandle(*value));
147 return managed->get();
148 }
149
150 namespace {
151
152 #define ASSIGN(type, var, expr) \
153 Local<type> var; \
154 do { \
155 if (!expr.ToLocal(&var)) { \
156 DCHECK(i_isolate->has_scheduled_exception()); \
157 return; \
158 } else { \
159 DCHECK(!i_isolate->has_scheduled_exception()); \
160 } \
161 } while (false)
162
v8_str(i::Isolate * isolate,const char * str)163 i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
164 return isolate->factory()->NewStringFromAsciiChecked(str);
165 }
v8_str(Isolate * isolate,const char * str)166 Local<String> v8_str(Isolate* isolate, const char* str) {
167 return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str));
168 }
169
170 #define GET_FIRST_ARGUMENT_AS(Type) \
171 i::MaybeHandle<i::Wasm##Type##Object> GetFirstArgumentAs##Type( \
172 const v8::FunctionCallbackInfo<v8::Value>& args, \
173 ErrorThrower* thrower) { \
174 i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]); \
175 if (!arg0->IsWasm##Type##Object()) { \
176 thrower->TypeError("Argument 0 must be a WebAssembly." #Type); \
177 return {}; \
178 } \
179 Local<Object> obj = Local<Object>::Cast(args[0]); \
180 return i::Handle<i::Wasm##Type##Object>::cast( \
181 v8::Utils::OpenHandle(*obj)); \
182 }
183
184 GET_FIRST_ARGUMENT_AS(Module)
GET_FIRST_ARGUMENT_AS(Memory)185 GET_FIRST_ARGUMENT_AS(Memory)
186 GET_FIRST_ARGUMENT_AS(Table)
187 GET_FIRST_ARGUMENT_AS(Global)
188
189 #undef GET_FIRST_ARGUMENT_AS
190
191 i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
192 const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower,
193 bool* is_shared) {
194 const uint8_t* start = nullptr;
195 size_t length = 0;
196 v8::Local<v8::Value> source = args[0];
197 if (source->IsArrayBuffer()) {
198 // A raw array buffer was passed.
199 Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source);
200 auto backing_store = buffer->GetBackingStore();
201
202 start = reinterpret_cast<const uint8_t*>(backing_store->Data());
203 length = backing_store->ByteLength();
204 *is_shared = buffer->IsSharedArrayBuffer();
205 } else if (source->IsTypedArray()) {
206 // A TypedArray was passed.
207 Local<TypedArray> array = Local<TypedArray>::Cast(source);
208 Local<ArrayBuffer> buffer = array->Buffer();
209
210 auto backing_store = buffer->GetBackingStore();
211
212 start = reinterpret_cast<const uint8_t*>(backing_store->Data()) +
213 array->ByteOffset();
214 length = array->ByteLength();
215 *is_shared = buffer->IsSharedArrayBuffer();
216 } else {
217 thrower->TypeError("Argument 0 must be a buffer source");
218 }
219 DCHECK_IMPLIES(length, start != nullptr);
220 if (length == 0) {
221 thrower->CompileError("BufferSource argument is empty");
222 }
223 size_t max_length = i::wasm::max_module_size();
224 if (length > max_length) {
225 thrower->RangeError("buffer source exceeds maximum size of %zu (is %zu)",
226 max_length, length);
227 }
228 if (thrower->error()) return i::wasm::ModuleWireBytes(nullptr, nullptr);
229 return i::wasm::ModuleWireBytes(start, start + length);
230 }
231
GetValueAsImports(Local<Value> arg,ErrorThrower * thrower)232 i::MaybeHandle<i::JSReceiver> GetValueAsImports(Local<Value> arg,
233 ErrorThrower* thrower) {
234 if (arg->IsUndefined()) return {};
235
236 if (!arg->IsObject()) {
237 thrower->TypeError("Argument 1 must be an object");
238 return {};
239 }
240 Local<Object> obj = Local<Object>::Cast(arg);
241 return i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
242 }
243
244 namespace {
245 // This class resolves the result of WebAssembly.compile. It just places the
246 // compilation result in the supplied {promise}.
247 class AsyncCompilationResolver : public i::wasm::CompilationResultResolver {
248 public:
AsyncCompilationResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise)249 AsyncCompilationResolver(i::Isolate* isolate, i::Handle<i::JSPromise> promise)
250 : promise_(isolate->global_handles()->Create(*promise)) {
251 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
252 kGlobalPromiseHandle);
253 }
254
~AsyncCompilationResolver()255 ~AsyncCompilationResolver() override {
256 i::GlobalHandles::Destroy(promise_.location());
257 }
258
OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result)259 void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result) override {
260 if (finished_) return;
261 finished_ = true;
262 i::MaybeHandle<i::Object> promise_result =
263 i::JSPromise::Resolve(promise_, result);
264 CHECK_EQ(promise_result.is_null(),
265 promise_->GetIsolate()->has_pending_exception());
266 }
267
OnCompilationFailed(i::Handle<i::Object> error_reason)268 void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
269 if (finished_) return;
270 finished_ = true;
271 i::MaybeHandle<i::Object> promise_result =
272 i::JSPromise::Reject(promise_, error_reason);
273 CHECK_EQ(promise_result.is_null(),
274 promise_->GetIsolate()->has_pending_exception());
275 }
276
277 private:
278 static constexpr char kGlobalPromiseHandle[] =
279 "AsyncCompilationResolver::promise_";
280 bool finished_ = false;
281 i::Handle<i::JSPromise> promise_;
282 };
283
284 constexpr char AsyncCompilationResolver::kGlobalPromiseHandle[];
285
286 // This class resolves the result of WebAssembly.instantiate(module, imports).
287 // It just places the instantiation result in the supplied {promise}.
288 class InstantiateModuleResultResolver
289 : public i::wasm::InstantiationResultResolver {
290 public:
InstantiateModuleResultResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise)291 InstantiateModuleResultResolver(i::Isolate* isolate,
292 i::Handle<i::JSPromise> promise)
293 : promise_(isolate->global_handles()->Create(*promise)) {
294 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
295 kGlobalPromiseHandle);
296 }
297
~InstantiateModuleResultResolver()298 ~InstantiateModuleResultResolver() override {
299 i::GlobalHandles::Destroy(promise_.location());
300 }
301
OnInstantiationSucceeded(i::Handle<i::WasmInstanceObject> instance)302 void OnInstantiationSucceeded(
303 i::Handle<i::WasmInstanceObject> instance) override {
304 i::MaybeHandle<i::Object> promise_result =
305 i::JSPromise::Resolve(promise_, instance);
306 CHECK_EQ(promise_result.is_null(),
307 promise_->GetIsolate()->has_pending_exception());
308 }
309
OnInstantiationFailed(i::Handle<i::Object> error_reason)310 void OnInstantiationFailed(i::Handle<i::Object> error_reason) override {
311 i::MaybeHandle<i::Object> promise_result =
312 i::JSPromise::Reject(promise_, error_reason);
313 CHECK_EQ(promise_result.is_null(),
314 promise_->GetIsolate()->has_pending_exception());
315 }
316
317 private:
318 static constexpr char kGlobalPromiseHandle[] =
319 "InstantiateModuleResultResolver::promise_";
320 i::Handle<i::JSPromise> promise_;
321 };
322
323 constexpr char InstantiateModuleResultResolver::kGlobalPromiseHandle[];
324
325 // This class resolves the result of WebAssembly.instantiate(bytes, imports).
326 // For that it creates a new {JSObject} which contains both the provided
327 // {WasmModuleObject} and the resulting {WebAssemblyInstanceObject} itself.
328 class InstantiateBytesResultResolver
329 : public i::wasm::InstantiationResultResolver {
330 public:
InstantiateBytesResultResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise,i::Handle<i::WasmModuleObject> module)331 InstantiateBytesResultResolver(i::Isolate* isolate,
332 i::Handle<i::JSPromise> promise,
333 i::Handle<i::WasmModuleObject> module)
334 : isolate_(isolate),
335 promise_(isolate_->global_handles()->Create(*promise)),
336 module_(isolate_->global_handles()->Create(*module)) {
337 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
338 kGlobalPromiseHandle);
339 i::GlobalHandles::AnnotateStrongRetainer(module_.location(),
340 kGlobalModuleHandle);
341 }
342
~InstantiateBytesResultResolver()343 ~InstantiateBytesResultResolver() override {
344 i::GlobalHandles::Destroy(promise_.location());
345 i::GlobalHandles::Destroy(module_.location());
346 }
347
OnInstantiationSucceeded(i::Handle<i::WasmInstanceObject> instance)348 void OnInstantiationSucceeded(
349 i::Handle<i::WasmInstanceObject> instance) override {
350 // The result is a JSObject with 2 fields which contain the
351 // WasmInstanceObject and the WasmModuleObject.
352 i::Handle<i::JSObject> result =
353 isolate_->factory()->NewJSObject(isolate_->object_function());
354
355 i::Handle<i::String> instance_name =
356 isolate_->factory()->NewStringFromStaticChars("instance");
357
358 i::Handle<i::String> module_name =
359 isolate_->factory()->NewStringFromStaticChars("module");
360
361 i::JSObject::AddProperty(isolate_, result, instance_name, instance,
362 i::NONE);
363 i::JSObject::AddProperty(isolate_, result, module_name, module_, i::NONE);
364
365 i::MaybeHandle<i::Object> promise_result =
366 i::JSPromise::Resolve(promise_, result);
367 CHECK_EQ(promise_result.is_null(), isolate_->has_pending_exception());
368 }
369
OnInstantiationFailed(i::Handle<i::Object> error_reason)370 void OnInstantiationFailed(i::Handle<i::Object> error_reason) override {
371 i::MaybeHandle<i::Object> promise_result =
372 i::JSPromise::Reject(promise_, error_reason);
373 CHECK_EQ(promise_result.is_null(), isolate_->has_pending_exception());
374 }
375
376 private:
377 static constexpr char kGlobalPromiseHandle[] =
378 "InstantiateBytesResultResolver::promise_";
379 static constexpr char kGlobalModuleHandle[] =
380 "InstantiateBytesResultResolver::module_";
381 i::Isolate* isolate_;
382 i::Handle<i::JSPromise> promise_;
383 i::Handle<i::WasmModuleObject> module_;
384 };
385
386 constexpr char InstantiateBytesResultResolver::kGlobalPromiseHandle[];
387 constexpr char InstantiateBytesResultResolver::kGlobalModuleHandle[];
388
389 // This class is the {CompilationResultResolver} for
390 // WebAssembly.instantiate(bytes, imports). When compilation finishes,
391 // {AsyncInstantiate} is started on the compilation result.
392 class AsyncInstantiateCompileResultResolver
393 : public i::wasm::CompilationResultResolver {
394 public:
AsyncInstantiateCompileResultResolver(i::Isolate * isolate,i::Handle<i::JSPromise> promise,i::MaybeHandle<i::JSReceiver> maybe_imports)395 AsyncInstantiateCompileResultResolver(
396 i::Isolate* isolate, i::Handle<i::JSPromise> promise,
397 i::MaybeHandle<i::JSReceiver> maybe_imports)
398 : isolate_(isolate),
399 promise_(isolate_->global_handles()->Create(*promise)),
400 maybe_imports_(maybe_imports.is_null()
401 ? maybe_imports
402 : isolate_->global_handles()->Create(
403 *maybe_imports.ToHandleChecked())) {
404 i::GlobalHandles::AnnotateStrongRetainer(promise_.location(),
405 kGlobalPromiseHandle);
406 if (!maybe_imports_.is_null()) {
407 i::GlobalHandles::AnnotateStrongRetainer(
408 maybe_imports_.ToHandleChecked().location(), kGlobalImportsHandle);
409 }
410 }
411
~AsyncInstantiateCompileResultResolver()412 ~AsyncInstantiateCompileResultResolver() override {
413 i::GlobalHandles::Destroy(promise_.location());
414 if (!maybe_imports_.is_null()) {
415 i::GlobalHandles::Destroy(maybe_imports_.ToHandleChecked().location());
416 }
417 }
418
OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result)419 void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result) override {
420 if (finished_) return;
421 finished_ = true;
422 isolate_->wasm_engine()->AsyncInstantiate(
423 isolate_,
424 std::make_unique<InstantiateBytesResultResolver>(isolate_, promise_,
425 result),
426 result, maybe_imports_);
427 }
428
OnCompilationFailed(i::Handle<i::Object> error_reason)429 void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
430 if (finished_) return;
431 finished_ = true;
432 i::MaybeHandle<i::Object> promise_result =
433 i::JSPromise::Reject(promise_, error_reason);
434 CHECK_EQ(promise_result.is_null(), isolate_->has_pending_exception());
435 }
436
437 private:
438 static constexpr char kGlobalPromiseHandle[] =
439 "AsyncInstantiateCompileResultResolver::promise_";
440 static constexpr char kGlobalImportsHandle[] =
441 "AsyncInstantiateCompileResultResolver::module_";
442 bool finished_ = false;
443 i::Isolate* isolate_;
444 i::Handle<i::JSPromise> promise_;
445 i::MaybeHandle<i::JSReceiver> maybe_imports_;
446 };
447
448 constexpr char AsyncInstantiateCompileResultResolver::kGlobalPromiseHandle[];
449 constexpr char AsyncInstantiateCompileResultResolver::kGlobalImportsHandle[];
450
ToString(const char * name)451 std::string ToString(const char* name) { return std::string(name); }
452
ToString(const i::Handle<i::String> name)453 std::string ToString(const i::Handle<i::String> name) {
454 return std::string("Property '") + name->ToCString().get() + "'";
455 }
456
457 // Web IDL: '[EnforceRange] unsigned long'
458 // Previously called ToNonWrappingUint32 in the draft WebAssembly JS spec.
459 // https://heycam.github.io/webidl/#EnforceRange
460 template <typename T>
EnforceUint32(T argument_name,Local<v8::Value> v,Local<Context> context,ErrorThrower * thrower,uint32_t * res)461 bool EnforceUint32(T argument_name, Local<v8::Value> v, Local<Context> context,
462 ErrorThrower* thrower, uint32_t* res) {
463 double double_number;
464
465 if (!v->NumberValue(context).To(&double_number)) {
466 thrower->TypeError("%s must be convertible to a number",
467 ToString(argument_name).c_str());
468 return false;
469 }
470 if (!std::isfinite(double_number)) {
471 thrower->TypeError("%s must be convertible to a valid number",
472 ToString(argument_name).c_str());
473 return false;
474 }
475 if (double_number < 0) {
476 thrower->TypeError("%s must be non-negative",
477 ToString(argument_name).c_str());
478 return false;
479 }
480 if (double_number > std::numeric_limits<uint32_t>::max()) {
481 thrower->TypeError("%s must be in the unsigned long range",
482 ToString(argument_name).c_str());
483 return false;
484 }
485
486 *res = static_cast<uint32_t>(double_number);
487 return true;
488 }
489 } // namespace
490
491 // WebAssembly.compile(bytes) -> Promise
WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value> & args)492 void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
493 constexpr const char* kAPIMethodName = "WebAssembly.compile()";
494 v8::Isolate* isolate = args.GetIsolate();
495 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
496
497 HandleScope scope(isolate);
498 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
499
500 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
501 thrower.CompileError("Wasm code generation disallowed by embedder");
502 }
503
504 Local<Context> context = isolate->GetCurrentContext();
505 ASSIGN(Promise::Resolver, promise_resolver, Promise::Resolver::New(context));
506 Local<Promise> promise = promise_resolver->GetPromise();
507 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
508 return_value.Set(promise);
509
510 std::shared_ptr<i::wasm::CompilationResultResolver> resolver(
511 new AsyncCompilationResolver(i_isolate, Utils::OpenHandle(*promise)));
512
513 bool is_shared = false;
514 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
515 if (thrower.error()) {
516 resolver->OnCompilationFailed(thrower.Reify());
517 return;
518 }
519 // Asynchronous compilation handles copying wire bytes if necessary.
520 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
521 i_isolate->wasm_engine()->AsyncCompile(i_isolate, enabled_features,
522 std::move(resolver), bytes, is_shared,
523 kAPIMethodName);
524 }
525
WasmStreamingCallbackForTesting(const v8::FunctionCallbackInfo<v8::Value> & args)526 void WasmStreamingCallbackForTesting(
527 const v8::FunctionCallbackInfo<v8::Value>& args) {
528 v8::Isolate* isolate = args.GetIsolate();
529 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
530
531 HandleScope scope(isolate);
532 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.compile()");
533
534 std::shared_ptr<v8::WasmStreaming> streaming =
535 v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());
536
537 bool is_shared = false;
538 i::wasm::ModuleWireBytes bytes =
539 GetFirstArgumentAsBytes(args, &thrower, &is_shared);
540 if (thrower.error()) {
541 streaming->Abort(Utils::ToLocal(thrower.Reify()));
542 return;
543 }
544 streaming->OnBytesReceived(bytes.start(), bytes.length());
545 streaming->Finish();
546 CHECK(!thrower.error());
547 }
548
WasmStreamingPromiseFailedCallback(const v8::FunctionCallbackInfo<v8::Value> & args)549 void WasmStreamingPromiseFailedCallback(
550 const v8::FunctionCallbackInfo<v8::Value>& args) {
551 std::shared_ptr<v8::WasmStreaming> streaming =
552 v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());
553 streaming->Abort(args[0]);
554 }
555
556 // WebAssembly.compileStreaming(Response | Promise<Response>)
557 // -> Promise<WebAssembly.Module>
WebAssemblyCompileStreaming(const v8::FunctionCallbackInfo<v8::Value> & args)558 void WebAssemblyCompileStreaming(
559 const v8::FunctionCallbackInfo<v8::Value>& args) {
560 v8::Isolate* isolate = args.GetIsolate();
561 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
562 HandleScope scope(isolate);
563 const char* const kAPIMethodName = "WebAssembly.compileStreaming()";
564 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
565 Local<Context> context = isolate->GetCurrentContext();
566
567 // Create and assign the return value of this function.
568 ASSIGN(Promise::Resolver, result_resolver, Promise::Resolver::New(context));
569 Local<Promise> promise = result_resolver->GetPromise();
570 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
571 return_value.Set(promise);
572
573 // Prepare the CompilationResultResolver for the compilation.
574 auto resolver = std::make_shared<AsyncCompilationResolver>(
575 i_isolate, Utils::OpenHandle(*promise));
576
577 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
578 thrower.CompileError("Wasm code generation disallowed by embedder");
579 resolver->OnCompilationFailed(thrower.Reify());
580 return;
581 }
582
583 // Allocate the streaming decoder in a Managed so we can pass it to the
584 // embedder.
585 i::Handle<i::Managed<WasmStreaming>> data =
586 i::Managed<WasmStreaming>::Allocate(
587 i_isolate, 0,
588 std::make_unique<WasmStreaming::WasmStreamingImpl>(
589 isolate, kAPIMethodName, resolver));
590
591 DCHECK_NOT_NULL(i_isolate->wasm_streaming_callback());
592 ASSIGN(
593 v8::Function, compile_callback,
594 v8::Function::New(context, i_isolate->wasm_streaming_callback(),
595 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
596 ASSIGN(
597 v8::Function, reject_callback,
598 v8::Function::New(context, WasmStreamingPromiseFailedCallback,
599 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
600
601 // The parameter may be of type {Response} or of type {Promise<Response>}.
602 // Treat either case of parameter as Promise.resolve(parameter)
603 // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments
604
605 // Ending with:
606 // return Promise.resolve(parameter).then(compile_callback);
607 ASSIGN(Promise::Resolver, input_resolver, Promise::Resolver::New(context));
608 if (!input_resolver->Resolve(context, args[0]).IsJust()) return;
609
610 // We do not have any use of the result here. The {compile_callback} will
611 // start streaming compilation, which will eventually resolve the promise we
612 // set as result value.
613 USE(input_resolver->GetPromise()->Then(context, compile_callback,
614 reject_callback));
615 }
616
617 // WebAssembly.validate(bytes) -> bool
WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value> & args)618 void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
619 v8::Isolate* isolate = args.GetIsolate();
620 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
621 HandleScope scope(isolate);
622 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.validate()");
623
624 bool is_shared = false;
625 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
626
627 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
628
629 if (thrower.error()) {
630 if (thrower.wasm_error()) thrower.Reset(); // Clear error.
631 return_value.Set(v8::False(isolate));
632 return;
633 }
634
635 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
636 bool validated = false;
637 if (is_shared) {
638 // Make a copy of the wire bytes to avoid concurrent modification.
639 std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
640 memcpy(copy.get(), bytes.start(), bytes.length());
641 i::wasm::ModuleWireBytes bytes_copy(copy.get(),
642 copy.get() + bytes.length());
643 validated = i_isolate->wasm_engine()->SyncValidate(
644 i_isolate, enabled_features, bytes_copy);
645 } else {
646 // The wire bytes are not shared, OK to use them directly.
647 validated = i_isolate->wasm_engine()->SyncValidate(i_isolate,
648 enabled_features, bytes);
649 }
650
651 return_value.Set(Boolean::New(isolate, validated));
652 }
653
654 // new WebAssembly.Module(bytes) -> WebAssembly.Module
WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value> & args)655 void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
656 v8::Isolate* isolate = args.GetIsolate();
657 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
658 if (i_isolate->wasm_module_callback()(args)) return;
659
660 HandleScope scope(isolate);
661 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module()");
662
663 if (!args.IsConstructCall()) {
664 thrower.TypeError("WebAssembly.Module must be invoked with 'new'");
665 return;
666 }
667 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
668 thrower.CompileError("Wasm code generation disallowed by embedder");
669 return;
670 }
671
672 bool is_shared = false;
673 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
674
675 if (thrower.error()) {
676 return;
677 }
678 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
679 i::MaybeHandle<i::Object> module_obj;
680 if (is_shared) {
681 // Make a copy of the wire bytes to avoid concurrent modification.
682 std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
683 memcpy(copy.get(), bytes.start(), bytes.length());
684 i::wasm::ModuleWireBytes bytes_copy(copy.get(),
685 copy.get() + bytes.length());
686 module_obj = i_isolate->wasm_engine()->SyncCompile(
687 i_isolate, enabled_features, &thrower, bytes_copy);
688 } else {
689 // The wire bytes are not shared, OK to use them directly.
690 module_obj = i_isolate->wasm_engine()->SyncCompile(
691 i_isolate, enabled_features, &thrower, bytes);
692 }
693
694 if (module_obj.is_null()) return;
695
696 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
697 return_value.Set(Utils::ToLocal(module_obj.ToHandleChecked()));
698 }
699
700 // WebAssembly.Module.imports(module) -> Array<Import>
WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value> & args)701 void WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value>& args) {
702 HandleScope scope(args.GetIsolate());
703 v8::Isolate* isolate = args.GetIsolate();
704 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
705 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()");
706
707 auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
708 if (thrower.error()) return;
709 auto imports = i::wasm::GetImports(i_isolate, maybe_module.ToHandleChecked());
710 args.GetReturnValue().Set(Utils::ToLocal(imports));
711 }
712
713 // WebAssembly.Module.exports(module) -> Array<Export>
WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value> & args)714 void WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value>& args) {
715 HandleScope scope(args.GetIsolate());
716 v8::Isolate* isolate = args.GetIsolate();
717 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
718 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()");
719
720 auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
721 if (thrower.error()) return;
722 auto exports = i::wasm::GetExports(i_isolate, maybe_module.ToHandleChecked());
723 args.GetReturnValue().Set(Utils::ToLocal(exports));
724 }
725
726 // WebAssembly.Module.customSections(module, name) -> Array<Section>
WebAssemblyModuleCustomSections(const v8::FunctionCallbackInfo<v8::Value> & args)727 void WebAssemblyModuleCustomSections(
728 const v8::FunctionCallbackInfo<v8::Value>& args) {
729 HandleScope scope(args.GetIsolate());
730 v8::Isolate* isolate = args.GetIsolate();
731 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
732 ScheduledErrorThrower thrower(i_isolate,
733 "WebAssembly.Module.customSections()");
734
735 auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
736 if (thrower.error()) return;
737
738 if (args[1]->IsUndefined()) {
739 thrower.TypeError("Argument 1 is required");
740 return;
741 }
742
743 i::MaybeHandle<i::Object> maybe_name =
744 i::Object::ToString(i_isolate, Utils::OpenHandle(*args[1]));
745 i::Handle<i::Object> name;
746 if (!maybe_name.ToHandle(&name)) return;
747 auto custom_sections =
748 i::wasm::GetCustomSections(i_isolate, maybe_module.ToHandleChecked(),
749 i::Handle<i::String>::cast(name), &thrower);
750 if (thrower.error()) return;
751 args.GetReturnValue().Set(Utils::ToLocal(custom_sections));
752 }
753
WebAssemblyInstantiateImpl(Isolate * isolate,Local<Value> module,Local<Value> ffi)754 MaybeLocal<Value> WebAssemblyInstantiateImpl(Isolate* isolate,
755 Local<Value> module,
756 Local<Value> ffi) {
757 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
758
759 i::MaybeHandle<i::Object> instance_object;
760 {
761 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
762
763 // TODO(ahaas): These checks on the module should not be necessary here They
764 // are just a workaround for https://crbug.com/837417.
765 i::Handle<i::Object> module_obj = Utils::OpenHandle(*module);
766 if (!module_obj->IsWasmModuleObject()) {
767 thrower.TypeError("Argument 0 must be a WebAssembly.Module object");
768 return {};
769 }
770
771 i::MaybeHandle<i::JSReceiver> maybe_imports =
772 GetValueAsImports(ffi, &thrower);
773 if (thrower.error()) return {};
774
775 instance_object = i_isolate->wasm_engine()->SyncInstantiate(
776 i_isolate, &thrower, i::Handle<i::WasmModuleObject>::cast(module_obj),
777 maybe_imports, i::MaybeHandle<i::JSArrayBuffer>());
778 }
779
780 DCHECK_EQ(instance_object.is_null(), i_isolate->has_scheduled_exception());
781 if (instance_object.is_null()) return {};
782 return Utils::ToLocal(instance_object.ToHandleChecked());
783 }
784
785 // new WebAssembly.Instance(module, imports) -> WebAssembly.Instance
WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value> & args)786 void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
787 Isolate* isolate = args.GetIsolate();
788 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
789 i_isolate->CountUsage(
790 v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
791
792 HandleScope scope(args.GetIsolate());
793 if (i_isolate->wasm_instance_callback()(args)) return;
794
795 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
796 if (!args.IsConstructCall()) {
797 thrower.TypeError("WebAssembly.Instance must be invoked with 'new'");
798 return;
799 }
800
801 GetFirstArgumentAsModule(args, &thrower);
802 if (thrower.error()) return;
803
804 // If args.Length < 2, this will be undefined - see FunctionCallbackInfo.
805 // We'll check for that in WebAssemblyInstantiateImpl.
806 Local<Value> data = args[1];
807
808 Local<Value> instance;
809 if (WebAssemblyInstantiateImpl(isolate, args[0], data).ToLocal(&instance)) {
810 args.GetReturnValue().Set(instance);
811 }
812 }
813
814 // WebAssembly.instantiateStreaming(Response | Promise<Response> [, imports])
815 // -> Promise<ResultObject>
816 // (where ResultObject has a "module" and an "instance" field)
WebAssemblyInstantiateStreaming(const v8::FunctionCallbackInfo<v8::Value> & args)817 void WebAssemblyInstantiateStreaming(
818 const v8::FunctionCallbackInfo<v8::Value>& args) {
819 v8::Isolate* isolate = args.GetIsolate();
820 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
821 i_isolate->CountUsage(
822 v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
823
824 HandleScope scope(isolate);
825 Local<Context> context = isolate->GetCurrentContext();
826 const char* const kAPIMethodName = "WebAssembly.instantiateStreaming()";
827 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
828
829 // Create and assign the return value of this function.
830 ASSIGN(Promise::Resolver, result_resolver, Promise::Resolver::New(context));
831 Local<Promise> promise = result_resolver->GetPromise();
832 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
833 return_value.Set(promise);
834
835 // Create an InstantiateResultResolver in case there is an issue with the
836 // passed parameters.
837 std::unique_ptr<i::wasm::InstantiationResultResolver> resolver(
838 new InstantiateModuleResultResolver(i_isolate,
839 Utils::OpenHandle(*promise)));
840
841 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
842 thrower.CompileError("Wasm code generation disallowed by embedder");
843 resolver->OnInstantiationFailed(thrower.Reify());
844 return;
845 }
846
847 // If args.Length < 2, this will be undefined - see FunctionCallbackInfo.
848 Local<Value> ffi = args[1];
849 i::MaybeHandle<i::JSReceiver> maybe_imports =
850 GetValueAsImports(ffi, &thrower);
851
852 if (thrower.error()) {
853 resolver->OnInstantiationFailed(thrower.Reify());
854 return;
855 }
856
857 // We start compilation now, we have no use for the
858 // {InstantiationResultResolver}.
859 resolver.reset();
860
861 std::shared_ptr<i::wasm::CompilationResultResolver> compilation_resolver(
862 new AsyncInstantiateCompileResultResolver(
863 i_isolate, Utils::OpenHandle(*promise), maybe_imports));
864
865 // Allocate the streaming decoder in a Managed so we can pass it to the
866 // embedder.
867 i::Handle<i::Managed<WasmStreaming>> data =
868 i::Managed<WasmStreaming>::Allocate(
869 i_isolate, 0,
870 std::make_unique<WasmStreaming::WasmStreamingImpl>(
871 isolate, kAPIMethodName, compilation_resolver));
872
873 DCHECK_NOT_NULL(i_isolate->wasm_streaming_callback());
874 ASSIGN(
875 v8::Function, compile_callback,
876 v8::Function::New(context, i_isolate->wasm_streaming_callback(),
877 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
878 ASSIGN(
879 v8::Function, reject_callback,
880 v8::Function::New(context, WasmStreamingPromiseFailedCallback,
881 Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
882
883 // The parameter may be of type {Response} or of type {Promise<Response>}.
884 // Treat either case of parameter as Promise.resolve(parameter)
885 // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments
886
887 // Ending with:
888 // return Promise.resolve(parameter).then(compile_callback);
889 ASSIGN(Promise::Resolver, input_resolver, Promise::Resolver::New(context));
890 if (!input_resolver->Resolve(context, args[0]).IsJust()) return;
891
892 // We do not have any use of the result here. The {compile_callback} will
893 // start streaming compilation, which will eventually resolve the promise we
894 // set as result value.
895 USE(input_resolver->GetPromise()->Then(context, compile_callback,
896 reject_callback));
897 }
898
899 // WebAssembly.instantiate(module, imports) -> WebAssembly.Instance
900 // WebAssembly.instantiate(bytes, imports) ->
901 // {module: WebAssembly.Module, instance: WebAssembly.Instance}
WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value> & args)902 void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {
903 constexpr const char* kAPIMethodName = "WebAssembly.instantiate()";
904 v8::Isolate* isolate = args.GetIsolate();
905 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
906 i_isolate->CountUsage(
907 v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
908
909 ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
910
911 HandleScope scope(isolate);
912
913 Local<Context> context = isolate->GetCurrentContext();
914
915 ASSIGN(Promise::Resolver, promise_resolver, Promise::Resolver::New(context));
916 Local<Promise> promise = promise_resolver->GetPromise();
917 args.GetReturnValue().Set(promise);
918
919 std::unique_ptr<i::wasm::InstantiationResultResolver> resolver(
920 new InstantiateModuleResultResolver(i_isolate,
921 Utils::OpenHandle(*promise)));
922
923 Local<Value> first_arg_value = args[0];
924 i::Handle<i::Object> first_arg = Utils::OpenHandle(*first_arg_value);
925 if (!first_arg->IsJSObject()) {
926 thrower.TypeError(
927 "Argument 0 must be a buffer source or a WebAssembly.Module object");
928 resolver->OnInstantiationFailed(thrower.Reify());
929 return;
930 }
931
932 // If args.Length < 2, this will be undefined - see FunctionCallbackInfo.
933 Local<Value> ffi = args[1];
934 i::MaybeHandle<i::JSReceiver> maybe_imports =
935 GetValueAsImports(ffi, &thrower);
936
937 if (thrower.error()) {
938 resolver->OnInstantiationFailed(thrower.Reify());
939 return;
940 }
941
942 if (first_arg->IsWasmModuleObject()) {
943 i::Handle<i::WasmModuleObject> module_obj =
944 i::Handle<i::WasmModuleObject>::cast(first_arg);
945
946 i_isolate->wasm_engine()->AsyncInstantiate(i_isolate, std::move(resolver),
947 module_obj, maybe_imports);
948 return;
949 }
950
951 bool is_shared = false;
952 auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
953 if (thrower.error()) {
954 resolver->OnInstantiationFailed(thrower.Reify());
955 return;
956 }
957
958 // We start compilation now, we have no use for the
959 // {InstantiationResultResolver}.
960 resolver.reset();
961
962 std::shared_ptr<i::wasm::CompilationResultResolver> compilation_resolver(
963 new AsyncInstantiateCompileResultResolver(
964 i_isolate, Utils::OpenHandle(*promise), maybe_imports));
965
966 // The first parameter is a buffer source, we have to check if we are allowed
967 // to compile it.
968 if (!i::wasm::IsWasmCodegenAllowed(i_isolate, i_isolate->native_context())) {
969 thrower.CompileError("Wasm code generation disallowed by embedder");
970 compilation_resolver->OnCompilationFailed(thrower.Reify());
971 return;
972 }
973
974 // Asynchronous compilation handles copying wire bytes if necessary.
975 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
976 i_isolate->wasm_engine()->AsyncCompile(i_isolate, enabled_features,
977 std::move(compilation_resolver), bytes,
978 is_shared, kAPIMethodName);
979 }
980
GetIntegerProperty(v8::Isolate * isolate,ErrorThrower * thrower,Local<Context> context,v8::Local<v8::Value> value,i::Handle<i::String> property_name,int64_t * result,int64_t lower_bound,uint64_t upper_bound)981 bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
982 Local<Context> context, v8::Local<v8::Value> value,
983 i::Handle<i::String> property_name, int64_t* result,
984 int64_t lower_bound, uint64_t upper_bound) {
985 uint32_t number;
986 if (!EnforceUint32(property_name, value, context, thrower, &number)) {
987 return false;
988 }
989 if (number < lower_bound) {
990 thrower->RangeError("Property '%s': value %" PRIu32
991 " is below the lower bound %" PRIx64,
992 property_name->ToCString().get(), number, lower_bound);
993 return false;
994 }
995 if (number > upper_bound) {
996 thrower->RangeError("Property '%s': value %" PRIu32
997 " is above the upper bound %" PRIu64,
998 property_name->ToCString().get(), number, upper_bound);
999 return false;
1000 }
1001
1002 *result = static_cast<int64_t>(number);
1003 return true;
1004 }
1005
GetOptionalIntegerProperty(v8::Isolate * isolate,ErrorThrower * thrower,Local<Context> context,Local<v8::Object> object,Local<String> property,bool * has_property,int64_t * result,int64_t lower_bound,uint64_t upper_bound)1006 bool GetOptionalIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
1007 Local<Context> context,
1008 Local<v8::Object> object,
1009 Local<String> property, bool* has_property,
1010 int64_t* result, int64_t lower_bound,
1011 uint64_t upper_bound) {
1012 v8::Local<v8::Value> value;
1013 if (!object->Get(context, property).ToLocal(&value)) {
1014 return false;
1015 }
1016
1017 // Web IDL: dictionary presence
1018 // https://heycam.github.io/webidl/#dfn-present
1019 if (value->IsUndefined()) {
1020 if (has_property != nullptr) *has_property = false;
1021 return true;
1022 }
1023
1024 if (has_property != nullptr) *has_property = true;
1025 i::Handle<i::String> property_name = v8::Utils::OpenHandle(*property);
1026
1027 return GetIntegerProperty(isolate, thrower, context, value, property_name,
1028 result, lower_bound, upper_bound);
1029 }
1030
1031 // Fetch 'initial' or 'minimum' property from object. If both are provided,
1032 // 'initial' is used.
1033 // TODO(aseemgarg): change behavior when the following bug is resolved:
1034 // https://github.com/WebAssembly/js-types/issues/6
GetInitialOrMinimumProperty(v8::Isolate * isolate,ErrorThrower * thrower,Local<Context> context,Local<v8::Object> object,int64_t * result,int64_t lower_bound,uint64_t upper_bound)1035 bool GetInitialOrMinimumProperty(v8::Isolate* isolate, ErrorThrower* thrower,
1036 Local<Context> context,
1037 Local<v8::Object> object, int64_t* result,
1038 int64_t lower_bound, uint64_t upper_bound) {
1039 bool has_initial = false;
1040 if (!GetOptionalIntegerProperty(isolate, thrower, context, object,
1041 v8_str(isolate, "initial"), &has_initial,
1042 result, lower_bound, upper_bound)) {
1043 return false;
1044 }
1045 auto enabled_features = i::wasm::WasmFeatures::FromFlags();
1046 if (!has_initial && enabled_features.has_type_reflection()) {
1047 if (!GetOptionalIntegerProperty(isolate, thrower, context, object,
1048 v8_str(isolate, "minimum"), &has_initial,
1049 result, lower_bound, upper_bound)) {
1050 return false;
1051 }
1052 }
1053 if (!has_initial) {
1054 // TODO(aseemgarg): update error message when the spec issue is resolved.
1055 thrower->TypeError("Property 'initial' is required");
1056 return false;
1057 }
1058 return true;
1059 }
1060
1061 // new WebAssembly.Table(args) -> WebAssembly.Table
WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value> & args)1062 void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
1063 v8::Isolate* isolate = args.GetIsolate();
1064 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1065 HandleScope scope(isolate);
1066 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module()");
1067 if (!args.IsConstructCall()) {
1068 thrower.TypeError("WebAssembly.Table must be invoked with 'new'");
1069 return;
1070 }
1071 if (!args[0]->IsObject()) {
1072 thrower.TypeError("Argument 0 must be a table descriptor");
1073 return;
1074 }
1075 Local<Context> context = isolate->GetCurrentContext();
1076 Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1077 i::wasm::ValueType type;
1078 // The descriptor's 'element'.
1079 {
1080 v8::MaybeLocal<v8::Value> maybe =
1081 descriptor->Get(context, v8_str(isolate, "element"));
1082 v8::Local<v8::Value> value;
1083 if (!maybe.ToLocal(&value)) return;
1084 v8::Local<v8::String> string;
1085 if (!value->ToString(context).ToLocal(&string)) return;
1086 auto enabled_features = i::wasm::WasmFeatures::FromFlags();
1087 // The JS api uses 'anyfunc' instead of 'funcref'.
1088 if (string->StringEquals(v8_str(isolate, "anyfunc"))) {
1089 type = i::wasm::kWasmFuncRef;
1090 } else if (enabled_features.has_reftypes() &&
1091 string->StringEquals(v8_str(isolate, "externref"))) {
1092 type = i::wasm::kWasmExternRef;
1093 } else {
1094 thrower.TypeError(
1095 "Descriptor property 'element' must be a WebAssembly reference type");
1096 return;
1097 }
1098 }
1099
1100 int64_t initial = 0;
1101 if (!GetInitialOrMinimumProperty(isolate, &thrower, context, descriptor,
1102 &initial, 0,
1103 i::wasm::max_table_init_entries())) {
1104 return;
1105 }
1106 // The descriptor's 'maximum'.
1107 int64_t maximum = -1;
1108 bool has_maximum = true;
1109 if (!GetOptionalIntegerProperty(isolate, &thrower, context, descriptor,
1110 v8_str(isolate, "maximum"), &has_maximum,
1111 &maximum, initial,
1112 std::numeric_limits<uint32_t>::max())) {
1113 return;
1114 }
1115
1116 i::Handle<i::FixedArray> fixed_array;
1117 i::Handle<i::JSObject> table_obj =
1118 i::WasmTableObject::New(i_isolate, i::Handle<i::WasmInstanceObject>(),
1119 type, static_cast<uint32_t>(initial), has_maximum,
1120 static_cast<uint32_t>(maximum), &fixed_array);
1121 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1122 return_value.Set(Utils::ToLocal(table_obj));
1123 }
1124
WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value> & args)1125 void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
1126 v8::Isolate* isolate = args.GetIsolate();
1127 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1128 HandleScope scope(isolate);
1129 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory()");
1130 if (!args.IsConstructCall()) {
1131 thrower.TypeError("WebAssembly.Memory must be invoked with 'new'");
1132 return;
1133 }
1134 if (!args[0]->IsObject()) {
1135 thrower.TypeError("Argument 0 must be a memory descriptor");
1136 return;
1137 }
1138 Local<Context> context = isolate->GetCurrentContext();
1139 Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1140
1141 int64_t initial = 0;
1142 if (!GetInitialOrMinimumProperty(isolate, &thrower, context, descriptor,
1143 &initial, 0, i::wasm::max_mem_pages())) {
1144 return;
1145 }
1146 // The descriptor's 'maximum'.
1147 int64_t maximum = -1;
1148 if (!GetOptionalIntegerProperty(isolate, &thrower, context, descriptor,
1149 v8_str(isolate, "maximum"), nullptr, &maximum,
1150 initial, i::wasm::max_mem_pages())) {
1151 return;
1152 }
1153
1154 auto shared = i::SharedFlag::kNotShared;
1155 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1156 if (enabled_features.has_threads()) {
1157 // Shared property of descriptor
1158 Local<String> shared_key = v8_str(isolate, "shared");
1159 v8::MaybeLocal<v8::Value> maybe_value =
1160 descriptor->Get(context, shared_key);
1161 v8::Local<v8::Value> value;
1162 if (maybe_value.ToLocal(&value)) {
1163 shared = value->BooleanValue(isolate) ? i::SharedFlag::kShared
1164 : i::SharedFlag::kNotShared;
1165 } else {
1166 DCHECK(i_isolate->has_scheduled_exception());
1167 return;
1168 }
1169
1170 // Throw TypeError if shared is true, and the descriptor has no "maximum"
1171 if (shared == i::SharedFlag::kShared && maximum == -1) {
1172 thrower.TypeError(
1173 "If shared is true, maximum property should be defined.");
1174 return;
1175 }
1176 }
1177
1178 i::Handle<i::JSObject> memory_obj;
1179 if (!i::WasmMemoryObject::New(i_isolate, static_cast<uint32_t>(initial),
1180 static_cast<uint32_t>(maximum), shared)
1181 .ToHandle(&memory_obj)) {
1182 thrower.RangeError("could not allocate memory");
1183 return;
1184 }
1185 if (shared == i::SharedFlag::kShared) {
1186 i::Handle<i::JSArrayBuffer> buffer(
1187 i::Handle<i::WasmMemoryObject>::cast(memory_obj)->array_buffer(),
1188 i_isolate);
1189 Maybe<bool> result =
1190 buffer->SetIntegrityLevel(buffer, i::FROZEN, i::kDontThrow);
1191 if (!result.FromJust()) {
1192 thrower.TypeError(
1193 "Status of setting SetIntegrityLevel of buffer is false.");
1194 return;
1195 }
1196 }
1197 args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
1198 }
1199
1200 // Determines the type encoded in a value type property (e.g. type reflection).
1201 // Returns false if there was an exception, true upon success. On success the
1202 // outgoing {type} is set accordingly, or set to {wasm::kWasmStmt} in case the
1203 // type could not be properly recognized.
GetValueType(Isolate * isolate,MaybeLocal<Value> maybe,Local<Context> context,i::wasm::ValueType * type,i::wasm::WasmFeatures enabled_features)1204 bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe,
1205 Local<Context> context, i::wasm::ValueType* type,
1206 i::wasm::WasmFeatures enabled_features) {
1207 v8::Local<v8::Value> value;
1208 if (!maybe.ToLocal(&value)) return false;
1209 v8::Local<v8::String> string;
1210 if (!value->ToString(context).ToLocal(&string)) return false;
1211 if (string->StringEquals(v8_str(isolate, "i32"))) {
1212 *type = i::wasm::kWasmI32;
1213 } else if (string->StringEquals(v8_str(isolate, "f32"))) {
1214 *type = i::wasm::kWasmF32;
1215 } else if (string->StringEquals(v8_str(isolate, "i64"))) {
1216 *type = i::wasm::kWasmI64;
1217 } else if (string->StringEquals(v8_str(isolate, "f64"))) {
1218 *type = i::wasm::kWasmF64;
1219 } else if (enabled_features.has_reftypes() &&
1220 string->StringEquals(v8_str(isolate, "externref"))) {
1221 *type = i::wasm::kWasmExternRef;
1222 } else if (enabled_features.has_reftypes() &&
1223 string->StringEquals(v8_str(isolate, "anyfunc"))) {
1224 // The JS api spec uses 'anyfunc' instead of 'funcref'.
1225 *type = i::wasm::kWasmFuncRef;
1226 } else if (enabled_features.has_eh() &&
1227 string->StringEquals(v8_str(isolate, "exnref"))) {
1228 *type = i::wasm::kWasmExnRef;
1229 } else if (enabled_features.has_gc() &&
1230 string->StringEquals(v8_str(isolate, "eqref"))) {
1231 *type = i::wasm::kWasmEqRef;
1232 } else {
1233 // Unrecognized type.
1234 *type = i::wasm::kWasmStmt;
1235 }
1236 return true;
1237 }
1238
1239 // WebAssembly.Global
WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value> & args)1240 void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
1241 v8::Isolate* isolate = args.GetIsolate();
1242 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1243 HandleScope scope(isolate);
1244 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Global()");
1245 if (!args.IsConstructCall()) {
1246 thrower.TypeError("WebAssembly.Global must be invoked with 'new'");
1247 return;
1248 }
1249 if (!args[0]->IsObject()) {
1250 thrower.TypeError("Argument 0 must be a global descriptor");
1251 return;
1252 }
1253 Local<Context> context = isolate->GetCurrentContext();
1254 Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1255 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1256
1257 // The descriptor's 'mutable'.
1258 bool is_mutable = false;
1259 {
1260 Local<String> mutable_key = v8_str(isolate, "mutable");
1261 v8::MaybeLocal<v8::Value> maybe = descriptor->Get(context, mutable_key);
1262 v8::Local<v8::Value> value;
1263 if (maybe.ToLocal(&value)) {
1264 is_mutable = value->BooleanValue(isolate);
1265 } else {
1266 DCHECK(i_isolate->has_scheduled_exception());
1267 return;
1268 }
1269 }
1270
1271 // The descriptor's type, called 'value'. It is called 'value' because this
1272 // descriptor is planned to be re-used as the global's type for reflection,
1273 // so calling it 'type' is redundant.
1274 i::wasm::ValueType type;
1275 {
1276 v8::MaybeLocal<v8::Value> maybe =
1277 descriptor->Get(context, v8_str(isolate, "value"));
1278 if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
1279 if (type == i::wasm::kWasmStmt) {
1280 thrower.TypeError(
1281 "Descriptor property 'value' must be a WebAssembly type");
1282 return;
1283 }
1284 }
1285
1286 const uint32_t offset = 0;
1287 i::MaybeHandle<i::WasmGlobalObject> maybe_global_obj =
1288 i::WasmGlobalObject::New(i_isolate, i::Handle<i::WasmInstanceObject>(),
1289 i::MaybeHandle<i::JSArrayBuffer>(),
1290 i::MaybeHandle<i::FixedArray>(), type, offset,
1291 is_mutable);
1292
1293 i::Handle<i::WasmGlobalObject> global_obj;
1294 if (!maybe_global_obj.ToHandle(&global_obj)) {
1295 thrower.RangeError("could not allocate memory");
1296 return;
1297 }
1298
1299 // Convert value to a WebAssembly value, the default value is 0.
1300 Local<v8::Value> value = Local<Value>::Cast(args[1]);
1301 switch (type.kind()) {
1302 case i::wasm::ValueType::kI32: {
1303 int32_t i32_value = 0;
1304 if (!value->IsUndefined()) {
1305 v8::Local<v8::Int32> int32_value;
1306 if (!value->ToInt32(context).ToLocal(&int32_value)) return;
1307 if (!int32_value->Int32Value(context).To(&i32_value)) return;
1308 }
1309 global_obj->SetI32(i32_value);
1310 break;
1311 }
1312 case i::wasm::ValueType::kI64: {
1313 int64_t i64_value = 0;
1314 if (!value->IsUndefined()) {
1315 if (!enabled_features.has_bigint()) {
1316 thrower.TypeError("Can't set the value of i64 WebAssembly.Global");
1317 return;
1318 }
1319
1320 v8::Local<v8::BigInt> bigint_value;
1321 if (!value->ToBigInt(context).ToLocal(&bigint_value)) return;
1322 i64_value = bigint_value->Int64Value();
1323 }
1324 global_obj->SetI64(i64_value);
1325 break;
1326 }
1327 case i::wasm::ValueType::kF32: {
1328 float f32_value = 0;
1329 if (!value->IsUndefined()) {
1330 double f64_value = 0;
1331 v8::Local<v8::Number> number_value;
1332 if (!value->ToNumber(context).ToLocal(&number_value)) return;
1333 if (!number_value->NumberValue(context).To(&f64_value)) return;
1334 f32_value = i::DoubleToFloat32(f64_value);
1335 }
1336 global_obj->SetF32(f32_value);
1337 break;
1338 }
1339 case i::wasm::ValueType::kF64: {
1340 double f64_value = 0;
1341 if (!value->IsUndefined()) {
1342 v8::Local<v8::Number> number_value;
1343 if (!value->ToNumber(context).ToLocal(&number_value)) return;
1344 if (!number_value->NumberValue(context).To(&f64_value)) return;
1345 }
1346 global_obj->SetF64(f64_value);
1347 break;
1348 }
1349 case i::wasm::ValueType::kRef:
1350 case i::wasm::ValueType::kOptRef: {
1351 switch (type.heap_representation()) {
1352 case i::wasm::HeapType::kExtern:
1353 case i::wasm::HeapType::kExn: {
1354 if (args.Length() < 2) {
1355 // When no initial value is provided, we have to use the WebAssembly
1356 // default value 'null', and not the JS default value 'undefined'.
1357 global_obj->SetExternRef(i_isolate->factory()->null_value());
1358 break;
1359 }
1360 global_obj->SetExternRef(Utils::OpenHandle(*value));
1361 break;
1362 }
1363 case i::wasm::HeapType::kFunc: {
1364 if (args.Length() < 2) {
1365 // When no initial value is provided, we have to use the WebAssembly
1366 // default value 'null', and not the JS default value 'undefined'.
1367 global_obj->SetFuncRef(i_isolate,
1368 i_isolate->factory()->null_value());
1369 break;
1370 }
1371
1372 if (!global_obj->SetFuncRef(i_isolate, Utils::OpenHandle(*value))) {
1373 thrower.TypeError(
1374 "The value of funcref globals must be null or an "
1375 "exported function");
1376 }
1377 break;
1378 }
1379 case i::wasm::HeapType::kEq:
1380 default:
1381 // TODO(7748): Implement these.
1382 UNIMPLEMENTED();
1383 }
1384 break;
1385 }
1386 case i::wasm::ValueType::kRtt:
1387 // TODO(7748): Implement.
1388 UNIMPLEMENTED();
1389 case i::wasm::ValueType::kI8:
1390 case i::wasm::ValueType::kI16:
1391 case i::wasm::ValueType::kStmt:
1392 case i::wasm::ValueType::kS128:
1393 case i::wasm::ValueType::kBottom:
1394 UNREACHABLE();
1395 }
1396
1397 i::Handle<i::JSObject> global_js_object(global_obj);
1398 args.GetReturnValue().Set(Utils::ToLocal(global_js_object));
1399 }
1400
1401 // WebAssembly.Exception
WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value> & args)1402 void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
1403 v8::Isolate* isolate = args.GetIsolate();
1404 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1405 HandleScope scope(isolate);
1406 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception()");
1407 thrower.TypeError("WebAssembly.Exception cannot be called");
1408 }
1409
1410 namespace {
1411
GetIterableLength(i::Isolate * isolate,Local<Context> context,Local<Object> iterable)1412 uint32_t GetIterableLength(i::Isolate* isolate, Local<Context> context,
1413 Local<Object> iterable) {
1414 Local<String> length = Utils::ToLocal(isolate->factory()->length_string());
1415 MaybeLocal<Value> property = iterable->Get(context, length);
1416 if (property.IsEmpty()) return i::kMaxUInt32;
1417 MaybeLocal<Uint32> number = property.ToLocalChecked()->ToArrayIndex(context);
1418 if (number.IsEmpty()) return i::kMaxUInt32;
1419 DCHECK_NE(i::kMaxUInt32, number.ToLocalChecked()->Value());
1420 return number.ToLocalChecked()->Value();
1421 }
1422
1423 } // namespace
1424
1425 // WebAssembly.Function
WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value> & args)1426 void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
1427 v8::Isolate* isolate = args.GetIsolate();
1428 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1429 HandleScope scope(isolate);
1430 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Function()");
1431 if (!args.IsConstructCall()) {
1432 thrower.TypeError("WebAssembly.Function must be invoked with 'new'");
1433 return;
1434 }
1435 if (!args[0]->IsObject()) {
1436 thrower.TypeError("Argument 0 must be a function type");
1437 return;
1438 }
1439 Local<Object> function_type = Local<Object>::Cast(args[0]);
1440 Local<Context> context = isolate->GetCurrentContext();
1441 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1442
1443 // Load the 'parameters' property of the function type.
1444 Local<String> parameters_key = v8_str(isolate, "parameters");
1445 v8::MaybeLocal<v8::Value> parameters_maybe =
1446 function_type->Get(context, parameters_key);
1447 v8::Local<v8::Value> parameters_value;
1448 if (!parameters_maybe.ToLocal(¶meters_value)) return;
1449 if (!parameters_value->IsObject()) {
1450 thrower.TypeError("Argument 0 must be a function type with 'parameters'");
1451 return;
1452 }
1453 Local<Object> parameters = parameters_value.As<Object>();
1454 uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters);
1455 if (parameters_len == i::kMaxUInt32) {
1456 thrower.TypeError("Argument 0 contains parameters without 'length'");
1457 return;
1458 }
1459 if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) {
1460 thrower.TypeError("Argument 0 contains too many parameters");
1461 return;
1462 }
1463
1464 // Load the 'results' property of the function type.
1465 Local<String> results_key = v8_str(isolate, "results");
1466 v8::MaybeLocal<v8::Value> results_maybe =
1467 function_type->Get(context, results_key);
1468 v8::Local<v8::Value> results_value;
1469 if (!results_maybe.ToLocal(&results_value)) return;
1470 if (!results_value->IsObject()) {
1471 thrower.TypeError("Argument 0 must be a function type with 'results'");
1472 return;
1473 }
1474 Local<Object> results = results_value.As<Object>();
1475 uint32_t results_len = GetIterableLength(i_isolate, context, results);
1476 if (results_len == i::kMaxUInt32) {
1477 thrower.TypeError("Argument 0 contains results without 'length'");
1478 return;
1479 }
1480 if (results_len > (enabled_features.has_mv()
1481 ? i::wasm::kV8MaxWasmFunctionMultiReturns
1482 : i::wasm::kV8MaxWasmFunctionReturns)) {
1483 thrower.TypeError("Argument 0 contains too many results");
1484 return;
1485 }
1486
1487 // Decode the function type and construct a signature.
1488 i::Zone zone(i_isolate->allocator(), ZONE_NAME);
1489 i::wasm::FunctionSig::Builder builder(&zone, results_len, parameters_len);
1490 for (uint32_t i = 0; i < parameters_len; ++i) {
1491 i::wasm::ValueType type;
1492 MaybeLocal<Value> maybe = parameters->Get(context, i);
1493 if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
1494 if (type == i::wasm::kWasmStmt) {
1495 thrower.TypeError(
1496 "Argument 0 parameter type at index #%u must be a value type", i);
1497 return;
1498 }
1499 builder.AddParam(type);
1500 }
1501 for (uint32_t i = 0; i < results_len; ++i) {
1502 i::wasm::ValueType type;
1503 MaybeLocal<Value> maybe = results->Get(context, i);
1504 if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
1505 if (type == i::wasm::kWasmStmt) {
1506 thrower.TypeError(
1507 "Argument 0 result type at index #%u must be a value type", i);
1508 return;
1509 }
1510 builder.AddReturn(type);
1511 }
1512
1513 if (!args[1]->IsFunction()) {
1514 thrower.TypeError("Argument 1 must be a function");
1515 return;
1516 }
1517 const i::wasm::FunctionSig* sig = builder.Build();
1518
1519 i::Handle<i::JSReceiver> callable =
1520 Utils::OpenHandle(*args[1].As<Function>());
1521 if (i::WasmExportedFunction::IsWasmExportedFunction(*callable)) {
1522 if (*i::Handle<i::WasmExportedFunction>::cast(callable)->sig() == *sig) {
1523 args.GetReturnValue().Set(Utils::ToLocal(callable));
1524 return;
1525 }
1526
1527 thrower.TypeError(
1528 "The signature of Argument 1 (a WebAssembly function) does "
1529 "not match the signature specified in Argument 0");
1530 return;
1531 }
1532
1533 if (i::WasmJSFunction::IsWasmJSFunction(*callable)) {
1534 if (i::Handle<i::WasmJSFunction>::cast(callable)->MatchesSignature(sig)) {
1535 args.GetReturnValue().Set(Utils::ToLocal(callable));
1536 return;
1537 }
1538
1539 thrower.TypeError(
1540 "The signature of Argument 1 (a WebAssembly function) does "
1541 "not match the signature specified in Argument 0");
1542 return;
1543 }
1544
1545 i::Handle<i::JSFunction> result =
1546 i::WasmJSFunction::New(i_isolate, sig, callable);
1547 args.GetReturnValue().Set(Utils::ToLocal(result));
1548 }
1549
1550 // WebAssembly.Function.type(WebAssembly.Function) -> FunctionType
WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value> & args)1551 void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) {
1552 v8::Isolate* isolate = args.GetIsolate();
1553 HandleScope scope(isolate);
1554 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1555 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Function.type()");
1556
1557 const i::wasm::FunctionSig* sig;
1558 i::Zone zone(i_isolate->allocator(), ZONE_NAME);
1559 i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
1560 if (i::WasmExportedFunction::IsWasmExportedFunction(*arg0)) {
1561 sig = i::Handle<i::WasmExportedFunction>::cast(arg0)->sig();
1562 } else if (i::WasmJSFunction::IsWasmJSFunction(*arg0)) {
1563 sig = i::Handle<i::WasmJSFunction>::cast(arg0)->GetSignature(&zone);
1564 } else {
1565 thrower.TypeError("Argument 0 must be a WebAssembly.Function");
1566 return;
1567 }
1568
1569 auto type = i::wasm::GetTypeForFunction(i_isolate, sig);
1570 args.GetReturnValue().Set(Utils::ToLocal(type));
1571 }
1572
1573 constexpr const char* kName_WasmGlobalObject = "WebAssembly.Global";
1574 constexpr const char* kName_WasmMemoryObject = "WebAssembly.Memory";
1575 constexpr const char* kName_WasmInstanceObject = "WebAssembly.Instance";
1576 constexpr const char* kName_WasmTableObject = "WebAssembly.Table";
1577
1578 #define EXTRACT_THIS(var, WasmType) \
1579 i::Handle<i::WasmType> var; \
1580 { \
1581 i::Handle<i::Object> this_arg = Utils::OpenHandle(*args.This()); \
1582 if (!this_arg->Is##WasmType()) { \
1583 thrower.TypeError("Receiver is not a %s", kName_##WasmType); \
1584 return; \
1585 } \
1586 var = i::Handle<i::WasmType>::cast(this_arg); \
1587 }
1588
WebAssemblyInstanceGetExports(const v8::FunctionCallbackInfo<v8::Value> & args)1589 void WebAssemblyInstanceGetExports(
1590 const v8::FunctionCallbackInfo<v8::Value>& args) {
1591 v8::Isolate* isolate = args.GetIsolate();
1592 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1593 HandleScope scope(isolate);
1594 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance.exports()");
1595 EXTRACT_THIS(receiver, WasmInstanceObject);
1596 i::Handle<i::JSObject> exports_object(receiver->exports_object(), i_isolate);
1597 args.GetReturnValue().Set(Utils::ToLocal(exports_object));
1598 }
1599
WebAssemblyTableGetLength(const v8::FunctionCallbackInfo<v8::Value> & args)1600 void WebAssemblyTableGetLength(
1601 const v8::FunctionCallbackInfo<v8::Value>& args) {
1602 v8::Isolate* isolate = args.GetIsolate();
1603 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1604 HandleScope scope(isolate);
1605 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.length()");
1606 EXTRACT_THIS(receiver, WasmTableObject);
1607 args.GetReturnValue().Set(
1608 v8::Number::New(isolate, receiver->current_length()));
1609 }
1610
1611 // WebAssembly.Table.grow(num, init_value = null) -> num
WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value> & args)1612 void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1613 v8::Isolate* isolate = args.GetIsolate();
1614 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1615 HandleScope scope(isolate);
1616 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.grow()");
1617 Local<Context> context = isolate->GetCurrentContext();
1618 EXTRACT_THIS(receiver, WasmTableObject);
1619
1620 uint32_t grow_by;
1621 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &grow_by)) {
1622 return;
1623 }
1624
1625 i::Handle<i::Object> init_value = i_isolate->factory()->null_value();
1626 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1627 if (enabled_features.has_typed_funcref()) {
1628 if (args.Length() >= 2 && !args[1]->IsUndefined()) {
1629 init_value = Utils::OpenHandle(*args[1]);
1630 }
1631 if (!i::WasmTableObject::IsValidElement(i_isolate, receiver, init_value)) {
1632 thrower.TypeError("Argument 1 must be a valid type for the table");
1633 return;
1634 }
1635 }
1636
1637 int old_size =
1638 i::WasmTableObject::Grow(i_isolate, receiver, grow_by, init_value);
1639
1640 if (old_size < 0) {
1641 thrower.RangeError("failed to grow table by %u", grow_by);
1642 return;
1643 }
1644 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1645 return_value.Set(old_size);
1646 }
1647
1648 // WebAssembly.Table.get(num) -> JSFunction
WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value> & args)1649 void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
1650 v8::Isolate* isolate = args.GetIsolate();
1651 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1652 HandleScope scope(isolate);
1653 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.get()");
1654 Local<Context> context = isolate->GetCurrentContext();
1655 EXTRACT_THIS(receiver, WasmTableObject);
1656
1657 uint32_t index;
1658 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) {
1659 return;
1660 }
1661 if (!i::WasmTableObject::IsInBounds(i_isolate, receiver, index)) {
1662 thrower.RangeError("invalid index %u into function table", index);
1663 return;
1664 }
1665
1666 i::Handle<i::Object> result =
1667 i::WasmTableObject::Get(i_isolate, receiver, index);
1668
1669 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1670 return_value.Set(Utils::ToLocal(result));
1671 }
1672
1673 // WebAssembly.Table.set(num, JSFunction)
WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value> & args)1674 void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
1675 v8::Isolate* isolate = args.GetIsolate();
1676 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1677 HandleScope scope(isolate);
1678 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.set()");
1679 Local<Context> context = isolate->GetCurrentContext();
1680 EXTRACT_THIS(table_object, WasmTableObject);
1681
1682 // Parameter 0.
1683 uint32_t index;
1684 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) {
1685 return;
1686 }
1687 if (!i::WasmTableObject::IsInBounds(i_isolate, table_object, index)) {
1688 thrower.RangeError("invalid index %u into function table", index);
1689 return;
1690 }
1691
1692 i::Handle<i::Object> element = Utils::OpenHandle(*args[1]);
1693 if (!i::WasmTableObject::IsValidElement(i_isolate, table_object, element)) {
1694 thrower.TypeError(
1695 "Argument 1 must be null or a WebAssembly function of type compatible "
1696 "to 'this'");
1697 return;
1698 }
1699 i::WasmTableObject::Set(i_isolate, table_object, index, element);
1700 }
1701
1702 // WebAssembly.Table.type(WebAssembly.Table) -> TableType
WebAssemblyTableType(const v8::FunctionCallbackInfo<v8::Value> & args)1703 void WebAssemblyTableType(const v8::FunctionCallbackInfo<v8::Value>& args) {
1704 v8::Isolate* isolate = args.GetIsolate();
1705 HandleScope scope(isolate);
1706 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1707 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.type()");
1708
1709 auto maybe_table = GetFirstArgumentAsTable(args, &thrower);
1710 if (thrower.error()) return;
1711 i::Handle<i::WasmTableObject> table = maybe_table.ToHandleChecked();
1712 base::Optional<uint32_t> max_size;
1713 if (!table->maximum_length().IsUndefined()) {
1714 uint64_t max_size64 = table->maximum_length().Number();
1715 DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max());
1716 max_size.emplace(static_cast<uint32_t>(max_size64));
1717 }
1718 auto type = i::wasm::GetTypeForTable(i_isolate, table->type(),
1719 table->current_length(), max_size);
1720 args.GetReturnValue().Set(Utils::ToLocal(type));
1721 }
1722
1723 // WebAssembly.Memory.grow(num) -> num
WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value> & args)1724 void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
1725 v8::Isolate* isolate = args.GetIsolate();
1726 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1727 HandleScope scope(isolate);
1728 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.grow()");
1729 Local<Context> context = isolate->GetCurrentContext();
1730 EXTRACT_THIS(receiver, WasmMemoryObject);
1731
1732 uint32_t delta_size;
1733 if (!EnforceUint32("Argument 0", args[0], context, &thrower, &delta_size)) {
1734 return;
1735 }
1736
1737 uint64_t max_size64 = receiver->maximum_pages();
1738 if (max_size64 > uint64_t{i::wasm::max_mem_pages()}) {
1739 max_size64 = i::wasm::max_mem_pages();
1740 }
1741 i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer(), i_isolate);
1742
1743 DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max());
1744
1745 uint64_t old_size64 = old_buffer->byte_length() / i::wasm::kWasmPageSize;
1746 uint64_t new_size64 = old_size64 + static_cast<uint64_t>(delta_size);
1747
1748 if (new_size64 > max_size64) {
1749 thrower.RangeError("Maximum memory size exceeded");
1750 return;
1751 }
1752
1753 int32_t ret = i::WasmMemoryObject::Grow(i_isolate, receiver, delta_size);
1754 if (ret == -1) {
1755 thrower.RangeError("Unable to grow instance memory.");
1756 return;
1757 }
1758 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1759 return_value.Set(ret);
1760 }
1761
1762 // WebAssembly.Memory.buffer -> ArrayBuffer
WebAssemblyMemoryGetBuffer(const v8::FunctionCallbackInfo<v8::Value> & args)1763 void WebAssemblyMemoryGetBuffer(
1764 const v8::FunctionCallbackInfo<v8::Value>& args) {
1765 v8::Isolate* isolate = args.GetIsolate();
1766 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1767 HandleScope scope(isolate);
1768 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer");
1769 EXTRACT_THIS(receiver, WasmMemoryObject);
1770
1771 i::Handle<i::Object> buffer_obj(receiver->array_buffer(), i_isolate);
1772 DCHECK(buffer_obj->IsJSArrayBuffer());
1773 i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(*buffer_obj),
1774 i_isolate);
1775 if (buffer->is_shared()) {
1776 // TODO(gdeepti): More needed here for when cached buffer, and current
1777 // buffer are out of sync, handle that here when bounds checks, and Grow
1778 // are handled correctly.
1779 Maybe<bool> result =
1780 buffer->SetIntegrityLevel(buffer, i::FROZEN, i::kDontThrow);
1781 if (!result.FromJust()) {
1782 thrower.TypeError(
1783 "Status of setting SetIntegrityLevel of buffer is false.");
1784 }
1785 }
1786 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1787 return_value.Set(Utils::ToLocal(buffer));
1788 }
1789
1790 // WebAssembly.Memory.type(WebAssembly.Memory) -> MemoryType
WebAssemblyMemoryType(const v8::FunctionCallbackInfo<v8::Value> & args)1791 void WebAssemblyMemoryType(const v8::FunctionCallbackInfo<v8::Value>& args) {
1792 v8::Isolate* isolate = args.GetIsolate();
1793 HandleScope scope(isolate);
1794 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1795 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.type()");
1796
1797 auto maybe_memory = GetFirstArgumentAsMemory(args, &thrower);
1798 if (thrower.error()) return;
1799 i::Handle<i::WasmMemoryObject> memory = maybe_memory.ToHandleChecked();
1800 i::Handle<i::JSArrayBuffer> buffer(memory->array_buffer(), i_isolate);
1801 size_t curr_size = buffer->byte_length() / i::wasm::kWasmPageSize;
1802 DCHECK_LE(curr_size, std::numeric_limits<uint32_t>::max());
1803 uint32_t min_size = static_cast<uint32_t>(curr_size);
1804 base::Optional<uint32_t> max_size;
1805 if (memory->has_maximum_pages()) {
1806 uint64_t max_size64 = memory->maximum_pages();
1807 DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max());
1808 max_size.emplace(static_cast<uint32_t>(max_size64));
1809 }
1810 auto type = i::wasm::GetTypeForMemory(i_isolate, min_size, max_size);
1811 args.GetReturnValue().Set(Utils::ToLocal(type));
1812 }
1813
WebAssemblyGlobalGetValueCommon(const v8::FunctionCallbackInfo<v8::Value> & args,const char * name)1814 void WebAssemblyGlobalGetValueCommon(
1815 const v8::FunctionCallbackInfo<v8::Value>& args, const char* name) {
1816 v8::Isolate* isolate = args.GetIsolate();
1817 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1818 HandleScope scope(isolate);
1819 ScheduledErrorThrower thrower(i_isolate, name);
1820 EXTRACT_THIS(receiver, WasmGlobalObject);
1821
1822 v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1823
1824 switch (receiver->type().kind()) {
1825 case i::wasm::ValueType::kI32:
1826 return_value.Set(receiver->GetI32());
1827 break;
1828 case i::wasm::ValueType::kI64: {
1829 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1830 if (enabled_features.has_bigint()) {
1831 Local<BigInt> value = BigInt::New(isolate, receiver->GetI64());
1832 return_value.Set(value);
1833 } else {
1834 thrower.TypeError("Can't get the value of i64 WebAssembly.Global");
1835 }
1836 break;
1837 }
1838 case i::wasm::ValueType::kF32:
1839 return_value.Set(receiver->GetF32());
1840 break;
1841 case i::wasm::ValueType::kF64:
1842 return_value.Set(receiver->GetF64());
1843 break;
1844 case i::wasm::ValueType::kS128:
1845 thrower.TypeError("Can't get the value of s128 WebAssembly.Global");
1846 break;
1847 case i::wasm::ValueType::kRef:
1848 case i::wasm::ValueType::kOptRef:
1849 switch (receiver->type().heap_representation()) {
1850 case i::wasm::HeapType::kExtern:
1851 case i::wasm::HeapType::kFunc:
1852 case i::wasm::HeapType::kExn:
1853 return_value.Set(Utils::ToLocal(receiver->GetRef()));
1854 break;
1855 case i::wasm::HeapType::kEq:
1856 default:
1857 // TODO(7748): Implement these.
1858 UNIMPLEMENTED();
1859 break;
1860 }
1861 break;
1862 case i::wasm::ValueType::kRtt:
1863 UNIMPLEMENTED(); // TODO(7748): Implement.
1864 break;
1865 case i::wasm::ValueType::kI8:
1866 case i::wasm::ValueType::kI16:
1867 case i::wasm::ValueType::kBottom:
1868 case i::wasm::ValueType::kStmt:
1869 UNREACHABLE();
1870 }
1871 }
1872
1873 // WebAssembly.Global.valueOf() -> num
WebAssemblyGlobalValueOf(const v8::FunctionCallbackInfo<v8::Value> & args)1874 void WebAssemblyGlobalValueOf(const v8::FunctionCallbackInfo<v8::Value>& args) {
1875 return WebAssemblyGlobalGetValueCommon(args, "WebAssembly.Global.valueOf()");
1876 }
1877
1878 // get WebAssembly.Global.value -> num
WebAssemblyGlobalGetValue(const v8::FunctionCallbackInfo<v8::Value> & args)1879 void WebAssemblyGlobalGetValue(
1880 const v8::FunctionCallbackInfo<v8::Value>& args) {
1881 return WebAssemblyGlobalGetValueCommon(args, "get WebAssembly.Global.value");
1882 }
1883
1884 // set WebAssembly.Global.value(num)
WebAssemblyGlobalSetValue(const v8::FunctionCallbackInfo<v8::Value> & args)1885 void WebAssemblyGlobalSetValue(
1886 const v8::FunctionCallbackInfo<v8::Value>& args) {
1887 v8::Isolate* isolate = args.GetIsolate();
1888 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1889 HandleScope scope(isolate);
1890 Local<Context> context = isolate->GetCurrentContext();
1891 ScheduledErrorThrower thrower(i_isolate, "set WebAssembly.Global.value");
1892 EXTRACT_THIS(receiver, WasmGlobalObject);
1893
1894 if (!receiver->is_mutable()) {
1895 thrower.TypeError("Can't set the value of an immutable global.");
1896 return;
1897 }
1898 if (args[0]->IsUndefined()) {
1899 thrower.TypeError("Argument 0 is required");
1900 return;
1901 }
1902
1903 switch (receiver->type().kind()) {
1904 case i::wasm::ValueType::kI32: {
1905 int32_t i32_value = 0;
1906 if (!args[0]->Int32Value(context).To(&i32_value)) return;
1907 receiver->SetI32(i32_value);
1908 break;
1909 }
1910 case i::wasm::ValueType::kI64: {
1911 auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1912 if (enabled_features.has_bigint()) {
1913 v8::Local<v8::BigInt> bigint_value;
1914 if (!args[0]->ToBigInt(context).ToLocal(&bigint_value)) return;
1915 receiver->SetI64(bigint_value->Int64Value());
1916 } else {
1917 thrower.TypeError("Can't set the value of i64 WebAssembly.Global");
1918 }
1919 break;
1920 }
1921 case i::wasm::ValueType::kF32: {
1922 double f64_value = 0;
1923 if (!args[0]->NumberValue(context).To(&f64_value)) return;
1924 receiver->SetF32(i::DoubleToFloat32(f64_value));
1925 break;
1926 }
1927 case i::wasm::ValueType::kF64: {
1928 double f64_value = 0;
1929 if (!args[0]->NumberValue(context).To(&f64_value)) return;
1930 receiver->SetF64(f64_value);
1931 break;
1932 }
1933 case i::wasm::ValueType::kS128:
1934 thrower.TypeError("Can't set the value of s128 WebAssembly.Global");
1935 break;
1936 case i::wasm::ValueType::kRef:
1937 case i::wasm::ValueType::kOptRef:
1938 switch (receiver->type().heap_representation()) {
1939 case i::wasm::HeapType::kExtern:
1940 case i::wasm::HeapType::kExn:
1941 receiver->SetExternRef(Utils::OpenHandle(*args[0]));
1942 break;
1943 case i::wasm::HeapType::kFunc: {
1944 if (!receiver->SetFuncRef(i_isolate, Utils::OpenHandle(*args[0]))) {
1945 thrower.TypeError(
1946 "value of an funcref reference must be either null or an "
1947 "exported function");
1948 }
1949 break;
1950 }
1951
1952 case i::wasm::HeapType::kEq:
1953 default:
1954 // TODO(7748): Implement these.
1955 UNIMPLEMENTED();
1956 break;
1957 }
1958 break;
1959 case i::wasm::ValueType::kRtt:
1960 // TODO(7748): Implement.
1961 UNIMPLEMENTED();
1962 break;
1963 case i::wasm::ValueType::kI8:
1964 case i::wasm::ValueType::kI16:
1965 case i::wasm::ValueType::kBottom:
1966 case i::wasm::ValueType::kStmt:
1967 UNREACHABLE();
1968 }
1969 }
1970
1971 // WebAssembly.Global.type(WebAssembly.Global) -> GlobalType
WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value> & args)1972 void WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value>& args) {
1973 v8::Isolate* isolate = args.GetIsolate();
1974 HandleScope scope(isolate);
1975 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1976 ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Global.type()");
1977
1978 auto maybe_global = GetFirstArgumentAsGlobal(args, &thrower);
1979 if (thrower.error()) return;
1980 i::Handle<i::WasmGlobalObject> global = maybe_global.ToHandleChecked();
1981 auto type = i::wasm::GetTypeForGlobal(i_isolate, global->is_mutable(),
1982 global->type());
1983 args.GetReturnValue().Set(Utils::ToLocal(type));
1984 }
1985
1986 } // namespace
1987
1988 // TODO(titzer): we use the API to create the function template because the
1989 // internal guts are too ugly to replicate here.
NewFunctionTemplate(i::Isolate * i_isolate,FunctionCallback func,bool has_prototype)1990 static i::Handle<i::FunctionTemplateInfo> NewFunctionTemplate(
1991 i::Isolate* i_isolate, FunctionCallback func, bool has_prototype) {
1992 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
1993 Local<FunctionTemplate> templ = FunctionTemplate::New(isolate, func);
1994 has_prototype ? templ->ReadOnlyPrototype() : templ->RemovePrototype();
1995 return v8::Utils::OpenHandle(*templ);
1996 }
1997
NewObjectTemplate(i::Isolate * i_isolate)1998 static i::Handle<i::ObjectTemplateInfo> NewObjectTemplate(
1999 i::Isolate* i_isolate) {
2000 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
2001 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
2002 return v8::Utils::OpenHandle(*templ);
2003 }
2004
2005 namespace internal {
2006
CreateFunc(Isolate * isolate,Handle<String> name,FunctionCallback func,bool has_prototype)2007 Handle<JSFunction> CreateFunc(Isolate* isolate, Handle<String> name,
2008 FunctionCallback func, bool has_prototype) {
2009 Handle<FunctionTemplateInfo> temp =
2010 NewFunctionTemplate(isolate, func, has_prototype);
2011 Handle<JSFunction> function =
2012 ApiNatives::InstantiateFunction(temp, name).ToHandleChecked();
2013 DCHECK(function->shared().HasSharedName());
2014 return function;
2015 }
2016
InstallFunc(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func,int length,bool has_prototype=false,PropertyAttributes attributes=NONE)2017 Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object,
2018 const char* str, FunctionCallback func,
2019 int length, bool has_prototype = false,
2020 PropertyAttributes attributes = NONE) {
2021 Handle<String> name = v8_str(isolate, str);
2022 Handle<JSFunction> function = CreateFunc(isolate, name, func, has_prototype);
2023 function->shared().set_length(length);
2024 JSObject::AddProperty(isolate, object, name, function, attributes);
2025 return function;
2026 }
2027
InstallConstructorFunc(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func)2028 Handle<JSFunction> InstallConstructorFunc(Isolate* isolate,
2029 Handle<JSObject> object,
2030 const char* str,
2031 FunctionCallback func) {
2032 return InstallFunc(isolate, object, str, func, 1, true, DONT_ENUM);
2033 }
2034
GetterName(Isolate * isolate,Handle<String> name)2035 Handle<String> GetterName(Isolate* isolate, Handle<String> name) {
2036 return Name::ToFunctionName(isolate, name, isolate->factory()->get_string())
2037 .ToHandleChecked();
2038 }
2039
InstallGetter(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func)2040 void InstallGetter(Isolate* isolate, Handle<JSObject> object, const char* str,
2041 FunctionCallback func) {
2042 Handle<String> name = v8_str(isolate, str);
2043 Handle<JSFunction> function =
2044 CreateFunc(isolate, GetterName(isolate, name), func, false);
2045
2046 Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name),
2047 Utils::ToLocal(function),
2048 Local<Function>(), v8::None);
2049 }
2050
SetterName(Isolate * isolate,Handle<String> name)2051 Handle<String> SetterName(Isolate* isolate, Handle<String> name) {
2052 return Name::ToFunctionName(isolate, name, isolate->factory()->set_string())
2053 .ToHandleChecked();
2054 }
2055
InstallGetterSetter(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback getter,FunctionCallback setter)2056 void InstallGetterSetter(Isolate* isolate, Handle<JSObject> object,
2057 const char* str, FunctionCallback getter,
2058 FunctionCallback setter) {
2059 Handle<String> name = v8_str(isolate, str);
2060 Handle<JSFunction> getter_func =
2061 CreateFunc(isolate, GetterName(isolate, name), getter, false);
2062 Handle<JSFunction> setter_func =
2063 CreateFunc(isolate, SetterName(isolate, name), setter, false);
2064 setter_func->shared().set_length(1);
2065
2066 Utils::ToLocal(object)->SetAccessorProperty(
2067 Utils::ToLocal(name), Utils::ToLocal(getter_func),
2068 Utils::ToLocal(setter_func), v8::None);
2069 }
2070
2071 // Assigns a dummy instance template to the given constructor function. Used to
2072 // make sure the implicit receivers for the constructors in this file have an
2073 // instance type different from the internal one, they allocate the resulting
2074 // object explicitly and ignore implicit receiver.
SetDummyInstanceTemplate(Isolate * isolate,Handle<JSFunction> fun)2075 void SetDummyInstanceTemplate(Isolate* isolate, Handle<JSFunction> fun) {
2076 Handle<ObjectTemplateInfo> instance_template = NewObjectTemplate(isolate);
2077 FunctionTemplateInfo::SetInstanceTemplate(
2078 isolate, handle(fun->shared().get_api_func_data(), isolate),
2079 instance_template);
2080 }
2081
2082 // static
Install(Isolate * isolate,bool exposed_on_global_object)2083 void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
2084 Handle<JSGlobalObject> global = isolate->global_object();
2085 Handle<Context> context(global->native_context(), isolate);
2086 // Install the JS API once only.
2087 Object prev = context->get(Context::WASM_MODULE_CONSTRUCTOR_INDEX);
2088 if (!prev.IsUndefined(isolate)) {
2089 DCHECK(prev.IsJSFunction());
2090 return;
2091 }
2092
2093 Factory* factory = isolate->factory();
2094
2095 // Setup WebAssembly
2096 Handle<String> name = v8_str(isolate, "WebAssembly");
2097 NewFunctionArgs args = NewFunctionArgs::ForFunctionWithoutCode(
2098 name, isolate->strict_function_map(), LanguageMode::kStrict);
2099 Handle<JSFunction> cons = factory->NewFunction(args);
2100 JSFunction::SetPrototype(cons, isolate->initial_object_prototype());
2101 Handle<JSObject> webassembly =
2102 factory->NewJSObject(cons, AllocationType::kOld);
2103
2104 PropertyAttributes ro_attributes =
2105 static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
2106 JSObject::AddProperty(isolate, webassembly, factory->to_string_tag_symbol(),
2107 name, ro_attributes);
2108 InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1);
2109 InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1);
2110 InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1);
2111
2112 if (FLAG_wasm_test_streaming) {
2113 isolate->set_wasm_streaming_callback(WasmStreamingCallbackForTesting);
2114 }
2115
2116 if (isolate->wasm_streaming_callback() != nullptr) {
2117 InstallFunc(isolate, webassembly, "compileStreaming",
2118 WebAssemblyCompileStreaming, 1);
2119 InstallFunc(isolate, webassembly, "instantiateStreaming",
2120 WebAssemblyInstantiateStreaming, 1);
2121 }
2122
2123 // Expose the API on the global object if configured to do so.
2124 if (exposed_on_global_object) {
2125 JSObject::AddProperty(isolate, global, name, webassembly, DONT_ENUM);
2126 }
2127
2128 // Setup Module
2129 Handle<JSFunction> module_constructor =
2130 InstallConstructorFunc(isolate, webassembly, "Module", WebAssemblyModule);
2131 context->set_wasm_module_constructor(*module_constructor);
2132 SetDummyInstanceTemplate(isolate, module_constructor);
2133 JSFunction::EnsureHasInitialMap(module_constructor);
2134 Handle<JSObject> module_proto(
2135 JSObject::cast(module_constructor->instance_prototype()), isolate);
2136 Handle<Map> module_map = isolate->factory()->NewMap(
2137 i::WASM_MODULE_OBJECT_TYPE, WasmModuleObject::kHeaderSize);
2138 JSFunction::SetInitialMap(module_constructor, module_map, module_proto);
2139 InstallFunc(isolate, module_constructor, "imports", WebAssemblyModuleImports,
2140 1);
2141 InstallFunc(isolate, module_constructor, "exports", WebAssemblyModuleExports,
2142 1);
2143 InstallFunc(isolate, module_constructor, "customSections",
2144 WebAssemblyModuleCustomSections, 2);
2145 JSObject::AddProperty(isolate, module_proto, factory->to_string_tag_symbol(),
2146 v8_str(isolate, "WebAssembly.Module"), ro_attributes);
2147
2148 // Setup Instance
2149 Handle<JSFunction> instance_constructor = InstallConstructorFunc(
2150 isolate, webassembly, "Instance", WebAssemblyInstance);
2151 context->set_wasm_instance_constructor(*instance_constructor);
2152 SetDummyInstanceTemplate(isolate, instance_constructor);
2153 JSFunction::EnsureHasInitialMap(instance_constructor);
2154 Handle<JSObject> instance_proto(
2155 JSObject::cast(instance_constructor->instance_prototype()), isolate);
2156 Handle<Map> instance_map = isolate->factory()->NewMap(
2157 i::WASM_INSTANCE_OBJECT_TYPE, WasmInstanceObject::kHeaderSize);
2158 JSFunction::SetInitialMap(instance_constructor, instance_map, instance_proto);
2159 InstallGetter(isolate, instance_proto, "exports",
2160 WebAssemblyInstanceGetExports);
2161 JSObject::AddProperty(isolate, instance_proto,
2162 factory->to_string_tag_symbol(),
2163 v8_str(isolate, "WebAssembly.Instance"), ro_attributes);
2164
2165 // The context is not set up completely yet. That's why we cannot use
2166 // {WasmFeatures::FromIsolate} and have to use {WasmFeatures::FromFlags}
2167 // instead.
2168 auto enabled_features = i::wasm::WasmFeatures::FromFlags();
2169
2170 // Setup Table
2171 Handle<JSFunction> table_constructor =
2172 InstallConstructorFunc(isolate, webassembly, "Table", WebAssemblyTable);
2173 context->set_wasm_table_constructor(*table_constructor);
2174 SetDummyInstanceTemplate(isolate, table_constructor);
2175 JSFunction::EnsureHasInitialMap(table_constructor);
2176 Handle<JSObject> table_proto(
2177 JSObject::cast(table_constructor->instance_prototype()), isolate);
2178 Handle<Map> table_map = isolate->factory()->NewMap(
2179 i::WASM_TABLE_OBJECT_TYPE, WasmTableObject::kHeaderSize);
2180 JSFunction::SetInitialMap(table_constructor, table_map, table_proto);
2181 InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength);
2182 InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow, 1);
2183 InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet, 1);
2184 InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet, 2);
2185 if (enabled_features.has_type_reflection()) {
2186 InstallFunc(isolate, table_constructor, "type", WebAssemblyTableType, 1);
2187 }
2188 JSObject::AddProperty(isolate, table_proto, factory->to_string_tag_symbol(),
2189 v8_str(isolate, "WebAssembly.Table"), ro_attributes);
2190
2191 // Setup Memory
2192 Handle<JSFunction> memory_constructor =
2193 InstallConstructorFunc(isolate, webassembly, "Memory", WebAssemblyMemory);
2194 context->set_wasm_memory_constructor(*memory_constructor);
2195 SetDummyInstanceTemplate(isolate, memory_constructor);
2196 JSFunction::EnsureHasInitialMap(memory_constructor);
2197 Handle<JSObject> memory_proto(
2198 JSObject::cast(memory_constructor->instance_prototype()), isolate);
2199 Handle<Map> memory_map = isolate->factory()->NewMap(
2200 i::WASM_MEMORY_OBJECT_TYPE, WasmMemoryObject::kHeaderSize);
2201 JSFunction::SetInitialMap(memory_constructor, memory_map, memory_proto);
2202 InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow, 1);
2203 InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer);
2204 if (enabled_features.has_type_reflection()) {
2205 InstallFunc(isolate, memory_constructor, "type", WebAssemblyMemoryType, 1);
2206 }
2207 JSObject::AddProperty(isolate, memory_proto, factory->to_string_tag_symbol(),
2208 v8_str(isolate, "WebAssembly.Memory"), ro_attributes);
2209
2210 // Setup Global
2211 Handle<JSFunction> global_constructor =
2212 InstallConstructorFunc(isolate, webassembly, "Global", WebAssemblyGlobal);
2213 context->set_wasm_global_constructor(*global_constructor);
2214 SetDummyInstanceTemplate(isolate, global_constructor);
2215 JSFunction::EnsureHasInitialMap(global_constructor);
2216 Handle<JSObject> global_proto(
2217 JSObject::cast(global_constructor->instance_prototype()), isolate);
2218 Handle<Map> global_map = isolate->factory()->NewMap(
2219 i::WASM_GLOBAL_OBJECT_TYPE, WasmGlobalObject::kHeaderSize);
2220 JSFunction::SetInitialMap(global_constructor, global_map, global_proto);
2221 InstallFunc(isolate, global_proto, "valueOf", WebAssemblyGlobalValueOf, 0);
2222 InstallGetterSetter(isolate, global_proto, "value", WebAssemblyGlobalGetValue,
2223 WebAssemblyGlobalSetValue);
2224 if (enabled_features.has_type_reflection()) {
2225 InstallFunc(isolate, global_constructor, "type", WebAssemblyGlobalType, 1);
2226 }
2227 JSObject::AddProperty(isolate, global_proto, factory->to_string_tag_symbol(),
2228 v8_str(isolate, "WebAssembly.Global"), ro_attributes);
2229
2230 // Setup Exception
2231 if (enabled_features.has_eh()) {
2232 Handle<JSFunction> exception_constructor = InstallConstructorFunc(
2233 isolate, webassembly, "Exception", WebAssemblyException);
2234 context->set_wasm_exception_constructor(*exception_constructor);
2235 SetDummyInstanceTemplate(isolate, exception_constructor);
2236 JSFunction::EnsureHasInitialMap(exception_constructor);
2237 Handle<JSObject> exception_proto(
2238 JSObject::cast(exception_constructor->instance_prototype()), isolate);
2239 Handle<Map> exception_map = isolate->factory()->NewMap(
2240 i::WASM_EXCEPTION_OBJECT_TYPE, WasmExceptionObject::kHeaderSize);
2241 JSFunction::SetInitialMap(exception_constructor, exception_map,
2242 exception_proto);
2243 }
2244
2245 // Setup Function
2246 if (enabled_features.has_type_reflection()) {
2247 Handle<JSFunction> function_constructor = InstallConstructorFunc(
2248 isolate, webassembly, "Function", WebAssemblyFunction);
2249 SetDummyInstanceTemplate(isolate, function_constructor);
2250 JSFunction::EnsureHasInitialMap(function_constructor);
2251 Handle<JSObject> function_proto(
2252 JSObject::cast(function_constructor->instance_prototype()), isolate);
2253 Handle<Map> function_map = isolate->factory()->CreateSloppyFunctionMap(
2254 FUNCTION_WITHOUT_PROTOTYPE, MaybeHandle<JSFunction>());
2255 CHECK(JSObject::SetPrototype(
2256 function_proto,
2257 handle(context->function_function().prototype(), isolate), false,
2258 kDontThrow)
2259 .FromJust());
2260 JSFunction::SetInitialMap(function_constructor, function_map,
2261 function_proto);
2262 InstallFunc(isolate, function_constructor, "type", WebAssemblyFunctionType,
2263 1);
2264 // Make all exported functions an instance of {WebAssembly.Function}.
2265 context->set_wasm_exported_function_map(*function_map);
2266 } else {
2267 // Make all exported functions an instance of {Function}.
2268 Handle<Map> function_map = isolate->sloppy_function_without_prototype_map();
2269 context->set_wasm_exported_function_map(*function_map);
2270 }
2271
2272 // Setup errors
2273 Handle<JSFunction> compile_error(
2274 isolate->native_context()->wasm_compile_error_function(), isolate);
2275 JSObject::AddProperty(isolate, webassembly,
2276 isolate->factory()->CompileError_string(),
2277 compile_error, DONT_ENUM);
2278 Handle<JSFunction> link_error(
2279 isolate->native_context()->wasm_link_error_function(), isolate);
2280 JSObject::AddProperty(isolate, webassembly,
2281 isolate->factory()->LinkError_string(), link_error,
2282 DONT_ENUM);
2283 Handle<JSFunction> runtime_error(
2284 isolate->native_context()->wasm_runtime_error_function(), isolate);
2285 JSObject::AddProperty(isolate, webassembly,
2286 isolate->factory()->RuntimeError_string(),
2287 runtime_error, DONT_ENUM);
2288 }
2289
2290 namespace {
SetMapValue(Isolate * isolate,Handle<JSMap> map,Handle<Object> key,Handle<Object> value)2291 void SetMapValue(Isolate* isolate, Handle<JSMap> map, Handle<Object> key,
2292 Handle<Object> value) {
2293 DCHECK(!map.is_null() && !key.is_null() && !value.is_null());
2294 Handle<Object> argv[] = {key, value};
2295 Execution::CallBuiltin(isolate, isolate->map_set(), map, arraysize(argv),
2296 argv)
2297 .Check();
2298 }
2299
GetMapValue(Isolate * isolate,Handle<JSMap> map,Handle<Object> key)2300 Handle<Object> GetMapValue(Isolate* isolate, Handle<JSMap> map,
2301 Handle<Object> key) {
2302 DCHECK(!map.is_null() && !key.is_null());
2303 Handle<Object> argv[] = {key};
2304 return Execution::CallBuiltin(isolate, isolate->map_get(), map,
2305 arraysize(argv), argv)
2306 .ToHandleChecked();
2307 }
2308
2309 // Look up a name in a name table. Name tables are stored under the "names"
2310 // property of the handler and map names to index.
ResolveValueSelector(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2311 base::Optional<int> ResolveValueSelector(Isolate* isolate,
2312 Handle<Name> property,
2313 Handle<JSObject> handler,
2314 bool enable_index_lookup) {
2315 size_t index = 0;
2316 if (enable_index_lookup && property->AsIntegerIndex(&index) &&
2317 index < kMaxInt) {
2318 return static_cast<int>(index);
2319 }
2320
2321 Handle<Object> name_table =
2322 JSObject::GetProperty(isolate, handler, "names").ToHandleChecked();
2323 DCHECK(name_table->IsJSMap());
2324
2325 Handle<Object> object =
2326 GetMapValue(isolate, Handle<JSMap>::cast(name_table), property);
2327 if (object->IsUndefined()) return {};
2328 DCHECK(object->IsNumeric());
2329 return NumberToInt32(*object);
2330 }
2331
2332 // Helper for unpacking a maybe name that makes a default with an index if
2333 // the name is empty. If the name is not empty, it's prefixed with a $.
GetNameOrDefault(Isolate * isolate,MaybeHandle<String> maybe_name,const char * default_name_prefix,int index)2334 Handle<String> GetNameOrDefault(Isolate* isolate,
2335 MaybeHandle<String> maybe_name,
2336 const char* default_name_prefix, int index) {
2337 Handle<String> name;
2338 if (maybe_name.ToHandle(&name)) {
2339 return isolate->factory()
2340 ->NewConsString(isolate->factory()->NewStringFromAsciiChecked("$"),
2341 name)
2342 .ToHandleChecked();
2343 }
2344
2345 // Maximum length of the default names: $memory-2147483648\0
2346 static constexpr int kMaxStrLen = 19;
2347 EmbeddedVector<char, kMaxStrLen> value;
2348 DCHECK_LT(strlen(default_name_prefix) + /*strlen(kMinInt)*/ 11, kMaxStrLen);
2349 int len = SNPrintF(value, "%s%d", default_name_prefix, index);
2350 return isolate->factory()->InternalizeString(value.SubVector(0, len));
2351 }
2352
2353 // Generate names for the locals. Names either come from the name table,
2354 // otherwise the default $varX is used.
GetLocalNames(Handle<WasmInstanceObject> instance,Address pc)2355 std::vector<Handle<String>> GetLocalNames(Handle<WasmInstanceObject> instance,
2356 Address pc) {
2357 wasm::NativeModule* native_module = instance->module_object().native_module();
2358 wasm::DebugInfo* debug_info = native_module->GetDebugInfo();
2359 int num_locals = debug_info->GetNumLocals(pc);
2360 auto* isolate = instance->GetIsolate();
2361
2362 wasm::ModuleWireBytes module_wire_bytes(
2363 instance->module_object().native_module()->wire_bytes());
2364 const wasm::WasmFunction& function = debug_info->GetFunctionAtAddress(pc);
2365
2366 std::vector<Handle<String>> names;
2367 for (int i = 0; i < num_locals; ++i) {
2368 wasm::WireBytesRef local_name_ref =
2369 debug_info->GetLocalName(function.func_index, i);
2370 DCHECK(module_wire_bytes.BoundsCheck(local_name_ref));
2371 Vector<const char> name_vec =
2372 module_wire_bytes.GetNameOrNull(local_name_ref);
2373 names.emplace_back(GetNameOrDefault(
2374 isolate,
2375 name_vec.empty() ? MaybeHandle<String>()
2376 : isolate->factory()->NewStringFromUtf8(name_vec),
2377 "$var", i));
2378 }
2379
2380 return names;
2381 }
2382
2383 // Generate names for the globals. Names either come from the name table,
2384 // otherwise the default $globalX is used.
GetGlobalNames(Handle<WasmInstanceObject> instance)2385 std::vector<Handle<String>> GetGlobalNames(
2386 Handle<WasmInstanceObject> instance) {
2387 Isolate* isolate = instance->GetIsolate();
2388 auto& globals = instance->module()->globals;
2389 std::vector<Handle<String>> names;
2390 for (uint32_t i = 0; i < globals.size(); ++i) {
2391 names.emplace_back(GetNameOrDefault(
2392 isolate, WasmInstanceObject::GetGlobalNameOrNull(isolate, instance, i),
2393 "$global", i));
2394 }
2395 return names;
2396 }
2397
2398 // Generate names for the functions.
GetFunctionNames(Handle<WasmInstanceObject> instance)2399 std::vector<Handle<String>> GetFunctionNames(
2400 Handle<WasmInstanceObject> instance) {
2401 Isolate* isolate = instance->GetIsolate();
2402 auto* module = instance->module();
2403
2404 wasm::ModuleWireBytes wire_bytes(
2405 instance->module_object().native_module()->wire_bytes());
2406
2407 std::vector<Handle<String>> names;
2408 for (auto& function : module->functions) {
2409 DCHECK_EQ(function.func_index, names.size());
2410 wasm::WireBytesRef name_ref =
2411 module->lazily_generated_names.LookupFunctionName(
2412 wire_bytes, function.func_index, VectorOf(module->export_table));
2413 DCHECK(wire_bytes.BoundsCheck(name_ref));
2414 Vector<const char> name_vec = wire_bytes.GetNameOrNull(name_ref);
2415 names.emplace_back(GetNameOrDefault(
2416 isolate,
2417 name_vec.empty() ? MaybeHandle<String>()
2418 : isolate->factory()->NewStringFromUtf8(name_vec),
2419 "$func", function.func_index));
2420 }
2421
2422 return names;
2423 }
2424
2425 // Generate names for the imports.
GetImportNames(Handle<WasmInstanceObject> instance)2426 std::vector<Handle<String>> GetImportNames(
2427 Handle<WasmInstanceObject> instance) {
2428 Isolate* isolate = instance->GetIsolate();
2429 const wasm::WasmModule* module = instance->module();
2430 Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
2431 int num_imports = static_cast<int>(module->import_table.size());
2432
2433 std::vector<Handle<String>> names;
2434 for (int index = 0; index < num_imports; ++index) {
2435 const wasm::WasmImport& import = module->import_table[index];
2436
2437 names.emplace_back(WasmModuleObject::ExtractUtf8StringFromModuleBytes(
2438 isolate, module_object, import.field_name, kInternalize));
2439 }
2440
2441 return names;
2442 }
2443
2444 // Generate names for the memories.
GetMemoryNames(Handle<WasmInstanceObject> instance)2445 std::vector<Handle<String>> GetMemoryNames(
2446 Handle<WasmInstanceObject> instance) {
2447 Isolate* isolate = instance->GetIsolate();
2448
2449 std::vector<Handle<String>> names;
2450 uint32_t memory_count = instance->has_memory_object() ? 1 : 0;
2451 for (uint32_t memory_index = 0; memory_index < memory_count; ++memory_index) {
2452 names.emplace_back(GetNameOrDefault(isolate,
2453 WasmInstanceObject::GetMemoryNameOrNull(
2454 isolate, instance, memory_index),
2455 "$memory", memory_index));
2456 }
2457
2458 return names;
2459 }
2460
2461 // Generate names for the tables.
GetTableNames(Handle<WasmInstanceObject> instance)2462 std::vector<Handle<String>> GetTableNames(Handle<WasmInstanceObject> instance) {
2463 Isolate* isolate = instance->GetIsolate();
2464 auto tables = handle(instance->tables(), isolate);
2465
2466 std::vector<Handle<String>> names;
2467 for (int table_index = 0; table_index < tables->length(); ++table_index) {
2468 auto func_table =
2469 handle(WasmTableObject::cast(tables->get(table_index)), isolate);
2470 if (!func_table->type().is_reference_to(wasm::HeapType::kFunc)) continue;
2471
2472 names.emplace_back(GetNameOrDefault(
2473 isolate,
2474 WasmInstanceObject::GetTableNameOrNull(isolate, instance, table_index),
2475 "$table", table_index));
2476 }
2477 return names;
2478 }
2479
2480 // Generate names for the exports
GetExportNames(Handle<WasmInstanceObject> instance)2481 std::vector<Handle<String>> GetExportNames(
2482 Handle<WasmInstanceObject> instance) {
2483 Isolate* isolate = instance->GetIsolate();
2484 const wasm::WasmModule* module = instance->module();
2485 Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
2486 int num_exports = static_cast<int>(module->export_table.size());
2487
2488 std::vector<Handle<String>> names;
2489
2490 for (int index = 0; index < num_exports; ++index) {
2491 const wasm::WasmExport& exp = module->export_table[index];
2492
2493 names.emplace_back(WasmModuleObject::ExtractUtf8StringFromModuleBytes(
2494 isolate, module_object, exp.name, kInternalize));
2495 }
2496 return names;
2497 }
2498
GetInstance(Isolate * isolate,Handle<JSObject> handler)2499 Handle<WasmInstanceObject> GetInstance(Isolate* isolate,
2500 Handle<JSObject> handler) {
2501 Handle<Object> instance =
2502 JSObject::GetProperty(isolate, handler, "instance").ToHandleChecked();
2503 DCHECK(instance->IsWasmInstanceObject());
2504 return Handle<WasmInstanceObject>::cast(instance);
2505 }
2506
GetPC(Isolate * isolate,Handle<JSObject> handler)2507 Address GetPC(Isolate* isolate, Handle<JSObject> handler) {
2508 Handle<Object> pc =
2509 JSObject::GetProperty(isolate, handler, "pc").ToHandleChecked();
2510 DCHECK(pc->IsBigInt());
2511 return Handle<BigInt>::cast(pc)->AsUint64();
2512 }
2513
GetFP(Isolate * isolate,Handle<JSObject> handler)2514 Address GetFP(Isolate* isolate, Handle<JSObject> handler) {
2515 Handle<Object> fp =
2516 JSObject::GetProperty(isolate, handler, "fp").ToHandleChecked();
2517 DCHECK(fp->IsBigInt());
2518 return Handle<BigInt>::cast(fp)->AsUint64();
2519 }
2520
GetCalleeFP(Isolate * isolate,Handle<JSObject> handler)2521 Address GetCalleeFP(Isolate* isolate, Handle<JSObject> handler) {
2522 Handle<Object> callee_fp =
2523 JSObject::GetProperty(isolate, handler, "callee_fp").ToHandleChecked();
2524 DCHECK(callee_fp->IsBigInt());
2525 return Handle<BigInt>::cast(callee_fp)->AsUint64();
2526 }
2527
2528 // Convert a WasmValue to an appropriate JS representation.
WasmValueToObject(Isolate * isolate,wasm::WasmValue value)2529 static Handle<Object> WasmValueToObject(Isolate* isolate,
2530 wasm::WasmValue value) {
2531 auto* factory = isolate->factory();
2532 switch (value.type().kind()) {
2533 case wasm::ValueType::kI32:
2534 return factory->NewNumberFromInt(value.to_i32());
2535 case wasm::ValueType::kI64:
2536 return BigInt::FromInt64(isolate, value.to_i64());
2537 case wasm::ValueType::kF32:
2538 return factory->NewNumber(value.to_f32());
2539 case wasm::ValueType::kF64:
2540 return factory->NewNumber(value.to_f64());
2541 case wasm::ValueType::kS128: {
2542 wasm::Simd128 s128 = value.to_s128();
2543 Handle<JSArrayBuffer> buffer;
2544 if (!isolate->factory()
2545 ->NewJSArrayBufferAndBackingStore(
2546 kSimd128Size, InitializedFlag::kUninitialized)
2547 .ToHandle(&buffer)) {
2548 isolate->FatalProcessOutOfHeapMemory(
2549 "failed to allocate backing store");
2550 }
2551
2552 memcpy(buffer->allocation_base(), s128.bytes(), buffer->byte_length());
2553 return isolate->factory()->NewJSTypedArray(kExternalUint8Array, buffer, 0,
2554 buffer->byte_length());
2555 }
2556 case wasm::ValueType::kRef:
2557 return value.to_externref();
2558 default:
2559 break;
2560 }
2561 return factory->undefined_value();
2562 }
2563
HasLocalImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2564 base::Optional<int> HasLocalImpl(Isolate* isolate, Handle<Name> property,
2565 Handle<JSObject> handler,
2566 bool enable_index_lookup) {
2567 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2568
2569 base::Optional<int> index =
2570 ResolveValueSelector(isolate, property, handler, enable_index_lookup);
2571 if (!index) return index;
2572 Address pc = GetPC(isolate, handler);
2573
2574 wasm::DebugInfo* debug_info =
2575 instance->module_object().native_module()->GetDebugInfo();
2576 int num_locals = debug_info->GetNumLocals(pc);
2577 if (0 <= index && index < num_locals) return index;
2578 return {};
2579 }
2580
GetLocalImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2581 Handle<Object> GetLocalImpl(Isolate* isolate, Handle<Name> property,
2582 Handle<JSObject> handler,
2583 bool enable_index_lookup) {
2584 Factory* factory = isolate->factory();
2585 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2586
2587 base::Optional<int> index =
2588 HasLocalImpl(isolate, property, handler, enable_index_lookup);
2589 if (!index) return factory->undefined_value();
2590 Address pc = GetPC(isolate, handler);
2591 Address fp = GetFP(isolate, handler);
2592 Address callee_fp = GetCalleeFP(isolate, handler);
2593
2594 wasm::DebugInfo* debug_info =
2595 instance->module_object().native_module()->GetDebugInfo();
2596 wasm::WasmValue value = debug_info->GetLocalValue(*index, pc, fp, callee_fp);
2597 return WasmValueToObject(isolate, value);
2598 }
2599
HasGlobalImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2600 base::Optional<int> HasGlobalImpl(Isolate* isolate, Handle<Name> property,
2601 Handle<JSObject> handler,
2602 bool enable_index_lookup) {
2603 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2604 base::Optional<int> index =
2605 ResolveValueSelector(isolate, property, handler, enable_index_lookup);
2606 if (!index) return index;
2607
2608 const std::vector<wasm::WasmGlobal>& globals = instance->module()->globals;
2609 if (globals.size() <= kMaxInt && 0 <= *index &&
2610 *index < static_cast<int>(globals.size())) {
2611 return index;
2612 }
2613 return {};
2614 }
2615
GetGlobalImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2616 Handle<Object> GetGlobalImpl(Isolate* isolate, Handle<Name> property,
2617 Handle<JSObject> handler,
2618 bool enable_index_lookup) {
2619 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2620 base::Optional<int> index =
2621 HasGlobalImpl(isolate, property, handler, enable_index_lookup);
2622 if (!index) return isolate->factory()->undefined_value();
2623
2624 const std::vector<wasm::WasmGlobal>& globals = instance->module()->globals;
2625 return WasmValueToObject(
2626 isolate, WasmInstanceObject::GetGlobalValue(instance, globals[*index]));
2627 }
2628
HasMemoryImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2629 base::Optional<int> HasMemoryImpl(Isolate* isolate, Handle<Name> property,
2630 Handle<JSObject> handler,
2631 bool enable_index_lookup) {
2632 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2633 base::Optional<int> index =
2634 ResolveValueSelector(isolate, property, handler, enable_index_lookup);
2635 if (index && *index == 0 && instance->has_memory_object()) return index;
2636 return {};
2637 }
2638
GetMemoryImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2639 Handle<Object> GetMemoryImpl(Isolate* isolate, Handle<Name> property,
2640 Handle<JSObject> handler,
2641 bool enable_index_lookup) {
2642 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2643 base::Optional<int> index =
2644 HasMemoryImpl(isolate, property, handler, enable_index_lookup);
2645 if (index) return handle(instance->memory_object(), isolate);
2646 return isolate->factory()->undefined_value();
2647 }
2648
HasFunctionImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2649 base::Optional<int> HasFunctionImpl(Isolate* isolate, Handle<Name> property,
2650 Handle<JSObject> handler,
2651 bool enable_index_lookup) {
2652 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2653 base::Optional<int> index =
2654 ResolveValueSelector(isolate, property, handler, enable_index_lookup);
2655 if (!index) return index;
2656 const std::vector<wasm::WasmFunction>& functions =
2657 instance->module()->functions;
2658 if (functions.size() <= kMaxInt && 0 <= *index &&
2659 *index < static_cast<int>(functions.size())) {
2660 return index;
2661 }
2662 return {};
2663 }
2664
GetFunctionImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2665 Handle<Object> GetFunctionImpl(Isolate* isolate, Handle<Name> property,
2666 Handle<JSObject> handler,
2667 bool enable_index_lookup) {
2668 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2669 base::Optional<int> index =
2670 HasFunctionImpl(isolate, property, handler, enable_index_lookup);
2671 if (!index) return isolate->factory()->undefined_value();
2672
2673 return WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate, instance,
2674 *index);
2675 }
2676
HasTableImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2677 base::Optional<int> HasTableImpl(Isolate* isolate, Handle<Name> property,
2678 Handle<JSObject> handler,
2679 bool enable_index_lookup) {
2680 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2681 base::Optional<int> index =
2682 ResolveValueSelector(isolate, property, handler, enable_index_lookup);
2683 if (!index) return index;
2684 Handle<FixedArray> tables(instance->tables(), isolate);
2685 int num_tables = tables->length();
2686 if (*index < 0 || *index >= num_tables) return {};
2687
2688 Handle<WasmTableObject> func_table(WasmTableObject::cast(tables->get(*index)),
2689 isolate);
2690 if (func_table->type().is_reference_to(wasm::HeapType::kFunc)) return index;
2691 return {};
2692 }
2693
GetTableImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2694 Handle<Object> GetTableImpl(Isolate* isolate, Handle<Name> property,
2695 Handle<JSObject> handler,
2696 bool enable_index_lookup) {
2697 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2698 base::Optional<int> index =
2699 HasTableImpl(isolate, property, handler, enable_index_lookup);
2700 if (!index) return isolate->factory()->undefined_value();
2701
2702 Handle<WasmTableObject> func_table(
2703 WasmTableObject::cast(instance->tables().get(*index)), isolate);
2704 return func_table;
2705 }
2706
HasImportImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2707 base::Optional<int> HasImportImpl(Isolate* isolate, Handle<Name> property,
2708 Handle<JSObject> handler,
2709 bool enable_index_lookup) {
2710 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2711 base::Optional<int> index =
2712 ResolveValueSelector(isolate, property, handler, enable_index_lookup);
2713 if (!index) return index;
2714 const wasm::WasmModule* module = instance->module();
2715 Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
2716 int num_imports = static_cast<int>(module->import_table.size());
2717 if (0 <= *index && *index < num_imports) return index;
2718 return {};
2719 }
2720
GetExternalObject(Isolate * isolate,wasm::ImportExportKindCode kind,uint32_t index)2721 Handle<JSObject> GetExternalObject(Isolate* isolate,
2722 wasm::ImportExportKindCode kind,
2723 uint32_t index) {
2724 Handle<JSObject> result = isolate->factory()->NewJSObjectWithNullProto();
2725 Handle<Object> value = isolate->factory()->NewNumberFromUint(index);
2726 switch (kind) {
2727 case wasm::kExternalFunction:
2728 JSObject::AddProperty(isolate, result, "func", value, NONE);
2729 break;
2730 case wasm::kExternalGlobal:
2731 JSObject::AddProperty(isolate, result, "global", value, NONE);
2732 break;
2733 case wasm::kExternalTable:
2734 JSObject::AddProperty(isolate, result, "table", value, NONE);
2735 break;
2736 case wasm::kExternalMemory:
2737 JSObject::AddProperty(isolate, result, "mem", value, NONE);
2738 break;
2739 case wasm::kExternalException:
2740 JSObject::AddProperty(isolate, result, "exn", value, NONE);
2741 break;
2742 }
2743 return result;
2744 }
2745
GetImportImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2746 Handle<Object> GetImportImpl(Isolate* isolate, Handle<Name> property,
2747 Handle<JSObject> handler,
2748 bool enable_index_lookup) {
2749 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2750 base::Optional<int> index =
2751 HasImportImpl(isolate, property, handler, enable_index_lookup);
2752 if (!index) return isolate->factory()->undefined_value();
2753
2754 const wasm::WasmImport& imp = instance->module()->import_table[*index];
2755 return GetExternalObject(isolate, imp.kind, imp.index);
2756 }
2757
HasExportImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2758 base::Optional<int> HasExportImpl(Isolate* isolate, Handle<Name> property,
2759 Handle<JSObject> handler,
2760 bool enable_index_lookup) {
2761 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2762 base::Optional<int> index =
2763 ResolveValueSelector(isolate, property, handler, enable_index_lookup);
2764 if (!index) return index;
2765
2766 const wasm::WasmModule* module = instance->module();
2767 Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
2768 int num_exports = static_cast<int>(module->export_table.size());
2769 if (0 <= *index && *index < num_exports) return index;
2770 return {};
2771 }
2772
GetExportImpl(Isolate * isolate,Handle<Name> property,Handle<JSObject> handler,bool enable_index_lookup)2773 Handle<Object> GetExportImpl(Isolate* isolate, Handle<Name> property,
2774 Handle<JSObject> handler,
2775 bool enable_index_lookup) {
2776 Handle<WasmInstanceObject> instance = GetInstance(isolate, handler);
2777 base::Optional<int> index =
2778 HasExportImpl(isolate, property, handler, enable_index_lookup);
2779 if (!index) return isolate->factory()->undefined_value();
2780
2781 const wasm::WasmExport& exp = instance->module()->export_table[*index];
2782 return GetExternalObject(isolate, exp.kind, exp.index);
2783 }
2784
2785 // Generic has trap callback for the index space proxies.
2786 template <base::Optional<int> Impl(Isolate*, Handle<Name>, Handle<JSObject>,
2787 bool)>
HasTrapCallback(const v8::FunctionCallbackInfo<v8::Value> & args)2788 void HasTrapCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
2789 DCHECK_GE(args.Length(), 2);
2790 Isolate* isolate = reinterpret_cast<Isolate*>(args.GetIsolate());
2791 DCHECK(args.This()->IsObject());
2792 Handle<JSObject> handler =
2793 Handle<JSObject>::cast(Utils::OpenHandle(*args.This()));
2794
2795 DCHECK(args[1]->IsName());
2796 Handle<Name> property = Handle<Name>::cast(Utils::OpenHandle(*args[1]));
2797 args.GetReturnValue().Set(Impl(isolate, property, handler, true).has_value());
2798 }
2799
2800 // Generic get trap callback for the index space proxies.
2801 template <Handle<Object> Impl(Isolate*, Handle<Name>, Handle<JSObject>, bool)>
GetTrapCallback(const v8::FunctionCallbackInfo<v8::Value> & args)2802 void GetTrapCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
2803 DCHECK_GE(args.Length(), 2);
2804 Isolate* isolate = reinterpret_cast<Isolate*>(args.GetIsolate());
2805 DCHECK(args.This()->IsObject());
2806 Handle<JSObject> handler =
2807 Handle<JSObject>::cast(Utils::OpenHandle(*args.This()));
2808
2809 DCHECK(args[1]->IsName());
2810 Handle<Name> property = Handle<Name>::cast(Utils::OpenHandle(*args[1]));
2811 args.GetReturnValue().Set(
2812 Utils::ToLocal(Impl(isolate, property, handler, true)));
2813 }
2814
2815 template <typename ReturnT>
DelegateToplevelCall(Isolate * isolate,Handle<JSObject> target,Handle<Name> property,const char * index_space,ReturnT (* impl)(Isolate *,Handle<Name>,Handle<JSObject>,bool))2816 ReturnT DelegateToplevelCall(Isolate* isolate, Handle<JSObject> target,
2817 Handle<Name> property, const char* index_space,
2818 ReturnT (*impl)(Isolate*, Handle<Name>,
2819 Handle<JSObject>, bool)) {
2820 Handle<Object> namespace_proxy =
2821 JSObject::GetProperty(isolate, target, index_space).ToHandleChecked();
2822 DCHECK(namespace_proxy->IsJSProxy());
2823 Handle<JSObject> namespace_handler(
2824 JSObject::cast(Handle<JSProxy>::cast(namespace_proxy)->handler()),
2825 isolate);
2826 return impl(isolate, property, namespace_handler, false);
2827 }
2828
2829 template <typename ReturnT>
2830 using DelegateCallback = ReturnT (*)(Isolate*, Handle<Name>, Handle<JSObject>,
2831 bool);
2832
2833 // Has trap callback for the top-level proxy.
ToplevelHasTrapCallback(const v8::FunctionCallbackInfo<v8::Value> & args)2834 void ToplevelHasTrapCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
2835 DCHECK_GE(args.Length(), 2);
2836 Isolate* isolate = reinterpret_cast<Isolate*>(args.GetIsolate());
2837 DCHECK(args[0]->IsObject());
2838 Handle<JSObject> target = Handle<JSObject>::cast(Utils::OpenHandle(*args[0]));
2839
2840 DCHECK(args[1]->IsName());
2841 Handle<Name> property = Handle<Name>::cast(Utils::OpenHandle(*args[1]));
2842
2843 // First check if the property exists on the target.
2844 if (JSObject::HasProperty(target, property).FromMaybe(false)) {
2845 args.GetReturnValue().Set(true);
2846 return;
2847 }
2848
2849 // Now check the index space proxies in order if they know the property.
2850 constexpr std::pair<const char*, DelegateCallback<base::Optional<int>>>
2851 kDelegates[] = {{"memories", HasMemoryImpl},
2852 {"locals", HasLocalImpl},
2853 {"tables", HasTableImpl},
2854 {"functions", HasFunctionImpl},
2855 {"globals", HasGlobalImpl}};
2856 for (auto& delegate : kDelegates) {
2857 if (DelegateToplevelCall(isolate, target, property, delegate.first,
2858 delegate.second)) {
2859 args.GetReturnValue().Set(true);
2860 return;
2861 }
2862 args.GetReturnValue().Set(false);
2863 }
2864 }
2865
2866 // Get trap callback for the top-level proxy.
ToplevelGetTrapCallback(const v8::FunctionCallbackInfo<v8::Value> & args)2867 void ToplevelGetTrapCallback(const v8::FunctionCallbackInfo<v8::Value>& args) {
2868 DCHECK_GE(args.Length(), 2);
2869 Isolate* isolate = reinterpret_cast<Isolate*>(args.GetIsolate());
2870 DCHECK(args[0]->IsObject());
2871 Handle<JSObject> target = Handle<JSObject>::cast(Utils::OpenHandle(*args[0]));
2872
2873 DCHECK(args[1]->IsName());
2874 Handle<Name> property = Handle<Name>::cast(Utils::OpenHandle(*args[1]));
2875
2876 // First, check if the property is a proper property on the target. If so,
2877 // return its value.
2878 Handle<Object> value =
2879 JSObject::GetProperty(isolate, target, property).ToHandleChecked();
2880 if (!value->IsUndefined()) {
2881 args.GetReturnValue().Set(Utils::ToLocal(value));
2882 return;
2883 }
2884
2885 // Try the index space proxies in the correct disambiguation order.
2886 constexpr std::pair<const char*, DelegateCallback<Handle<Object>>>
2887 kDelegates[] = {{"memories", GetMemoryImpl},
2888 {"locals", GetLocalImpl},
2889 {"tables", GetTableImpl},
2890 {"functions", GetFunctionImpl},
2891 {"globals", GetGlobalImpl}};
2892 for (auto& delegate : kDelegates) {
2893 value = DelegateToplevelCall(isolate, target, property, delegate.first,
2894 delegate.second);
2895 if (!value->IsUndefined()) {
2896 args.GetReturnValue().Set(Utils::ToLocal(value));
2897 return;
2898 }
2899 }
2900 }
2901
2902 // Populate a JSMap with name->index mappings from an ordered list of names.
GetNameTable(Isolate * isolate,const std::vector<Handle<String>> & names)2903 Handle<JSMap> GetNameTable(Isolate* isolate,
2904 const std::vector<Handle<String>>& names) {
2905 Factory* factory = isolate->factory();
2906 Handle<JSMap> name_table = factory->NewJSMap();
2907
2908 for (size_t i = 0; i < names.size(); ++i) {
2909 SetMapValue(isolate, name_table, names[i], factory->NewNumberFromInt64(i));
2910 }
2911 return name_table;
2912 }
2913
2914 // Produce a JSProxy with a given name table and get and has trap handlers.
GetJSProxy(WasmFrame * frame,Handle<JSMap> name_table,void (* get_callback)(const v8::FunctionCallbackInfo<v8::Value> &),void (* has_callback)(const v8::FunctionCallbackInfo<v8::Value> &))2915 Handle<JSProxy> GetJSProxy(
2916 WasmFrame* frame, Handle<JSMap> name_table,
2917 void (*get_callback)(const v8::FunctionCallbackInfo<v8::Value>&),
2918 void (*has_callback)(const v8::FunctionCallbackInfo<v8::Value>&)) {
2919 Isolate* isolate = frame->isolate();
2920 Factory* factory = isolate->factory();
2921 Handle<JSObject> target = factory->NewJSObjectWithNullProto();
2922 Handle<JSObject> handler = factory->NewJSObjectWithNullProto();
2923
2924 // Besides the name table, the get and has traps need access to the instance
2925 // and frame information.
2926 JSObject::AddProperty(isolate, handler, "names", name_table, DONT_ENUM);
2927 Handle<WasmInstanceObject> instance(frame->wasm_instance(), isolate);
2928 JSObject::AddProperty(isolate, handler, "instance", instance, DONT_ENUM);
2929 Handle<BigInt> pc = BigInt::FromInt64(isolate, frame->pc());
2930 JSObject::AddProperty(isolate, handler, "pc", pc, DONT_ENUM);
2931 Handle<BigInt> fp = BigInt::FromInt64(isolate, frame->fp());
2932 JSObject::AddProperty(isolate, handler, "fp", fp, DONT_ENUM);
2933 Handle<BigInt> callee_fp = BigInt::FromInt64(isolate, frame->callee_fp());
2934 JSObject::AddProperty(isolate, handler, "callee_fp", callee_fp, DONT_ENUM);
2935
2936 InstallFunc(isolate, handler, "get", get_callback, 3, false, READ_ONLY);
2937 InstallFunc(isolate, handler, "has", has_callback, 2, false, READ_ONLY);
2938
2939 return factory->NewJSProxy(target, handler);
2940 }
2941
GetStackObject(WasmFrame * frame)2942 Handle<JSObject> GetStackObject(WasmFrame* frame) {
2943 Isolate* isolate = frame->isolate();
2944 Handle<JSObject> object = isolate->factory()->NewJSObjectWithNullProto();
2945 wasm::DebugInfo* debug_info =
2946 frame->wasm_instance().module_object().native_module()->GetDebugInfo();
2947 int num_values = debug_info->GetStackDepth(frame->pc());
2948 for (int i = 0; i < num_values; ++i) {
2949 wasm::WasmValue value = debug_info->GetStackValue(
2950 i, frame->pc(), frame->fp(), frame->callee_fp());
2951 JSObject::AddDataElement(object, i, WasmValueToObject(isolate, value),
2952 NONE);
2953 }
2954 return object;
2955 }
2956 } // namespace
2957
2958 // This function generates the JS debug proxy for a given Wasm frame. The debug
2959 // proxy is used when evaluating debug JS expressions on a wasm frame and let's
2960 // the developer inspect the engine state from JS. The proxy provides the
2961 // following interface:
2962 //
2963 // type WasmSimdValue = Uint8Array;
2964 // type WasmValue = number | bigint | object | WasmSimdValue;
2965 // type WasmFunction = (... args : WasmValue[]) = > WasmValue;
2966 // type WasmExport = {name : string} & ({func : number} | {table : number} |
2967 // {mem : number} | {global : number});
2968 // type WasmImport = {name : string, module : string} &
2969 // ({func : number} | {table : number} | {mem : number} |
2970 // {global : number});
2971 // interface WasmInterface {
2972 // $globalX: WasmValue;
2973 // $varX: WasmValue;
2974 // $funcX(a : WasmValue /*, ...*/) : WasmValue;
2975 // readonly $memoryX : WebAssembly.Memory;
2976 // readonly $tableX : WebAssembly.Table;
2977 // readonly memories : {[nameOrIndex:string | number] : WebAssembly.Memory};
2978 // readonly tables : {[nameOrIndex:string | number] : WebAssembly.Table};
2979 // readonly stack : WasmValue[];
2980 // readonly imports : {[nameOrIndex:string | number] : WasmImport};
2981 // readonly exports : {[nameOrIndex:string | number] : WasmExport};
2982 // readonly globals : {[nameOrIndex:string | number] : WasmValue};
2983 // readonly locals : {[nameOrIndex:string | number] : WasmValue};
2984 // readonly functions : {[nameOrIndex:string | number] : WasmFunction};
2985 // }
2986 //
2987 // The wasm index spaces memories, tables, imports, exports, globals, locals
2988 // functions are JSProxies that lazily produce values either by index or by
2989 // name. A top level JSProxy is wrapped around those for top-level lookup of
2990 // names in the disambiguation order memory, local, table, function, global.
2991 // Import and export names are not globally resolved.
2992
GetJSDebugProxy(WasmFrame * frame)2993 Handle<JSProxy> WasmJs::GetJSDebugProxy(WasmFrame* frame) {
2994 Isolate* isolate = frame->isolate();
2995 Factory* factory = isolate->factory();
2996 Handle<WasmInstanceObject> instance(frame->wasm_instance(), isolate);
2997
2998 // The top level proxy delegates lookups to the index space proxies.
2999 Handle<JSObject> handler = factory->NewJSObjectWithNullProto();
3000 InstallFunc(isolate, handler, "get", ToplevelGetTrapCallback, 3, false,
3001 READ_ONLY);
3002 InstallFunc(isolate, handler, "has", ToplevelHasTrapCallback, 2, false,
3003 READ_ONLY);
3004
3005 Handle<JSObject> target = factory->NewJSObjectWithNullProto();
3006
3007 // Generate JSMaps per index space for name->index lookup. Every index space
3008 // proxy is associated with its table for local name lookup.
3009
3010 auto local_name_table =
3011 GetNameTable(isolate, GetLocalNames(instance, frame->pc()));
3012 auto locals =
3013 GetJSProxy(frame, local_name_table, GetTrapCallback<GetLocalImpl>,
3014 HasTrapCallback<HasLocalImpl>);
3015 JSObject::AddProperty(isolate, target, "locals", locals, READ_ONLY);
3016
3017 auto global_name_table = GetNameTable(isolate, GetGlobalNames(instance));
3018 auto globals =
3019 GetJSProxy(frame, global_name_table, GetTrapCallback<GetGlobalImpl>,
3020 HasTrapCallback<HasGlobalImpl>);
3021 JSObject::AddProperty(isolate, target, "globals", globals, READ_ONLY);
3022
3023 auto function_name_table = GetNameTable(isolate, GetFunctionNames(instance));
3024 auto functions =
3025 GetJSProxy(frame, function_name_table, GetTrapCallback<GetFunctionImpl>,
3026 HasTrapCallback<HasFunctionImpl>);
3027 JSObject::AddProperty(isolate, target, "functions", functions, READ_ONLY);
3028
3029 auto memory_name_table = GetNameTable(isolate, GetMemoryNames(instance));
3030 auto memories =
3031 GetJSProxy(frame, memory_name_table, GetTrapCallback<GetMemoryImpl>,
3032 HasTrapCallback<HasMemoryImpl>);
3033 JSObject::AddProperty(isolate, target, "memories", memories, READ_ONLY);
3034
3035 auto table_name_table = GetNameTable(isolate, GetTableNames(instance));
3036 auto tables =
3037 GetJSProxy(frame, table_name_table, GetTrapCallback<GetTableImpl>,
3038 HasTrapCallback<HasTableImpl>);
3039 JSObject::AddProperty(isolate, target, "tables", tables, READ_ONLY);
3040
3041 auto import_name_table = GetNameTable(isolate, GetImportNames(instance));
3042 auto imports =
3043 GetJSProxy(frame, import_name_table, GetTrapCallback<GetImportImpl>,
3044 HasTrapCallback<HasImportImpl>);
3045 JSObject::AddProperty(isolate, target, "imports", imports, READ_ONLY);
3046
3047 auto export_name_table = GetNameTable(isolate, GetExportNames(instance));
3048 auto exports =
3049 GetJSProxy(frame, export_name_table, GetTrapCallback<GetExportImpl>,
3050 HasTrapCallback<HasExportImpl>);
3051 JSObject::AddProperty(isolate, target, "exports", exports, READ_ONLY);
3052
3053 auto stack = GetStackObject(frame);
3054 JSObject::AddProperty(isolate, target, "stack", stack, READ_ONLY);
3055
3056 return factory->NewJSProxy(target, handler);
3057 }
3058
3059 #undef ASSIGN
3060 #undef EXTRACT_THIS
3061
3062 } // namespace internal
3063 } // namespace v8
3064