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(&parameters_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