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