wasm-js.cc 128 KB
Newer Older
1 2 3 4
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#include "src/wasm/wasm-js.h"

7 8
#include <cinttypes>
#include <cstring>
9

10
#include "include/v8-function.h"
11 12
#include "include/v8-persistent-handle.h"
#include "include/v8-promise.h"
13
#include "include/v8-wasm.h"
14 15
#include "src/api/api-inl.h"
#include "src/api/api-natives.h"
16
#include "src/base/logging.h"
17 18
#include "src/execution/execution.h"
#include "src/execution/isolate.h"
19
#include "src/execution/messages.h"
20
#include "src/handles/handles.h"
21
#include "src/heap/factory.h"
22
#include "src/objects/fixed-array.h"
23
#include "src/objects/instance-type.h"
24
#include "src/objects/js-function.h"
25
#include "src/objects/managed-inl.h"
26
#include "src/objects/objects-inl.h"
27
#include "src/objects/shared-function-info.h"
28
#include "src/objects/templates.h"
29
#include "src/wasm/function-compiler.h"
30
#include "src/wasm/streaming-decoder.h"
31
#include "src/wasm/value-type.h"
32
#include "src/wasm/wasm-debug.h"
33
#include "src/wasm/wasm-engine.h"
34
#include "src/wasm/wasm-limits.h"
35
#include "src/wasm/wasm-objects-inl.h"
36
#include "src/wasm/wasm-serialization.h"
37
#include "src/wasm/wasm-value.h"
38 39

using v8::internal::wasm::ErrorThrower;
40
using v8::internal::wasm::ScheduledErrorThrower;
41 42 43

namespace v8 {

44 45
class WasmStreaming::WasmStreamingImpl {
 public:
46
  WasmStreamingImpl(
47
      Isolate* isolate, const char* api_method_name,
48 49 50
      std::shared_ptr<internal::wasm::CompilationResultResolver> resolver)
      : isolate_(isolate), resolver_(std::move(resolver)) {
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate_);
51
    auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
52
    streaming_decoder_ = i::wasm::GetWasmEngine()->StartStreamingCompilation(
53
        i_isolate, enabled_features, handle(i_isolate->context(), i_isolate),
54
        api_method_name, resolver_);
55 56 57
  }

  void OnBytesReceived(const uint8_t* bytes, size_t size) {
58
    streaming_decoder_->OnBytesReceived(base::VectorOf(bytes, size));
59
  }
60 61 62
  void Finish(bool can_use_compiled_module) {
    streaming_decoder_->Finish(can_use_compiled_module);
  }
63

64 65 66 67 68 69 70 71 72 73 74 75
  void Abort(MaybeLocal<Value> exception) {
    i::HandleScope scope(reinterpret_cast<i::Isolate*>(isolate_));
    streaming_decoder_->Abort();

    // If no exception value is provided, we do not reject the promise. This can
    // happen when streaming compilation gets aborted when no script execution
    // is allowed anymore, e.g. when a browser tab gets refreshed.
    if (exception.IsEmpty()) return;

    resolver_->OnCompilationFailed(
        Utils::OpenHandle(*exception.ToLocalChecked()));
  }
76

77 78 79 80
  bool SetCompiledModuleBytes(base::Vector<const uint8_t> bytes) {
    if (!i::wasm::IsSupportedVersion(bytes)) return false;
    streaming_decoder_->SetCompiledModuleBytes(bytes);
    return true;
81 82
  }

83 84 85 86 87 88 89 90
  void SetMoreFunctionsCanBeSerializedCallback(
      std::function<void(CompiledWasmModule)> callback) {
    streaming_decoder_->SetMoreFunctionsCanBeSerializedCallback(
        [callback = std::move(callback),
         streaming_decoder = streaming_decoder_](
            const std::shared_ptr<i::wasm::NativeModule>& native_module) {
          base::Vector<const char> url = streaming_decoder->url();
          callback(CompiledWasmModule{native_module, url.begin(), url.size()});
91 92 93
        });
  }

94
  void SetUrl(base::Vector<const char> url) { streaming_decoder_->SetUrl(url); }
95

96
 private:
97
  Isolate* const isolate_;
98 99
  std::shared_ptr<internal::wasm::StreamingDecoder> streaming_decoder_;
  std::shared_ptr<internal::wasm::CompilationResultResolver> resolver_;
100 101 102
};

WasmStreaming::WasmStreaming(std::unique_ptr<WasmStreamingImpl> impl)
103 104 105
    : impl_(std::move(impl)) {
  TRACE_EVENT0("v8.wasm", "wasm.InitializeStreaming");
}
106 107 108 109 110 111

// The destructor is defined here because we have a unique_ptr with forward
// declaration.
WasmStreaming::~WasmStreaming() = default;

void WasmStreaming::OnBytesReceived(const uint8_t* bytes, size_t size) {
112
  TRACE_EVENT1("v8.wasm", "wasm.OnBytesReceived", "bytes", size);
113 114 115
  impl_->OnBytesReceived(bytes, size);
}

116
void WasmStreaming::Finish(bool can_use_compiled_module) {
117
  TRACE_EVENT0("v8.wasm", "wasm.FinishStreaming");
118
  impl_->Finish(can_use_compiled_module);
119
}
120 121

void WasmStreaming::Abort(MaybeLocal<Value> exception) {
122
  TRACE_EVENT0("v8.wasm", "wasm.AbortStreaming");
123 124 125
  impl_->Abort(exception);
}

126
bool WasmStreaming::SetCompiledModuleBytes(const uint8_t* bytes, size_t size) {
127
  TRACE_EVENT0("v8.wasm", "wasm.SetCompiledModuleBytes");
128
  return impl_->SetCompiledModuleBytes(base::VectorOf(bytes, size));
129 130
}

131 132 133 134 135
void WasmStreaming::SetMoreFunctionsCanBeSerializedCallback(
    std::function<void(CompiledWasmModule)> callback) {
  impl_->SetMoreFunctionsCanBeSerializedCallback(std::move(callback));
}

136
void WasmStreaming::SetUrl(const char* url, size_t length) {
137 138
  DCHECK_EQ('\0', url[length]);  // {url} is null-terminated.
  TRACE_EVENT1("v8.wasm", "wasm.SetUrl", "url", url);
139
  impl_->SetUrl(base::VectorOf(url, length));
140 141
}

142 143 144
// static
std::shared_ptr<WasmStreaming> WasmStreaming::Unpack(Isolate* isolate,
                                                     Local<Value> value) {
145
  TRACE_EVENT0("v8.wasm", "wasm.WasmStreaming.Unpack");
146 147 148 149 150 151
  i::HandleScope scope(reinterpret_cast<i::Isolate*>(isolate));
  auto managed =
      i::Handle<i::Managed<WasmStreaming>>::cast(Utils::OpenHandle(*value));
  return managed->get();
}

152
namespace {
153

154 155 156 157 158 159 160 161 162
#define ASSIGN(type, var, expr)                      \
  Local<type> var;                                   \
  do {                                               \
    if (!expr.ToLocal(&var)) {                       \
      DCHECK(i_isolate->has_scheduled_exception());  \
      return;                                        \
    } else {                                         \
      DCHECK(!i_isolate->has_scheduled_exception()); \
    }                                                \
163 164
  } while (false)

165 166 167 168 169 170 171
i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) {
  return isolate->factory()->NewStringFromAsciiChecked(str);
}
Local<String> v8_str(Isolate* isolate, const char* str) {
  return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str));
}

172 173 174 175 176 177 178 179 180
#define GET_FIRST_ARGUMENT_AS(Type)                                  \
  i::MaybeHandle<i::Wasm##Type##Object> GetFirstArgumentAs##Type(    \
      const v8::FunctionCallbackInfo<v8::Value>& args,               \
      ErrorThrower* thrower) {                                       \
    i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);         \
    if (!arg0->IsWasm##Type##Object()) {                             \
      thrower->TypeError("Argument 0 must be a WebAssembly." #Type); \
      return {};                                                     \
    }                                                                \
181
    return i::Handle<i::Wasm##Type##Object>::cast(arg0);             \
182 183
  }

184
GET_FIRST_ARGUMENT_AS(Module)
185
GET_FIRST_ARGUMENT_AS(Tag)
186 187

#undef GET_FIRST_ARGUMENT_AS
188 189

i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
190 191
    const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower,
    bool* is_shared) {
192
  const uint8_t* start = nullptr;
193
  size_t length = 0;
194
  v8::Local<v8::Value> source = args[0];
rossberg's avatar
rossberg committed
195
  if (source->IsArrayBuffer()) {
196
    // A raw array buffer was passed.
rossberg's avatar
rossberg committed
197
    Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(source);
198
    auto backing_store = buffer->GetBackingStore();
199

200 201
    start = reinterpret_cast<const uint8_t*>(backing_store->Data());
    length = backing_store->ByteLength();
202
    *is_shared = buffer->IsSharedArrayBuffer();
rossberg's avatar
rossberg committed
203
  } else if (source->IsTypedArray()) {
204
    // A TypedArray was passed.
rossberg's avatar
rossberg committed
205
    Local<TypedArray> array = Local<TypedArray>::Cast(source);
206 207
    Local<ArrayBuffer> buffer = array->Buffer();

208
    auto backing_store = buffer->GetBackingStore();
209

210 211
    start = reinterpret_cast<const uint8_t*>(backing_store->Data()) +
            array->ByteOffset();
212
    length = array->ByteLength();
213
    *is_shared = buffer->IsSharedArrayBuffer();
214
  } else {
215
    thrower->TypeError("Argument 0 must be a buffer source");
216
  }
217 218
  DCHECK_IMPLIES(length, start != nullptr);
  if (length == 0) {
219 220
    thrower->CompileError("BufferSource argument is empty");
  }
221 222
  size_t max_length = i::wasm::max_module_size();
  if (length > max_length) {
223
    thrower->RangeError("buffer source exceeds maximum size of %zu (is %zu)",
224
                        max_length, length);
225 226 227
  }
  if (thrower->error()) return i::wasm::ModuleWireBytes(nullptr, nullptr);
  return i::wasm::ModuleWireBytes(start, start + length);
228 229
}

230 231 232
namespace {
i::MaybeHandle<i::JSReceiver> ImportsAsMaybeReceiver(Local<Value> ffi) {
  if (ffi->IsUndefined()) return {};
233

234
  Local<Object> obj = Local<Object>::Cast(ffi);
235
  return i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
236 237
}

238 239 240 241
// This class resolves the result of WebAssembly.compile. It just places the
// compilation result in the supplied {promise}.
class AsyncCompilationResolver : public i::wasm::CompilationResultResolver {
 public:
242 243 244 245 246 247 248
  AsyncCompilationResolver(Isolate* isolate, Local<Context> context,
                           Local<Promise::Resolver> promise_resolver)
      : isolate_(isolate),
        context_(isolate, context),
        promise_resolver_(isolate, promise_resolver) {
    context_.SetWeak();
    promise_resolver_.AnnotateStrongRetainer(kGlobalPromiseHandle);
249 250 251
  }

  void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result) override {
252 253
    if (finished_) return;
    finished_ = true;
254 255 256 257 258 259 260
    if (context_.IsEmpty()) return;
    auto callback = reinterpret_cast<i::Isolate*>(isolate_)
                        ->wasm_async_resolve_promise_callback();
    CHECK(callback);
    callback(isolate_, context_.Get(isolate_), promise_resolver_.Get(isolate_),
             Utils::ToLocal(i::Handle<i::Object>::cast(result)),
             WasmAsyncSuccess::kSuccess);
261 262 263
  }

  void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
264 265
    if (finished_) return;
    finished_ = true;
266 267 268 269 270 271
    if (context_.IsEmpty()) return;
    auto callback = reinterpret_cast<i::Isolate*>(isolate_)
                        ->wasm_async_resolve_promise_callback();
    CHECK(callback);
    callback(isolate_, context_.Get(isolate_), promise_resolver_.Get(isolate_),
             Utils::ToLocal(error_reason), WasmAsyncSuccess::kFail);
272 273 274
  }

 private:
275 276
  static constexpr char kGlobalPromiseHandle[] =
      "AsyncCompilationResolver::promise_";
277
  bool finished_ = false;
278 279 280
  Isolate* isolate_;
  Global<Context> context_;
  Global<Promise::Resolver> promise_resolver_;
281 282
};

283 284
constexpr char AsyncCompilationResolver::kGlobalPromiseHandle[];

285 286 287 288 289
// This class resolves the result of WebAssembly.instantiate(module, imports).
// It just places the instantiation result in the supplied {promise}.
class InstantiateModuleResultResolver
    : public i::wasm::InstantiationResultResolver {
 public:
290 291 292 293 294 295 296
  InstantiateModuleResultResolver(Isolate* isolate, Local<Context> context,
                                  Local<Promise::Resolver> promise_resolver)
      : isolate_(isolate),
        context_(isolate, context),
        promise_resolver_(isolate, promise_resolver) {
    context_.SetWeak();
    promise_resolver_.AnnotateStrongRetainer(kGlobalPromiseHandle);
297 298 299 300
  }

  void OnInstantiationSucceeded(
      i::Handle<i::WasmInstanceObject> instance) override {
301 302 303 304 305 306 307
    if (context_.IsEmpty()) return;
    auto callback = reinterpret_cast<i::Isolate*>(isolate_)
                        ->wasm_async_resolve_promise_callback();
    CHECK(callback);
    callback(isolate_, context_.Get(isolate_), promise_resolver_.Get(isolate_),
             Utils::ToLocal(i::Handle<i::Object>::cast(instance)),
             WasmAsyncSuccess::kSuccess);
308 309 310
  }

  void OnInstantiationFailed(i::Handle<i::Object> error_reason) override {
311 312 313 314 315 316
    if (context_.IsEmpty()) return;
    auto callback = reinterpret_cast<i::Isolate*>(isolate_)
                        ->wasm_async_resolve_promise_callback();
    CHECK(callback);
    callback(isolate_, context_.Get(isolate_), promise_resolver_.Get(isolate_),
             Utils::ToLocal(error_reason), WasmAsyncSuccess::kFail);
317 318 319
  }

 private:
320 321
  static constexpr char kGlobalPromiseHandle[] =
      "InstantiateModuleResultResolver::promise_";
322 323 324
  Isolate* isolate_;
  Global<Context> context_;
  Global<Promise::Resolver> promise_resolver_;
325 326
};

327 328
constexpr char InstantiateModuleResultResolver::kGlobalPromiseHandle[];

329 330 331 332 333 334
// This class resolves the result of WebAssembly.instantiate(bytes, imports).
// For that it creates a new {JSObject} which contains both the provided
// {WasmModuleObject} and the resulting {WebAssemblyInstanceObject} itself.
class InstantiateBytesResultResolver
    : public i::wasm::InstantiationResultResolver {
 public:
335 336 337
  InstantiateBytesResultResolver(Isolate* isolate, Local<Context> context,
                                 Local<Promise::Resolver> promise_resolver,
                                 Local<Value> module)
338
      : isolate_(isolate),
339 340 341 342 343 344
        context_(isolate, context),
        promise_resolver_(isolate, promise_resolver),
        module_(isolate, module) {
    context_.SetWeak();
    promise_resolver_.AnnotateStrongRetainer(kGlobalPromiseHandle);
    module_.AnnotateStrongRetainer(kGlobalModuleHandle);
345 346 347 348
  }

  void OnInstantiationSucceeded(
      i::Handle<i::WasmInstanceObject> instance) override {
349 350 351
    if (context_.IsEmpty()) return;
    Local<Context> context = context_.Get(isolate_);

352 353
    // The result is a JSObject with 2 fields which contain the
    // WasmInstanceObject and the WasmModuleObject.
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
    Local<Object> result = Object::New(isolate_);
    result
        ->CreateDataProperty(context, v8_str(isolate_, "module"),
                             module_.Get(isolate_))
        .Check();
    result
        ->CreateDataProperty(
            context, v8_str(isolate_, "instance"),
            Utils::ToLocal(i::Handle<i::Object>::cast(instance)))
        .Check();

    auto callback = reinterpret_cast<i::Isolate*>(isolate_)
                        ->wasm_async_resolve_promise_callback();
    CHECK(callback);
    callback(isolate_, context, promise_resolver_.Get(isolate_), result,
             WasmAsyncSuccess::kSuccess);
370 371 372
  }

  void OnInstantiationFailed(i::Handle<i::Object> error_reason) override {
373 374 375 376 377 378
    if (context_.IsEmpty()) return;
    auto callback = reinterpret_cast<i::Isolate*>(isolate_)
                        ->wasm_async_resolve_promise_callback();
    CHECK(callback);
    callback(isolate_, context_.Get(isolate_), promise_resolver_.Get(isolate_),
             Utils::ToLocal(error_reason), WasmAsyncSuccess::kFail);
379 380 381
  }

 private:
382 383 384 385
  static constexpr char kGlobalPromiseHandle[] =
      "InstantiateBytesResultResolver::promise_";
  static constexpr char kGlobalModuleHandle[] =
      "InstantiateBytesResultResolver::module_";
386 387 388 389
  Isolate* isolate_;
  Global<Context> context_;
  Global<Promise::Resolver> promise_resolver_;
  Global<Value> module_;
390 391
};

392 393 394
constexpr char InstantiateBytesResultResolver::kGlobalPromiseHandle[];
constexpr char InstantiateBytesResultResolver::kGlobalModuleHandle[];

395 396 397 398 399 400 401
// This class is the {CompilationResultResolver} for
// WebAssembly.instantiate(bytes, imports). When compilation finishes,
// {AsyncInstantiate} is started on the compilation result.
class AsyncInstantiateCompileResultResolver
    : public i::wasm::CompilationResultResolver {
 public:
  AsyncInstantiateCompileResultResolver(
402 403
      Isolate* isolate, Local<Context> context,
      Local<Promise::Resolver> promise_resolver, Local<Value> imports)
404
      : isolate_(isolate),
405 406 407 408 409 410
        context_(isolate, context),
        promise_resolver_(isolate, promise_resolver),
        imports_(isolate, imports) {
    context_.SetWeak();
    promise_resolver_.AnnotateStrongRetainer(kGlobalPromiseHandle);
    imports_.AnnotateStrongRetainer(kGlobalImportsHandle);
411 412 413
  }

  void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> result) override {
414 415
    if (finished_) return;
    finished_ = true;
416
    i::wasm::GetWasmEngine()->AsyncInstantiate(
417 418 419 420 421
        reinterpret_cast<i::Isolate*>(isolate_),
        std::make_unique<InstantiateBytesResultResolver>(
            isolate_, context_.Get(isolate_), promise_resolver_.Get(isolate_),
            Utils::ToLocal(i::Handle<i::Object>::cast(result))),
        result, ImportsAsMaybeReceiver(imports_.Get(isolate_)));
422 423 424
  }

  void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
425 426
    if (finished_) return;
    finished_ = true;
427 428 429 430 431 432
    if (context_.IsEmpty()) return;
    auto callback = reinterpret_cast<i::Isolate*>(isolate_)
                        ->wasm_async_resolve_promise_callback();
    CHECK(callback);
    callback(isolate_, context_.Get(isolate_), promise_resolver_.Get(isolate_),
             Utils::ToLocal(error_reason), WasmAsyncSuccess::kFail);
433 434 435
  }

 private:
436 437 438 439
  static constexpr char kGlobalPromiseHandle[] =
      "AsyncInstantiateCompileResultResolver::promise_";
  static constexpr char kGlobalImportsHandle[] =
      "AsyncInstantiateCompileResultResolver::module_";
440
  bool finished_ = false;
441 442 443 444
  Isolate* isolate_;
  Global<Context> context_;
  Global<Promise::Resolver> promise_resolver_;
  Global<Value> imports_;
445 446
};

447 448
constexpr char AsyncInstantiateCompileResultResolver::kGlobalPromiseHandle[];
constexpr char AsyncInstantiateCompileResultResolver::kGlobalImportsHandle[];
449 450 451 452 453 454

std::string ToString(const char* name) { return std::string(name); }

std::string ToString(const i::Handle<i::String> name) {
  return std::string("Property '") + name->ToCString().get() + "'";
}
455

456 457 458
// Web IDL: '[EnforceRange] unsigned long'
// Previously called ToNonWrappingUint32 in the draft WebAssembly JS spec.
// https://heycam.github.io/webidl/#EnforceRange
459 460 461
template <typename T>
bool EnforceUint32(T argument_name, Local<v8::Value> v, Local<Context> context,
                   ErrorThrower* thrower, uint32_t* res) {
462 463 464
  double double_number;

  if (!v->NumberValue(context).To(&double_number)) {
465 466
    thrower->TypeError("%s must be convertible to a number",
                       ToString(argument_name).c_str());
467 468 469
    return false;
  }
  if (!std::isfinite(double_number)) {
470 471
    thrower->TypeError("%s must be convertible to a valid number",
                       ToString(argument_name).c_str());
472 473 474
    return false;
  }
  if (double_number < 0) {
475 476
    thrower->TypeError("%s must be non-negative",
                       ToString(argument_name).c_str());
477 478 479
    return false;
  }
  if (double_number > std::numeric_limits<uint32_t>::max()) {
480 481
    thrower->TypeError("%s must be in the unsigned long range",
                       ToString(argument_name).c_str());
482 483 484 485 486 487
    return false;
  }

  *res = static_cast<uint32_t>(double_number);
  return true;
}
488
}  // namespace
489

490
// WebAssembly.compile(bytes) -> Promise
rossberg's avatar
rossberg committed
491
void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) {
492
  constexpr const char* kAPIMethodName = "WebAssembly.compile()";
rossberg's avatar
rossberg committed
493
  v8::Isolate* isolate = args.GetIsolate();
494
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
495

rossberg's avatar
rossberg committed
496
  HandleScope scope(isolate);
497
  ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
rossberg's avatar
rossberg committed
498

499 500 501 502 503
  i::Handle<i::Context> native_context = i_isolate->native_context();
  if (!i::wasm::IsWasmCodegenAllowed(i_isolate, native_context)) {
    i::Handle<i::String> error =
        i::wasm::ErrorStringForCodegen(i_isolate, native_context);
    thrower.CompileError("%s", error->ToCString().get());
504 505
  }

506
  Local<Context> context = isolate->GetCurrentContext();
507 508
  ASSIGN(Promise::Resolver, promise_resolver, Promise::Resolver::New(context));
  Local<Promise> promise = promise_resolver->GetPromise();
509
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
510 511
  return_value.Set(promise);

512
  std::shared_ptr<i::wasm::CompilationResultResolver> resolver(
513
      new AsyncCompilationResolver(isolate, context, promise_resolver));
514

515 516
  bool is_shared = false;
  auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
517
  if (thrower.error()) {
518
    resolver->OnCompilationFailed(thrower.Reify());
519
    return;
520
  }
521
  // Asynchronous compilation handles copying wire bytes if necessary.
522
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
523
  i::wasm::GetWasmEngine()->AsyncCompile(i_isolate, enabled_features,
524 525
                                         std::move(resolver), bytes, is_shared,
                                         kAPIMethodName);
rossberg's avatar
rossberg committed
526 527
}

528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
void WasmStreamingCallbackForTesting(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);

  HandleScope scope(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.compile()");

  std::shared_ptr<v8::WasmStreaming> streaming =
      v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());

  bool is_shared = false;
  i::wasm::ModuleWireBytes bytes =
      GetFirstArgumentAsBytes(args, &thrower, &is_shared);
  if (thrower.error()) {
    streaming->Abort(Utils::ToLocal(thrower.Reify()));
    return;
  }
  streaming->OnBytesReceived(bytes.start(), bytes.length());
  streaming->Finish();
  CHECK(!thrower.error());
}

void WasmStreamingPromiseFailedCallback(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  std::shared_ptr<v8::WasmStreaming> streaming =
      v8::WasmStreaming::Unpack(args.GetIsolate(), args.Data());
  streaming->Abort(args[0]);
}

558 559
// WebAssembly.compileStreaming(Response | Promise<Response>)
//   -> Promise<WebAssembly.Module>
560 561 562 563 564
void WebAssemblyCompileStreaming(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
565 566
  const char* const kAPIMethodName = "WebAssembly.compileStreaming()";
  ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
567 568 569
  Local<Context> context = isolate->GetCurrentContext();

  // Create and assign the return value of this function.
570 571
  ASSIGN(Promise::Resolver, promise_resolver, Promise::Resolver::New(context));
  Local<Promise> promise = promise_resolver->GetPromise();
572 573 574 575
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(promise);

  // Prepare the CompilationResultResolver for the compilation.
576 577
  auto resolver = std::make_shared<AsyncCompilationResolver>(isolate, context,
                                                             promise_resolver);
578

579 580 581 582 583
  i::Handle<i::Context> native_context = i_isolate->native_context();
  if (!i::wasm::IsWasmCodegenAllowed(i_isolate, native_context)) {
    i::Handle<i::String> error =
        i::wasm::ErrorStringForCodegen(i_isolate, native_context);
    thrower.CompileError("%s", error->ToCString().get());
584 585 586 587 588 589 590 591 592
    resolver->OnCompilationFailed(thrower.Reify());
    return;
  }

  // Allocate the streaming decoder in a Managed so we can pass it to the
  // embedder.
  i::Handle<i::Managed<WasmStreaming>> data =
      i::Managed<WasmStreaming>::Allocate(
          i_isolate, 0,
593
          std::make_unique<WasmStreaming::WasmStreamingImpl>(
594
              isolate, kAPIMethodName, resolver));
595 596 597 598 599 600

  DCHECK_NOT_NULL(i_isolate->wasm_streaming_callback());
  ASSIGN(
      v8::Function, compile_callback,
      v8::Function::New(context, i_isolate->wasm_streaming_callback(),
                        Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
601 602 603 604
  ASSIGN(
      v8::Function, reject_callback,
      v8::Function::New(context, WasmStreamingPromiseFailedCallback,
                        Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
605 606 607 608 609 610 611 612 613 614 615 616 617

  // The parameter may be of type {Response} or of type {Promise<Response>}.
  // Treat either case of parameter as Promise.resolve(parameter)
  // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments

  // Ending with:
  //    return Promise.resolve(parameter).then(compile_callback);
  ASSIGN(Promise::Resolver, input_resolver, Promise::Resolver::New(context));
  if (!input_resolver->Resolve(context, args[0]).IsJust()) return;

  // We do not have any use of the result here. The {compile_callback} will
  // start streaming compilation, which will eventually resolve the promise we
  // set as result value.
618 619
  USE(input_resolver->GetPromise()->Then(context, compile_callback,
                                         reject_callback));
620 621
}

622
// WebAssembly.validate(bytes) -> bool
623 624
void WebAssemblyValidate(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
625
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
626
  HandleScope scope(isolate);
627
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.validate()");
628

629 630
  bool is_shared = false;
  auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
631 632

  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
633 634

  if (thrower.error()) {
635
    if (thrower.wasm_error()) thrower.Reset();  // Clear error.
636
    return_value.Set(v8::False(isolate));
637 638 639
    return;
  }

640
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
641 642 643 644
  bool validated = false;
  if (is_shared) {
    // Make a copy of the wire bytes to avoid concurrent modification.
    std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
645
    memcpy(copy.get(), bytes.start(), bytes.length());
646 647
    i::wasm::ModuleWireBytes bytes_copy(copy.get(),
                                        copy.get() + bytes.length());
648
    validated = i::wasm::GetWasmEngine()->SyncValidate(
649
        i_isolate, enabled_features, bytes_copy);
650 651
  } else {
    // The wire bytes are not shared, OK to use them directly.
652
    validated = i::wasm::GetWasmEngine()->SyncValidate(i_isolate,
653
                                                       enabled_features, bytes);
654
  }
655 656

  return_value.Set(Boolean::New(isolate, validated));
657 658
}

659 660 661 662 663 664 665
namespace {
bool TransferPrototype(i::Isolate* isolate, i::Handle<i::JSObject> destination,
                       i::Handle<i::JSReceiver> source) {
  i::MaybeHandle<i::HeapObject> maybe_prototype =
      i::JSObject::GetPrototype(isolate, source);
  i::Handle<i::HeapObject> prototype;
  if (maybe_prototype.ToHandle(&prototype)) {
666 667 668
    Maybe<bool> result = i::JSObject::SetPrototype(
        isolate, destination, prototype,
        /*from_javascript=*/false, internal::kThrowOnError);
669 670 671 672 673 674 675 676 677
    if (!result.FromJust()) {
      DCHECK(isolate->has_pending_exception());
      return false;
    }
  }
  return true;
}
}  // namespace

678
// new WebAssembly.Module(bytes) -> WebAssembly.Module
rossberg's avatar
rossberg committed
679 680
void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
681
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
682 683
  if (i_isolate->wasm_module_callback()(args)) return;

rossberg's avatar
rossberg committed
684
  HandleScope scope(isolate);
685
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module()");
rossberg's avatar
rossberg committed
686

687 688 689 690
  if (!args.IsConstructCall()) {
    thrower.TypeError("WebAssembly.Module must be invoked with 'new'");
    return;
  }
691 692 693 694 695
  i::Handle<i::Context> native_context = i_isolate->native_context();
  if (!i::wasm::IsWasmCodegenAllowed(i_isolate, native_context)) {
    i::Handle<i::String> error =
        i::wasm::ErrorStringForCodegen(i_isolate, native_context);
    thrower.CompileError("%s", error->ToCString().get());
696 697 698
    return;
  }

699 700
  bool is_shared = false;
  auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
701

702 703 704
  if (thrower.error()) {
    return;
  }
705
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
706
  i::MaybeHandle<i::WasmModuleObject> maybe_module_obj;
707 708 709
  if (is_shared) {
    // Make a copy of the wire bytes to avoid concurrent modification.
    std::unique_ptr<uint8_t[]> copy(new uint8_t[bytes.length()]);
710
    memcpy(copy.get(), bytes.start(), bytes.length());
711 712
    i::wasm::ModuleWireBytes bytes_copy(copy.get(),
                                        copy.get() + bytes.length());
713
    maybe_module_obj = i::wasm::GetWasmEngine()->SyncCompile(
714
        i_isolate, enabled_features, &thrower, bytes_copy);
715 716
  } else {
    // The wire bytes are not shared, OK to use them directly.
717
    maybe_module_obj = i::wasm::GetWasmEngine()->SyncCompile(
718
        i_isolate, enabled_features, &thrower, bytes);
719 720
  }

721 722 723 724 725 726 727 728 729 730 731 732 733 734
  i::Handle<i::WasmModuleObject> module_obj;
  if (!maybe_module_obj.ToHandle(&module_obj)) return;

  // The infrastructure for `new Foo` calls allocates an object, which is
  // available here as {args.This()}. We're going to discard this object
  // and use {module_obj} instead, but it does have the correct prototype,
  // which we must harvest from it. This makes a difference when the JS
  // constructor function wasn't {WebAssembly.Module} directly, but some
  // subclass: {module_obj} has {WebAssembly.Module}'s prototype at this
  // point, so we must overwrite that with the correct prototype for {Foo}.
  if (!TransferPrototype(i_isolate, module_obj,
                         Utils::OpenHandle(*args.This()))) {
    return;
  }
rossberg's avatar
rossberg committed
735 736

  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
737
  return_value.Set(Utils::ToLocal(i::Handle<i::JSObject>::cast(module_obj)));
rossberg's avatar
rossberg committed
738 739
}

740
// WebAssembly.Module.imports(module) -> Array<Import>
741 742 743 744
void WebAssemblyModuleImports(const v8::FunctionCallbackInfo<v8::Value>& args) {
  HandleScope scope(args.GetIsolate());
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
745
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.imports()");
746

747
  auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
748 749 750
  if (thrower.error()) return;
  auto imports = i::wasm::GetImports(i_isolate, maybe_module.ToHandleChecked());
  args.GetReturnValue().Set(Utils::ToLocal(imports));
751 752
}

753
// WebAssembly.Module.exports(module) -> Array<Export>
754 755 756 757
void WebAssemblyModuleExports(const v8::FunctionCallbackInfo<v8::Value>& args) {
  HandleScope scope(args.GetIsolate());
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
758
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Module.exports()");
759

760
  auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
761 762 763
  if (thrower.error()) return;
  auto exports = i::wasm::GetExports(i_isolate, maybe_module.ToHandleChecked());
  args.GetReturnValue().Set(Utils::ToLocal(exports));
764
}
765

766
// WebAssembly.Module.customSections(module, name) -> Array<Section>
767 768 769 770 771
void WebAssemblyModuleCustomSections(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  HandleScope scope(args.GetIsolate());
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
772 773
  ScheduledErrorThrower thrower(i_isolate,
                                "WebAssembly.Module.customSections()");
774

775
  auto maybe_module = GetFirstArgumentAsModule(args, &thrower);
776
  if (thrower.error()) return;
777

778 779 780 781 782
  if (args[1]->IsUndefined()) {
    thrower.TypeError("Argument 1 is required");
    return;
  }

783 784 785 786
  i::MaybeHandle<i::Object> maybe_name =
      i::Object::ToString(i_isolate, Utils::OpenHandle(*args[1]));
  i::Handle<i::Object> name;
  if (!maybe_name.ToHandle(&name)) return;
787 788 789 790 791
  auto custom_sections =
      i::wasm::GetCustomSections(i_isolate, maybe_module.ToHandleChecked(),
                                 i::Handle<i::String>::cast(name), &thrower);
  if (thrower.error()) return;
  args.GetReturnValue().Set(Utils::ToLocal(custom_sections));
792 793
}

794
// new WebAssembly.Instance(module, imports) -> WebAssembly.Instance
795
void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) {
796
  Isolate* isolate = args.GetIsolate();
797 798 799
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  i_isolate->CountUsage(
      v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
800

rossberg's avatar
rossberg committed
801
  HandleScope scope(args.GetIsolate());
802 803
  if (i_isolate->wasm_instance_callback()(args)) return;

804 805 806
  i::MaybeHandle<i::JSObject> maybe_instance_obj;
  {
    ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance()");
807 808
    if (!args.IsConstructCall()) {
      thrower.TypeError("WebAssembly.Instance must be invoked with 'new'");
809 810 811
      return;
    }

812 813 814 815 816 817
    i::MaybeHandle<i::WasmModuleObject> maybe_module =
        GetFirstArgumentAsModule(args, &thrower);
    if (thrower.error()) return;

    i::Handle<i::WasmModuleObject> module_obj = maybe_module.ToHandleChecked();

818 819 820 821 822 823
    Local<Value> ffi = args[1];

    if (!ffi->IsUndefined() && !ffi->IsObject()) {
      thrower.TypeError("Argument 1 must be an object");
      return;
    }
824 825 826
    if (thrower.error()) return;

    maybe_instance_obj = i::wasm::GetWasmEngine()->SyncInstantiate(
827
        i_isolate, &thrower, module_obj, ImportsAsMaybeReceiver(ffi),
828
        i::MaybeHandle<i::JSArrayBuffer>());
829
  }
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849

  i::Handle<i::JSObject> instance_obj;
  if (!maybe_instance_obj.ToHandle(&instance_obj)) {
    DCHECK(i_isolate->has_scheduled_exception());
    return;
  }

  // The infrastructure for `new Foo` calls allocates an object, which is
  // available here as {args.This()}. We're going to discard this object
  // and use {instance_obj} instead, but it does have the correct prototype,
  // which we must harvest from it. This makes a difference when the JS
  // constructor function wasn't {WebAssembly.Instance} directly, but some
  // subclass: {instance_obj} has {WebAssembly.Instance}'s prototype at this
  // point, so we must overwrite that with the correct prototype for {Foo}.
  if (!TransferPrototype(i_isolate, instance_obj,
                         Utils::OpenHandle(*args.This()))) {
    return;
  }

  args.GetReturnValue().Set(Utils::ToLocal(instance_obj));
850 851
}

852 853 854
// WebAssembly.instantiateStreaming(Response | Promise<Response> [, imports])
//   -> Promise<ResultObject>
// (where ResultObject has a "module" and an "instance" field)
855 856 857 858
void WebAssemblyInstantiateStreaming(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
859 860
  i_isolate->CountUsage(
      v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
861

862
  HandleScope scope(isolate);
863
  Local<Context> context = isolate->GetCurrentContext();
864 865
  const char* const kAPIMethodName = "WebAssembly.instantiateStreaming()";
  ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
866

867 868 869 870 871
  // Create and assign the return value of this function.
  ASSIGN(Promise::Resolver, result_resolver, Promise::Resolver::New(context));
  Local<Promise> promise = result_resolver->GetPromise();
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(promise);
872

873 874 875
  // Create an InstantiateResultResolver in case there is an issue with the
  // passed parameters.
  std::unique_ptr<i::wasm::InstantiationResultResolver> resolver(
876
      new InstantiateModuleResultResolver(isolate, context, result_resolver));
877

878 879 880 881 882
  i::Handle<i::Context> native_context = i_isolate->native_context();
  if (!i::wasm::IsWasmCodegenAllowed(i_isolate, native_context)) {
    i::Handle<i::String> error =
        i::wasm::ErrorStringForCodegen(i_isolate, native_context);
    thrower.CompileError("%s", error->ToCString().get());
883 884 885 886 887 888 889
    resolver->OnInstantiationFailed(thrower.Reify());
    return;
  }

  // If args.Length < 2, this will be undefined - see FunctionCallbackInfo.
  Local<Value> ffi = args[1];

890 891
  if (!ffi->IsUndefined() && !ffi->IsObject()) {
    thrower.TypeError("Argument 1 must be an object");
892 893 894 895 896 897 898 899 900
    resolver->OnInstantiationFailed(thrower.Reify());
    return;
  }

  // We start compilation now, we have no use for the
  // {InstantiationResultResolver}.
  resolver.reset();

  std::shared_ptr<i::wasm::CompilationResultResolver> compilation_resolver(
901 902
      new AsyncInstantiateCompileResultResolver(isolate, context,
                                                result_resolver, ffi));
903 904 905 906 907 908

  // Allocate the streaming decoder in a Managed so we can pass it to the
  // embedder.
  i::Handle<i::Managed<WasmStreaming>> data =
      i::Managed<WasmStreaming>::Allocate(
          i_isolate, 0,
909
          std::make_unique<WasmStreaming::WasmStreamingImpl>(
910
              isolate, kAPIMethodName, compilation_resolver));
911 912 913 914 915 916

  DCHECK_NOT_NULL(i_isolate->wasm_streaming_callback());
  ASSIGN(
      v8::Function, compile_callback,
      v8::Function::New(context, i_isolate->wasm_streaming_callback(),
                        Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
917 918 919 920
  ASSIGN(
      v8::Function, reject_callback,
      v8::Function::New(context, WasmStreamingPromiseFailedCallback,
                        Utils::ToLocal(i::Handle<i::Object>::cast(data)), 1));
921 922 923 924 925 926 927 928 929 930 931 932 933

  // The parameter may be of type {Response} or of type {Promise<Response>}.
  // Treat either case of parameter as Promise.resolve(parameter)
  // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments

  // Ending with:
  //    return Promise.resolve(parameter).then(compile_callback);
  ASSIGN(Promise::Resolver, input_resolver, Promise::Resolver::New(context));
  if (!input_resolver->Resolve(context, args[0]).IsJust()) return;

  // We do not have any use of the result here. The {compile_callback} will
  // start streaming compilation, which will eventually resolve the promise we
  // set as result value.
934 935
  USE(input_resolver->GetPromise()->Then(context, compile_callback,
                                         reject_callback));
936 937
}

938 939 940
// WebAssembly.instantiate(module, imports) -> WebAssembly.Instance
// WebAssembly.instantiate(bytes, imports) ->
//     {module: WebAssembly.Module, instance: WebAssembly.Instance}
941
void WebAssemblyInstantiate(const v8::FunctionCallbackInfo<v8::Value>& args) {
942
  constexpr const char* kAPIMethodName = "WebAssembly.instantiate()";
943 944
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
945 946
  i_isolate->CountUsage(
      v8::Isolate::UseCounterFeature::kWebAssemblyInstantiation);
947

948
  ScheduledErrorThrower thrower(i_isolate, kAPIMethodName);
949 950 951

  HandleScope scope(isolate);

952
  Local<Context> context = isolate->GetCurrentContext();
953

954 955
  ASSIGN(Promise::Resolver, promise_resolver, Promise::Resolver::New(context));
  Local<Promise> promise = promise_resolver->GetPromise();
956
  args.GetReturnValue().Set(promise);
957

958
  std::unique_ptr<i::wasm::InstantiationResultResolver> resolver(
959
      new InstantiateModuleResultResolver(isolate, context, promise_resolver));
960

961 962
  Local<Value> first_arg_value = args[0];
  i::Handle<i::Object> first_arg = Utils::OpenHandle(*first_arg_value);
963 964 965
  if (!first_arg->IsJSObject()) {
    thrower.TypeError(
        "Argument 0 must be a buffer source or a WebAssembly.Module object");
966 967 968 969 970 971 972
    resolver->OnInstantiationFailed(thrower.Reify());
    return;
  }

  // If args.Length < 2, this will be undefined - see FunctionCallbackInfo.
  Local<Value> ffi = args[1];

973 974
  if (!ffi->IsUndefined() && !ffi->IsObject()) {
    thrower.TypeError("Argument 1 must be an object");
975
    resolver->OnInstantiationFailed(thrower.Reify());
976 977
    return;
  }
978

979
  if (first_arg->IsWasmModuleObject()) {
980 981 982
    i::Handle<i::WasmModuleObject> module_obj =
        i::Handle<i::WasmModuleObject>::cast(first_arg);

983
    i::wasm::GetWasmEngine()->AsyncInstantiate(i_isolate, std::move(resolver),
984 985
                                               module_obj,
                                               ImportsAsMaybeReceiver(ffi));
986 987
    return;
  }
988

989 990 991 992
  bool is_shared = false;
  auto bytes = GetFirstArgumentAsBytes(args, &thrower, &is_shared);
  if (thrower.error()) {
    resolver->OnInstantiationFailed(thrower.Reify());
993
    return;
994
  }
995

996 997 998 999
  // We start compilation now, we have no use for the
  // {InstantiationResultResolver}.
  resolver.reset();

1000
  std::shared_ptr<i::wasm::CompilationResultResolver> compilation_resolver(
1001 1002
      new AsyncInstantiateCompileResultResolver(isolate, context,
                                                promise_resolver, ffi));
1003 1004 1005

  // The first parameter is a buffer source, we have to check if we are allowed
  // to compile it.
1006 1007 1008 1009 1010
  i::Handle<i::Context> native_context = i_isolate->native_context();
  if (!i::wasm::IsWasmCodegenAllowed(i_isolate, native_context)) {
    i::Handle<i::String> error =
        i::wasm::ErrorStringForCodegen(i_isolate, native_context);
    thrower.CompileError("%s", error->ToCString().get());
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 1022

bool GetIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
1023 1024
                        Local<Context> context, v8::Local<v8::Value> value,
                        i::Handle<i::String> property_name, int64_t* result,
1025
                        int64_t lower_bound, uint64_t upper_bound) {
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
  uint32_t number;
  if (!EnforceUint32(property_name, value, context, thrower, &number)) {
    return false;
  }
  if (number < lower_bound) {
    thrower->RangeError("Property '%s': value %" PRIu32
                        " is below the lower bound %" PRIx64,
                        property_name->ToCString().get(), number, lower_bound);
    return false;
  }
  if (number > upper_bound) {
    thrower->RangeError("Property '%s': value %" PRIu32
                        " is above the upper bound %" PRIu64,
                        property_name->ToCString().get(), number, upper_bound);
    return false;
  }

  *result = static_cast<int64_t>(number);
  return true;
}

bool GetOptionalIntegerProperty(v8::Isolate* isolate, ErrorThrower* thrower,
                                Local<Context> context,
                                Local<v8::Object> object,
1050 1051 1052
                                Local<String> property, bool* has_property,
                                int64_t* result, int64_t lower_bound,
                                uint64_t upper_bound) {
1053 1054 1055 1056 1057 1058 1059 1060
  v8::Local<v8::Value> value;
  if (!object->Get(context, property).ToLocal(&value)) {
    return false;
  }

  // Web IDL: dictionary presence
  // https://heycam.github.io/webidl/#dfn-present
  if (value->IsUndefined()) {
1061
    if (has_property != nullptr) *has_property = false;
1062 1063
    return true;
  }
1064

1065
  if (has_property != nullptr) *has_property = true;
1066 1067 1068 1069
  i::Handle<i::String> property_name = v8::Utils::OpenHandle(*property);

  return GetIntegerProperty(isolate, thrower, context, value, property_name,
                            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 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
// TODO(aseemgarg): change behavior when the following bug is resolved:
// https://github.com/WebAssembly/js-types/issues/6
bool GetInitialOrMinimumProperty(v8::Isolate* isolate, ErrorThrower* thrower,
                                 Local<Context> context,
                                 Local<v8::Object> object, int64_t* result,
                                 int64_t lower_bound, uint64_t upper_bound) {
  bool has_initial = false;
  if (!GetOptionalIntegerProperty(isolate, thrower, context, object,
                                  v8_str(isolate, "initial"), &has_initial,
                                  result, lower_bound, upper_bound)) {
    return false;
  }
1086 1087 1088 1089 1090
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(
      reinterpret_cast<i::Isolate*>(isolate));
  if (enabled_features.has_type_reflection()) {
    bool has_minimum = false;
    int64_t minimum = 0;
1091
    if (!GetOptionalIntegerProperty(isolate, thrower, context, object,
1092 1093
                                    v8_str(isolate, "minimum"), &has_minimum,
                                    &minimum, lower_bound, upper_bound)) {
1094 1095
      return false;
    }
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
    if (has_initial && has_minimum) {
      thrower->TypeError(
          "The properties 'initial' and 'minimum' are not allowed at the same "
          "time");
      return false;
    }
    if (has_minimum) {
      // Only {minimum} exists, so we use {minimum} as {initial}.
      has_initial = true;
      *result = minimum;
    }
1107 1108 1109 1110 1111 1112 1113 1114 1115
  }
  if (!has_initial) {
    // TODO(aseemgarg): update error message when the spec issue is resolved.
    thrower->TypeError("Property 'initial' is required");
    return false;
  }
  return true;
}

1116 1117 1118 1119
namespace {
i::Handle<i::Object> DefaultReferenceValue(i::Isolate* isolate,
                                           i::wasm::ValueType type) {
  if (type.is_reference()) {
1120 1121 1122 1123 1124 1125
    // Use undefined for JS type (externref) but null for wasm types as wasm
    // does not know undefined.
    if (type.heap_representation() == i::wasm::HeapType::kExtern) {
      return isolate->factory()->undefined_value();
    }
    return isolate->factory()->null_value();
1126 1127 1128 1129 1130
  }
  UNREACHABLE();
}
}  // namespace

1131
// new WebAssembly.Table(args) -> WebAssembly.Table
1132 1133
void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
1134
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1135
  HandleScope scope(isolate);
1136
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table()");
1137 1138 1139 1140
  if (!args.IsConstructCall()) {
    thrower.TypeError("WebAssembly.Table must be invoked with 'new'");
    return;
  }
1141
  if (!args[0]->IsObject()) {
1142 1143 1144 1145
    thrower.TypeError("Argument 0 must be a table descriptor");
    return;
  }
  Local<Context> context = isolate->GetCurrentContext();
1146
  Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1147
  i::wasm::ValueType type;
1148 1149 1150 1151 1152 1153 1154 1155
  // The descriptor's 'element'.
  {
    v8::MaybeLocal<v8::Value> maybe =
        descriptor->Get(context, v8_str(isolate, "element"));
    v8::Local<v8::Value> value;
    if (!maybe.ToLocal(&value)) return;
    v8::Local<v8::String> string;
    if (!value->ToString(context).ToLocal(&string)) return;
1156
    auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1157
    // The JS api uses 'anyfunc' instead of 'funcref'.
1158
    if (string->StringEquals(v8_str(isolate, "anyfunc"))) {
1159
      type = i::wasm::kWasmFuncRef;
1160 1161 1162 1163 1164
    } else if (enabled_features.has_type_reflection() &&
               string->StringEquals(v8_str(isolate, "funcref"))) {
      // With the type reflection proposal, "funcref" replaces "anyfunc",
      // and anyfunc just becomes an alias for "funcref".
      type = i::wasm::kWasmFuncRef;
1165
    } else if (string->StringEquals(v8_str(isolate, "externref"))) {
1166
      type = i::wasm::kWasmExternRef;
1167 1168 1169
    } else if (enabled_features.has_stringref() &&
               string->StringEquals(v8_str(isolate, "stringref"))) {
      type = i::wasm::kWasmStringRef;
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
    } else if (enabled_features.has_gc() &&
               string->StringEquals(v8_str(isolate, "anyref"))) {
      type = i::wasm::kWasmAnyRef;
    } else if (enabled_features.has_gc() &&
               string->StringEquals(v8_str(isolate, "eqref"))) {
      type = i::wasm::kWasmEqRef;
    } else if (enabled_features.has_gc() &&
               string->StringEquals(v8_str(isolate, "dataref"))) {
      type = i::wasm::kWasmDataRef;
    } else if (enabled_features.has_gc() &&
               string->StringEquals(v8_str(isolate, "arrayref"))) {
      type = i::wasm::kWasmArrayRef;
1182
    } else {
1183
      // TODO(7748): Add "i31ref".
1184 1185
      thrower.TypeError(
          "Descriptor property 'element' must be a WebAssembly reference type");
1186 1187 1188
      return;
    }
  }
1189

1190
  int64_t initial = 0;
1191 1192 1193
  if (!GetInitialOrMinimumProperty(isolate, &thrower, context, descriptor,
                                   &initial, 0,
                                   i::wasm::max_table_init_entries())) {
1194 1195 1196
    return;
  }
  // The descriptor's 'maximum'.
1197
  int64_t maximum = -1;
1198
  bool has_maximum = true;
1199 1200 1201 1202
  if (!GetOptionalIntegerProperty(isolate, &thrower, context, descriptor,
                                  v8_str(isolate, "maximum"), &has_maximum,
                                  &maximum, initial,
                                  std::numeric_limits<uint32_t>::max())) {
1203
    return;
1204 1205
  }

1206
  i::Handle<i::FixedArray> fixed_array;
1207
  i::Handle<i::WasmTableObject> table_obj =
1208 1209
      i::WasmTableObject::New(i_isolate, i::Handle<i::WasmInstanceObject>(),
                              type, static_cast<uint32_t>(initial), has_maximum,
1210 1211
                              static_cast<uint32_t>(maximum), &fixed_array,
                              DefaultReferenceValue(i_isolate, type));
1212

1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224
  // The infrastructure for `new Foo` calls allocates an object, which is
  // available here as {args.This()}. We're going to discard this object
  // and use {table_obj} instead, but it does have the correct prototype,
  // which we must harvest from it. This makes a difference when the JS
  // constructor function wasn't {WebAssembly.Table} directly, but some
  // subclass: {table_obj} has {WebAssembly.Table}'s prototype at this
  // point, so we must overwrite that with the correct prototype for {Foo}.
  if (!TransferPrototype(i_isolate, table_obj,
                         Utils::OpenHandle(*args.This()))) {
    return;
  }

1225 1226
  if (initial > 0 && args.Length() >= 2 && !args[1]->IsUndefined()) {
    i::Handle<i::Object> element = Utils::OpenHandle(*args[1]);
1227
    if (!i::WasmTableObject::IsValidJSElement(i_isolate, table_obj, element)) {
1228 1229 1230 1231 1232 1233
      thrower.TypeError(
          "Argument 2 must be undefined, null, or a value of type compatible "
          "with the type of the new table.");
      return;
    }
    for (uint32_t index = 0; index < static_cast<uint32_t>(initial); ++index) {
1234 1235
      i::WasmTableObject::Set(i_isolate, table_obj, index, element,
                              i::WasmTableObject::kJS);
1236
    }
1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
  } else if (initial > 0) {
    switch (table_obj->type().heap_representation()) {
      case i::wasm::HeapType::kString:
        thrower.TypeError(
            "Missing initial value when creating stringref table");
        return;
      case i::wasm::HeapType::kStringViewWtf8:
        thrower.TypeError("stringview_wtf8 has no JS representation");
        return;
      case i::wasm::HeapType::kStringViewWtf16:
        thrower.TypeError("stringview_wtf16 has no JS representation");
        return;
      case i::wasm::HeapType::kStringViewIter:
        thrower.TypeError("stringview_iter has no JS representation");
        return;
      default:
        break;
    }
1255
  }
1256
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
1257
  return_value.Set(Utils::ToLocal(i::Handle<i::JSObject>::cast(table_obj)));
1258
}
1259

1260 1261
void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
1262
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
1263
  HandleScope scope(isolate);
1264
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory()");
1265 1266 1267 1268
  if (!args.IsConstructCall()) {
    thrower.TypeError("WebAssembly.Memory must be invoked with 'new'");
    return;
  }
1269
  if (!args[0]->IsObject()) {
1270
    thrower.TypeError("Argument 0 must be a memory descriptor");
1271 1272 1273
    return;
  }
  Local<Context> context = isolate->GetCurrentContext();
1274
  Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1275

1276 1277
  // TODO(clemensb): The JS API spec is not updated for memory64 yet; fix this
  // code once it is.
1278
  int64_t initial = 0;
1279
  if (!GetInitialOrMinimumProperty(isolate, &thrower, context, descriptor,
1280 1281
                                   &initial, 0,
                                   i::wasm::kSpecMaxMemory32Pages)) {
1282 1283
    return;
  }
1284
  // The descriptor's 'maximum'.
1285
  int64_t maximum = i::WasmMemoryObject::kNoMaximum;
1286
  if (!GetOptionalIntegerProperty(isolate, &thrower, context, descriptor,
1287
                                  v8_str(isolate, "maximum"), nullptr, &maximum,
1288
                                  initial, i::wasm::kSpecMaxMemory32Pages)) {
1289
    return;
1290
  }
1291

1292
  auto shared = i::SharedFlag::kNotShared;
1293 1294
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
  if (enabled_features.has_threads()) {
1295 1296
    // Shared property of descriptor
    Local<String> shared_key = v8_str(isolate, "shared");
1297 1298 1299 1300
    v8::MaybeLocal<v8::Value> maybe_value =
        descriptor->Get(context, shared_key);
    v8::Local<v8::Value> value;
    if (maybe_value.ToLocal(&value)) {
1301 1302
      shared = value->BooleanValue(isolate) ? i::SharedFlag::kShared
                                            : i::SharedFlag::kNotShared;
1303 1304 1305
    } else {
      DCHECK(i_isolate->has_scheduled_exception());
      return;
1306
    }
1307

1308
    // Throw TypeError if shared is true, and the descriptor has no "maximum"
1309
    if (shared == i::SharedFlag::kShared && maximum == -1) {
1310 1311
      thrower.TypeError(
          "If shared is true, maximum property should be defined.");
1312
      return;
1313 1314 1315
    }
  }

1316
  i::Handle<i::JSObject> memory_obj;
1317 1318
  if (!i::WasmMemoryObject::New(i_isolate, static_cast<int>(initial),
                                static_cast<int>(maximum), shared)
1319
           .ToHandle(&memory_obj)) {
1320 1321 1322
    thrower.RangeError("could not allocate memory");
    return;
  }
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335

  // The infrastructure for `new Foo` calls allocates an object, which is
  // available here as {args.This()}. We're going to discard this object
  // and use {memory_obj} instead, but it does have the correct prototype,
  // which we must harvest from it. This makes a difference when the JS
  // constructor function wasn't {WebAssembly.Memory} directly, but some
  // subclass: {memory_obj} has {WebAssembly.Memory}'s prototype at this
  // point, so we must overwrite that with the correct prototype for {Foo}.
  if (!TransferPrototype(i_isolate, memory_obj,
                         Utils::OpenHandle(*args.This()))) {
    return;
  }

1336
  if (shared == i::SharedFlag::kShared) {
1337 1338 1339
    i::Handle<i::JSArrayBuffer> buffer(
        i::Handle<i::WasmMemoryObject>::cast(memory_obj)->array_buffer(),
        i_isolate);
1340
    Maybe<bool> result =
1341
        buffer->SetIntegrityLevel(buffer, i::FROZEN, i::kDontThrow);
1342 1343 1344
    if (!result.FromJust()) {
      thrower.TypeError(
          "Status of setting SetIntegrityLevel of buffer is false.");
1345
      return;
1346 1347
    }
  }
1348
  args.GetReturnValue().Set(Utils::ToLocal(memory_obj));
1349
}
1350

1351 1352
// Determines the type encoded in a value type property (e.g. type reflection).
// Returns false if there was an exception, true upon success. On success the
1353
// outgoing {type} is set accordingly, or set to {wasm::kWasmVoid} in case the
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369
// type could not be properly recognized.
bool GetValueType(Isolate* isolate, MaybeLocal<Value> maybe,
                  Local<Context> context, i::wasm::ValueType* type,
                  i::wasm::WasmFeatures enabled_features) {
  v8::Local<v8::Value> value;
  if (!maybe.ToLocal(&value)) return false;
  v8::Local<v8::String> string;
  if (!value->ToString(context).ToLocal(&string)) return false;
  if (string->StringEquals(v8_str(isolate, "i32"))) {
    *type = i::wasm::kWasmI32;
  } else if (string->StringEquals(v8_str(isolate, "f32"))) {
    *type = i::wasm::kWasmF32;
  } else if (string->StringEquals(v8_str(isolate, "i64"))) {
    *type = i::wasm::kWasmI64;
  } else if (string->StringEquals(v8_str(isolate, "f64"))) {
    *type = i::wasm::kWasmF64;
1370
  } else if (string->StringEquals(v8_str(isolate, "externref"))) {
1371
    *type = i::wasm::kWasmExternRef;
1372 1373 1374 1375 1376
  } else if (enabled_features.has_type_reflection() &&
             string->StringEquals(v8_str(isolate, "funcref"))) {
    // The type reflection proposal renames "anyfunc" to "funcref", and makes
    // "anyfunc" an alias of "funcref".
    *type = i::wasm::kWasmFuncRef;
1377
  } else if (string->StringEquals(v8_str(isolate, "anyfunc"))) {
1378
    // The JS api spec uses 'anyfunc' instead of 'funcref'.
1379
    *type = i::wasm::kWasmFuncRef;
1380 1381 1382
  } else if (enabled_features.has_gc() &&
             string->StringEquals(v8_str(isolate, "eqref"))) {
    *type = i::wasm::kWasmEqRef;
1383 1384 1385
  } else if (enabled_features.has_stringref() &&
             string->StringEquals(v8_str(isolate, "stringref"))) {
    *type = i::wasm::kWasmStringRef;
1386 1387 1388
  } else if (enabled_features.has_gc() &&
             string->StringEquals(v8_str(isolate, "anyref"))) {
    *type = i::wasm::kWasmAnyRef;
1389 1390 1391 1392 1393 1394
  } else if (enabled_features.has_gc() &&
             string->StringEquals(v8_str(isolate, "dataref"))) {
    *type = i::wasm::kWasmDataRef;
  } else if (enabled_features.has_gc() &&
             string->StringEquals(v8_str(isolate, "arrayref"))) {
    *type = i::wasm::kWasmArrayRef;
1395 1396
  } else {
    // Unrecognized type.
1397
    // TODO(7748): Add "i31ref".
1398
    *type = i::wasm::kWasmVoid;
1399 1400 1401 1402
  }
  return true;
}

1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442
namespace {

bool ToI32(Local<v8::Value> value, Local<Context> context, int32_t* i32_value) {
  if (!value->IsUndefined()) {
    v8::Local<v8::Int32> int32_value;
    if (!value->ToInt32(context).ToLocal(&int32_value)) return false;
    if (!int32_value->Int32Value(context).To(i32_value)) return false;
  }
  return true;
}

bool ToI64(Local<v8::Value> value, Local<Context> context, int64_t* i64_value) {
  if (!value->IsUndefined()) {
    v8::Local<v8::BigInt> bigint_value;
    if (!value->ToBigInt(context).ToLocal(&bigint_value)) return false;
    *i64_value = bigint_value->Int64Value();
  }
  return true;
}

bool ToF32(Local<v8::Value> value, Local<Context> context, float* f32_value) {
  if (!value->IsUndefined()) {
    double f64_value = 0;
    v8::Local<v8::Number> number_value;
    if (!value->ToNumber(context).ToLocal(&number_value)) return false;
    if (!number_value->NumberValue(context).To(&f64_value)) return false;
    *f32_value = i::DoubleToFloat32(f64_value);
  }
  return true;
}

bool ToF64(Local<v8::Value> value, Local<Context> context, double* f64_value) {
  if (!value->IsUndefined()) {
    v8::Local<v8::Number> number_value;
    if (!value->ToNumber(context).ToLocal(&number_value)) return false;
    if (!number_value->NumberValue(context).To(f64_value)) return false;
  }
  return true;
}

1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
bool checkAndUnpackAnyRef(i::Isolate* i_isolate,
                          i::Handle<i::Object>& in_out_value,
                          i::wasm::ValueType type, ErrorThrower& thrower) {
  const char* error_message = nullptr;
  auto module = nullptr;
  bool valid = internal::wasm::TypecheckJSObject(
      i_isolate, module, in_out_value, type, &error_message);
  if (!valid) {
    DCHECK(error_message != nullptr);
    thrower.TypeError("%s", error_message);
    return false;
  }
  if (!internal::v8_flags.wasm_gc_js_interop) {
    internal::wasm::TryUnpackObjectWrapper(i_isolate, in_out_value);
  }
  return true;
}

1461 1462
}  // namespace

1463
// WebAssembly.Global
1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478
void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Global()");
  if (!args.IsConstructCall()) {
    thrower.TypeError("WebAssembly.Global must be invoked with 'new'");
    return;
  }
  if (!args[0]->IsObject()) {
    thrower.TypeError("Argument 0 must be a global descriptor");
    return;
  }
  Local<Context> context = isolate->GetCurrentContext();
  Local<v8::Object> descriptor = Local<Object>::Cast(args[0]);
1479
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1480 1481 1482 1483 1484 1485 1486 1487

  // The descriptor's 'mutable'.
  bool is_mutable = false;
  {
    Local<String> mutable_key = v8_str(isolate, "mutable");
    v8::MaybeLocal<v8::Value> maybe = descriptor->Get(context, mutable_key);
    v8::Local<v8::Value> value;
    if (maybe.ToLocal(&value)) {
1488
      is_mutable = value->BooleanValue(isolate);
1489 1490 1491
    } else {
      DCHECK(i_isolate->has_scheduled_exception());
      return;
1492 1493 1494
    }
  }

1495 1496 1497
  // The descriptor's type, called 'value'. It is called 'value' because this
  // descriptor is planned to be re-used as the global's type for reflection,
  // so calling it 'type' is redundant.
1498 1499 1500
  i::wasm::ValueType type;
  {
    v8::MaybeLocal<v8::Value> maybe =
1501
        descriptor->Get(context, v8_str(isolate, "value"));
1502
    if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
1503
    if (type == i::wasm::kWasmVoid) {
1504
      thrower.TypeError(
1505
          "Descriptor property 'value' must be a WebAssembly type");
1506 1507 1508 1509
      return;
    }
  }

1510 1511
  const uint32_t offset = 0;
  i::MaybeHandle<i::WasmGlobalObject> maybe_global_obj =
1512 1513
      i::WasmGlobalObject::New(i_isolate, i::Handle<i::WasmInstanceObject>(),
                               i::MaybeHandle<i::JSArrayBuffer>(),
1514 1515
                               i::MaybeHandle<i::FixedArray>(), type, offset,
                               is_mutable);
1516 1517 1518 1519 1520 1521 1522

  i::Handle<i::WasmGlobalObject> global_obj;
  if (!maybe_global_obj.ToHandle(&global_obj)) {
    thrower.RangeError("could not allocate memory");
    return;
  }

1523 1524 1525 1526 1527 1528 1529
  // The infrastructure for `new Foo` calls allocates an object, which is
  // available here as {args.This()}. We're going to discard this object
  // and use {global_obj} instead, but it does have the correct prototype,
  // which we must harvest from it. This makes a difference when the JS
  // constructor function wasn't {WebAssembly.Global} directly, but some
  // subclass: {global_obj} has {WebAssembly.Global}'s prototype at this
  // point, so we must overwrite that with the correct prototype for {Foo}.
1530 1531 1532
  if (!TransferPrototype(i_isolate, global_obj,
                         Utils::OpenHandle(*args.This()))) {
    return;
1533 1534
  }

1535 1536
  // Convert value to a WebAssembly value, the default value is 0.
  Local<v8::Value> value = Local<Value>::Cast(args[1]);
1537
  switch (type.kind()) {
1538
    case i::wasm::kI32: {
1539
      int32_t i32_value = 0;
1540
      if (!ToI32(value, context, &i32_value)) return;
1541 1542 1543
      global_obj->SetI32(i32_value);
      break;
    }
1544
    case i::wasm::kI64: {
1545
      int64_t i64_value = 0;
1546
      if (!ToI64(value, context, &i64_value)) return;
1547 1548 1549
      global_obj->SetI64(i64_value);
      break;
    }
1550
    case i::wasm::kF32: {
1551
      float f32_value = 0;
1552
      if (!ToF32(value, context, &f32_value)) return;
1553 1554 1555
      global_obj->SetF32(f32_value);
      break;
    }
1556
    case i::wasm::kF64: {
1557
      double f64_value = 0;
1558
      if (!ToF64(value, context, &f64_value)) return;
1559 1560
      global_obj->SetF64(f64_value);
      break;
1561
    }
1562
    case i::wasm::kRef:
1563
    case i::wasm::kRefNull: {
1564
      switch (type.heap_representation()) {
1565
        case i::wasm::HeapType::kExtern: {
1566 1567 1568 1569 1570 1571 1572 1573 1574
          if (args.Length() < 2) {
            // When no initial value is provided, we have to use the WebAssembly
            // default value 'null', and not the JS default value 'undefined'.
            global_obj->SetExternRef(i_isolate->factory()->null_value());
            break;
          }
          global_obj->SetExternRef(Utils::OpenHandle(*value));
          break;
        }
1575
        case i::wasm::HeapType::kFunc: {
1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590
          if (args.Length() < 2) {
            // When no initial value is provided, we have to use the WebAssembly
            // default value 'null', and not the JS default value 'undefined'.
            global_obj->SetFuncRef(i_isolate,
                                   i_isolate->factory()->null_value());
            break;
          }

          if (!global_obj->SetFuncRef(i_isolate, Utils::OpenHandle(*value))) {
            thrower.TypeError(
                "The value of funcref globals must be null or an "
                "exported function");
          }
          break;
        }
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601
        case i::wasm::HeapType::kString: {
          if (args.Length() < 2) {
            thrower.TypeError(
                "Missing initial value when creating stringref global");
            break;
          }

          DCHECK_EQ(type.nullability(), i::wasm::kNullable);
          if (!value->IsNull() && !value->IsString()) {
            thrower.TypeError(
                "The value of stringref globals must be null or a string");
1602
            break;
1603 1604 1605 1606 1607
          }

          global_obj->SetStringRef(Utils::OpenHandle(*value));
          break;
        }
1608 1609
        case internal::wasm::HeapType::kBottom:
          UNREACHABLE();
1610
        case i::wasm::HeapType::kEq:
1611 1612
        case internal::wasm::HeapType::kI31:
        case internal::wasm::HeapType::kData:
1613
        case internal::wasm::HeapType::kArray:
1614 1615 1616 1617 1618 1619 1620 1621
        case internal::wasm::HeapType::kAny: {
          internal::Handle<internal::Object> value_handle =
              Utils::OpenHandle(*value);
          if (checkAndUnpackAnyRef(i_isolate, value_handle, type, thrower)) {
            global_obj->SetAnyRef(value_handle);
          }
          break;
        }
1622 1623 1624
        case internal::wasm::HeapType::kStringViewWtf8:
        case internal::wasm::HeapType::kStringViewWtf16:
        case internal::wasm::HeapType::kStringViewIter:
1625 1626 1627
        default:
          // TODO(7748): Implement these.
          UNIMPLEMENTED();
1628 1629 1630
      }
      break;
    }
1631
    case i::wasm::kRtt:
1632
      // TODO(7748): Implement.
1633
      UNIMPLEMENTED();
1634 1635
    case i::wasm::kI8:
    case i::wasm::kI16:
1636
    case i::wasm::kVoid:
1637 1638
    case i::wasm::kS128:
    case i::wasm::kBottom:
1639
      UNREACHABLE();
1640 1641 1642 1643
  }

  i::Handle<i::JSObject> global_js_object(global_obj);
  args.GetReturnValue().Set(Utils::ToLocal(global_js_object));
1644 1645
}

1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660
namespace {

uint32_t GetIterableLength(i::Isolate* isolate, Local<Context> context,
                           Local<Object> iterable) {
  Local<String> length = Utils::ToLocal(isolate->factory()->length_string());
  MaybeLocal<Value> property = iterable->Get(context, length);
  if (property.IsEmpty()) return i::kMaxUInt32;
  MaybeLocal<Uint32> number = property.ToLocalChecked()->ToArrayIndex(context);
  if (number.IsEmpty()) return i::kMaxUInt32;
  DCHECK_NE(i::kMaxUInt32, number.ToLocalChecked()->Value());
  return number.ToLocalChecked()->Value();
}

}  // namespace

1661 1662
// WebAssembly.Tag
void WebAssemblyTag(const v8::FunctionCallbackInfo<v8::Value>& args) {
1663 1664 1665 1666
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);

1667
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Tag()");
1668
  if (!args.IsConstructCall()) {
1669
    thrower.TypeError("WebAssembly.Tag must be invoked with 'new'");
1670 1671 1672
    return;
  }
  if (!args[0]->IsObject()) {
1673
    thrower.TypeError("Argument 0 must be a tag type");
1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687
    return;
  }

  Local<Object> event_type = Local<Object>::Cast(args[0]);
  Local<Context> context = isolate->GetCurrentContext();
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);

  // Load the 'parameters' property of the event type.
  Local<String> parameters_key = v8_str(isolate, "parameters");
  v8::MaybeLocal<v8::Value> parameters_maybe =
      event_type->Get(context, parameters_key);
  v8::Local<v8::Value> parameters_value;
  if (!parameters_maybe.ToLocal(&parameters_value) ||
      !parameters_value->IsObject()) {
1688
    thrower.TypeError("Argument 0 must be a tag type with 'parameters'");
1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701
    return;
  }
  Local<Object> parameters = parameters_value.As<Object>();
  uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters);
  if (parameters_len == i::kMaxUInt32) {
    thrower.TypeError("Argument 0 contains parameters without 'length'");
    return;
  }
  if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) {
    thrower.TypeError("Argument 0 contains too many parameters");
    return;
  }

1702
  // Decode the tag type and construct a signature.
1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718
  std::vector<i::wasm::ValueType> param_types(parameters_len,
                                              i::wasm::kWasmVoid);
  for (uint32_t i = 0; i < parameters_len; ++i) {
    i::wasm::ValueType& type = param_types[i];
    MaybeLocal<Value> maybe = parameters->Get(context, i);
    if (!GetValueType(isolate, maybe, context, &type, enabled_features) ||
        type == i::wasm::kWasmVoid) {
      thrower.TypeError(
          "Argument 0 parameter type at index #%u must be a value type", i);
      return;
    }
  }
  const i::wasm::FunctionSig sig{0, parameters_len, param_types.data()};
  // Set the tag index to 0. It is only used for debugging purposes, and has no
  // meaningful value when declared outside of a wasm module.
  auto tag = i::WasmExceptionTag::New(i_isolate, 0);
1719 1720 1721
  i::Handle<i::JSObject> tag_object =
      i::WasmTagObject::New(i_isolate, &sig, tag);
  args.GetReturnValue().Set(Utils::ToLocal(tag_object));
1722 1723
}

1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751
// WebAssembly.Suspender
void WebAssemblySuspender(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);

  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Suspender()");
  if (!args.IsConstructCall()) {
    thrower.TypeError("WebAssembly.Suspender must be invoked with 'new'");
    return;
  }

  i::Handle<i::JSObject> suspender = i::WasmSuspenderObject::New(i_isolate);

  // The infrastructure for `new Foo` calls allocates an object, which is
  // available here as {args.This()}. We're going to discard this object
  // and use {suspender} instead, but it does have the correct prototype,
  // which we must harvest from it. This makes a difference when the JS
  // constructor function wasn't {WebAssembly.Suspender} directly, but some
  // subclass: {suspender} has {WebAssembly.Suspender}'s prototype at this
  // point, so we must overwrite that with the correct prototype for {Foo}.
  if (!TransferPrototype(i_isolate, suspender,
                         Utils::OpenHandle(*args.This()))) {
    return;
  }
  args.GetReturnValue().Set(Utils::ToLocal(suspender));
}

1752 1753
namespace {

1754
uint32_t GetEncodedSize(i::Handle<i::WasmTagObject> tag_object) {
1755
  auto serialized_sig = tag_object->serialized_signature();
1756 1757 1758 1759 1760
  i::wasm::WasmTagSig sig{0, static_cast<size_t>(serialized_sig.length()),
                          reinterpret_cast<i::wasm::ValueType*>(
                              serialized_sig.GetDataStartAddress())};
  i::wasm::WasmTag tag(&sig);
  return i::WasmExceptionPackage::GetEncodedSize(&tag);
1761 1762 1763
}

void EncodeExceptionValues(v8::Isolate* isolate,
1764
                           i::Handle<i::PodArray<i::wasm::ValueType>> signature,
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774
                           const Local<Value>& arg,
                           ScheduledErrorThrower* thrower,
                           i::Handle<i::FixedArray> values_out) {
  Local<Context> context = isolate->GetCurrentContext();
  uint32_t index = 0;
  if (!arg->IsObject()) {
    thrower->TypeError("Exception values must be an iterable object");
    return;
  }
  auto values = arg.As<Object>();
1775
  for (int i = 0; i < signature->length(); ++i) {
1776
    MaybeLocal<Value> maybe_value = values->Get(context, i);
1777 1778
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
    if (i_isolate->has_scheduled_exception()) return;
1779
    Local<Value> value = maybe_value.ToLocalChecked();
1780
    i::wasm::ValueType type = signature->get(i);
1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796
    switch (type.kind()) {
      case i::wasm::kI32: {
        int32_t i32 = 0;
        if (!ToI32(value, context, &i32)) return;
        i::EncodeI32ExceptionValue(values_out, &index, i32);
        break;
      }
      case i::wasm::kI64: {
        int64_t i64 = 0;
        if (!ToI64(value, context, &i64)) return;
        i::EncodeI64ExceptionValue(values_out, &index, i64);
        break;
      }
      case i::wasm::kF32: {
        float f32 = 0;
        if (!ToF32(value, context, &f32)) return;
1797
        int32_t i32 = base::bit_cast<int32_t>(f32);
1798 1799 1800 1801 1802 1803
        i::EncodeI32ExceptionValue(values_out, &index, i32);
        break;
      }
      case i::wasm::kF64: {
        double f64 = 0;
        if (!ToF64(value, context, &f64)) return;
1804
        int64_t i64 = base::bit_cast<int64_t>(f64);
1805 1806 1807 1808
        i::EncodeI64ExceptionValue(values_out, &index, i64);
        break;
      }
      case i::wasm::kRef:
1809
      case i::wasm::kRefNull:
1810 1811
        switch (type.heap_representation()) {
          case i::wasm::HeapType::kFunc:
1812
          case i::wasm::HeapType::kExtern:
1813 1814 1815 1816
          case i::wasm::HeapType::kAny:
          case i::wasm::HeapType::kEq:
          case i::wasm::HeapType::kI31:
          case i::wasm::HeapType::kData:
1817
          case i::wasm::HeapType::kArray:
1818 1819 1820 1821
          case i::wasm::HeapType::kString:
          case i::wasm::HeapType::kStringViewWtf8:
          case i::wasm::HeapType::kStringViewWtf16:
          case i::wasm::HeapType::kStringViewIter:
1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843
            values_out->set(index++, *Utils::OpenHandle(*value));
            break;
          case internal::wasm::HeapType::kBottom:
            UNREACHABLE();
          default:
            // TODO(7748): Add support for custom struct/array types.
            UNIMPLEMENTED();
        }
        break;
      case i::wasm::kRtt:
      case i::wasm::kI8:
      case i::wasm::kI16:
      case i::wasm::kVoid:
      case i::wasm::kBottom:
      case i::wasm::kS128:
        UNREACHABLE();
    }
  }
}

}  // namespace

1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858
void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);

  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception()");
  if (!args.IsConstructCall()) {
    thrower.TypeError("WebAssembly.Exception must be invoked with 'new'");
    return;
  }
  if (!args[0]->IsObject()) {
    thrower.TypeError("Argument 0 must be a WebAssembly tag");
    return;
  }
  i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
1859
  if (!i::HeapObject::cast(*arg0).IsWasmTagObject()) {
1860 1861 1862
    thrower.TypeError("Argument 0 must be a WebAssembly tag");
    return;
  }
1863 1864 1865
  i::Handle<i::WasmTagObject> tag_object =
      i::Handle<i::WasmTagObject>::cast(arg0);
  i::Handle<i::WasmExceptionTag> tag(
1866
      i::WasmExceptionTag::cast(tag_object->tag()), i_isolate);
1867
  uint32_t size = GetEncodedSize(tag_object);
1868 1869
  i::Handle<i::WasmExceptionPackage> runtime_exception =
      i::WasmExceptionPackage::New(i_isolate, tag, size);
1870
  // The constructor above should guarantee that the cast below succeeds.
1871
  i::Handle<i::FixedArray> values = i::Handle<i::FixedArray>::cast(
1872 1873
      i::WasmExceptionPackage::GetExceptionValues(i_isolate,
                                                  runtime_exception));
1874 1875
  i::Handle<i::PodArray<i::wasm::ValueType>> signature(
      tag_object->serialized_signature(), i_isolate);
1876 1877
  EncodeExceptionValues(isolate, signature, args[1], &thrower, values);
  if (thrower.error()) return;
1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904

  // Third argument: optional ExceptionOption ({traceStack: <bool>}).
  if (!args[2]->IsNullOrUndefined() && !args[2]->IsObject()) {
    thrower.TypeError("Argument 2 is not an object");
    return;
  }
  if (args[2]->IsObject()) {
    Local<Context> context = isolate->GetCurrentContext();
    Local<Object> trace_stack_obj = Local<Object>::Cast(args[2]);
    Local<String> trace_stack_key = v8_str(isolate, "traceStack");
    v8::MaybeLocal<v8::Value> maybe_trace_stack =
        trace_stack_obj->Get(context, trace_stack_key);
    v8::Local<Value> trace_stack_value;
    if (maybe_trace_stack.ToLocal(&trace_stack_value) &&
        trace_stack_value->BooleanValue(isolate)) {
      auto caller = Utils::OpenHandle(*args.NewTarget());
      i_isolate->CaptureAndSetErrorStack(runtime_exception, i::SKIP_NONE,
                                         caller);
      i::Handle<i::AccessorInfo> error_stack =
          i_isolate->factory()->error_stack_accessor();
      i::Handle<i::Name> name(i::Name::cast(error_stack->name()), i_isolate);
      i::JSObject::SetAccessor(runtime_exception, name, error_stack,
                               i::DONT_ENUM)
          .Assert();
    }
  }

1905 1906 1907 1908
  args.GetReturnValue().Set(
      Utils::ToLocal(i::Handle<i::Object>::cast(runtime_exception)));
}

1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958
namespace {
bool HasJSPromiseIntegrationFlag(Isolate* isolate, Local<Object> usage_obj,
                                 ErrorThrower* thrower, const char* flag_name) {
  Local<Context> context = isolate->GetCurrentContext();
  Local<String> flag_str = v8_str(isolate, flag_name);
  Local<String> first_str = v8_str(isolate, "first");
  Local<String> last_str = v8_str(isolate, "last");
  Local<String> none_str = v8_str(isolate, "none");
  v8::MaybeLocal<v8::Value> maybe_flag = usage_obj->Get(context, flag_str);
  v8::Local<Value> flag_value;
  v8::Local<String> flag_value_str;
  if (maybe_flag.ToLocal(&flag_value) && !flag_value->IsUndefined() &&
      flag_value->ToString(context).ToLocal(&flag_value_str)) {
    if (!flag_value_str->StringEquals(first_str) &&
        !flag_value_str->StringEquals(last_str) &&
        !flag_value_str->StringEquals(none_str)) {
      thrower->TypeError(
          "JS Promise Integration: Expected suspender "
          "position to be \"first\", \"last\" or \"none\"");
      return false;
    } else if (flag_value_str->StringEquals(last_str)) {
      // TODO(thibaudm): Support the "last" position.
      UNIMPLEMENTED();
    } else if (flag_value_str->StringEquals(first_str)) {
      return true;
    }
  }
  return false;
}

// Given {inner_sig}: [ti*] -> [to*]
// {outer_sig} must be: [externref ti*] -> [to*]
bool IsSuspendingSignature(const i::wasm::FunctionSig* inner_sig,
                           const i::wasm::FunctionSig* outer_sig) {
  if (inner_sig->parameter_count() + 1 != outer_sig->parameter_count()) {
    return false;
  }
  if (inner_sig->return_count() != outer_sig->return_count()) {
    return false;
  }
  if (outer_sig->GetParam(0) != i::wasm::kWasmExternRef) return false;
  for (size_t i = 1; i < outer_sig->parameter_count(); ++i) {
    if (outer_sig->GetParam(i) != inner_sig->GetParam(i - 1)) return false;
  }
  for (size_t i = 0; i < outer_sig->return_count(); ++i) {
    if (outer_sig->GetReturn(i) != inner_sig->GetReturn(i)) return false;
  }
  return true;
}

1959 1960
// Given {inner_sig}: externref [ti*] -> [to*]
// {outer_sig} must be: [ti*] -> [externref]
1961 1962 1963 1964 1965
bool IsPromisingSignature(const i::wasm::FunctionSig* inner_sig,
                          const i::wasm::FunctionSig* outer_sig) {
  if (inner_sig->parameter_count() != outer_sig->parameter_count() + 1) {
    return false;
  }
1966
  if (outer_sig->return_count() != 1) {
1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978
    return false;
  }
  if (inner_sig->GetParam(0) != i::wasm::kWasmExternRef) return false;
  for (size_t i = 0; i < outer_sig->parameter_count(); ++i) {
    if (outer_sig->GetParam(i) != inner_sig->GetParam(i + 1)) return false;
  }
  if (outer_sig->GetReturn(0) != i::wasm::kWasmExternRef) return false;
  return true;
}

}  // namespace

1979 1980 1981 1982 1983 1984 1985 1986 1987 1988
// WebAssembly.Function
void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Function()");
  if (!args.IsConstructCall()) {
    thrower.TypeError("WebAssembly.Function must be invoked with 'new'");
    return;
  }
1989 1990 1991 1992 1993 1994
  if (!args[0]->IsObject()) {
    thrower.TypeError("Argument 0 must be a function type");
    return;
  }
  Local<Object> function_type = Local<Object>::Cast(args[0]);
  Local<Context> context = isolate->GetCurrentContext();
1995
  auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
1996 1997 1998 1999 2000 2001

  // Load the 'parameters' property of the function type.
  Local<String> parameters_key = v8_str(isolate, "parameters");
  v8::MaybeLocal<v8::Value> parameters_maybe =
      function_type->Get(context, parameters_key);
  v8::Local<v8::Value> parameters_value;
2002 2003
  if (!parameters_maybe.ToLocal(&parameters_value) ||
      !parameters_value->IsObject()) {
2004 2005 2006
    thrower.TypeError("Argument 0 must be a function type with 'parameters'");
    return;
  }
2007 2008 2009 2010 2011 2012
  Local<Object> parameters = parameters_value.As<Object>();
  uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters);
  if (parameters_len == i::kMaxUInt32) {
    thrower.TypeError("Argument 0 contains parameters without 'length'");
    return;
  }
2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023
  if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) {
    thrower.TypeError("Argument 0 contains too many parameters");
    return;
  }

  // Load the 'results' property of the function type.
  Local<String> results_key = v8_str(isolate, "results");
  v8::MaybeLocal<v8::Value> results_maybe =
      function_type->Get(context, results_key);
  v8::Local<v8::Value> results_value;
  if (!results_maybe.ToLocal(&results_value)) return;
2024
  if (!results_value->IsObject()) {
2025 2026 2027
    thrower.TypeError("Argument 0 must be a function type with 'results'");
    return;
  }
2028 2029 2030 2031 2032 2033
  Local<Object> results = results_value.As<Object>();
  uint32_t results_len = GetIterableLength(i_isolate, context, results);
  if (results_len == i::kMaxUInt32) {
    thrower.TypeError("Argument 0 contains results without 'length'");
    return;
  }
2034
  if (results_len > i::wasm::kV8MaxWasmFunctionReturns) {
2035 2036 2037 2038 2039 2040
    thrower.TypeError("Argument 0 contains too many results");
    return;
  }

  // Decode the function type and construct a signature.
  i::Zone zone(i_isolate->allocator(), ZONE_NAME);
2041
  i::wasm::FunctionSig::Builder builder(&zone, results_len, parameters_len);
2042 2043 2044
  for (uint32_t i = 0; i < parameters_len; ++i) {
    i::wasm::ValueType type;
    MaybeLocal<Value> maybe = parameters->Get(context, i);
2045 2046
    if (!GetValueType(isolate, maybe, context, &type, enabled_features) ||
        type == i::wasm::kWasmVoid) {
2047 2048 2049 2050 2051 2052 2053 2054 2055 2056
      thrower.TypeError(
          "Argument 0 parameter type at index #%u must be a value type", i);
      return;
    }
    builder.AddParam(type);
  }
  for (uint32_t i = 0; i < results_len; ++i) {
    i::wasm::ValueType type;
    MaybeLocal<Value> maybe = results->Get(context, i);
    if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
2057
    if (type == i::wasm::kWasmVoid) {
2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068
      thrower.TypeError(
          "Argument 0 result type at index #%u must be a value type", i);
      return;
    }
    builder.AddReturn(type);
  }

  if (!args[1]->IsFunction()) {
    thrower.TypeError("Argument 1 must be a function");
    return;
  }
2069
  const i::wasm::FunctionSig* sig = builder.Build();
2070

2071 2072
  i::Handle<i::JSReceiver> callable =
      Utils::OpenHandle(*args[1].As<Function>());
2073 2074 2075

  i::wasm::Suspend suspend = i::wasm::kNoSuspend;
  i::wasm::Promise promise = i::wasm::kNoPromise;
2076
  if (i::v8_flags.experimental_wasm_stack_switching) {
2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101
    // Optional third argument for JS Promise Integration.
    if (!args[2]->IsNullOrUndefined() && !args[2]->IsObject()) {
      thrower.TypeError(
          "Expected argument 3 to be an object with a "
          "'suspending' or 'promising' property");
      return;
    }
    if (args[2]->IsObject()) {
      Local<Object> usage_obj = Local<Object>::Cast(args[2]);
      if (HasJSPromiseIntegrationFlag(isolate, usage_obj, &thrower,
                                      "suspending")) {
        suspend = i::wasm::kSuspend;
      }
      if (HasJSPromiseIntegrationFlag(isolate, usage_obj, &thrower,
                                      "promising")) {
        promise = i::wasm::kPromise;
      }
    }
  }

  bool is_wasm_exported_function =
      i::WasmExportedFunction::IsWasmExportedFunction(*callable);
  bool is_wasm_js_function = i::WasmJSFunction::IsWasmJSFunction(*callable);

  if (is_wasm_exported_function && !suspend && !promise) {
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112
    if (*i::Handle<i::WasmExportedFunction>::cast(callable)->sig() == *sig) {
      args.GetReturnValue().Set(Utils::ToLocal(callable));
      return;
    }

    thrower.TypeError(
        "The signature of Argument 1 (a WebAssembly function) does "
        "not match the signature specified in Argument 0");
    return;
  }

2113
  if (is_wasm_js_function && !suspend && !promise) {
2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124
    if (i::Handle<i::WasmJSFunction>::cast(callable)->MatchesSignature(sig)) {
      args.GetReturnValue().Set(Utils::ToLocal(callable));
      return;
    }

    thrower.TypeError(
        "The signature of Argument 1 (a WebAssembly function) does "
        "not match the signature specified in Argument 0");
    return;
  }

2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165
  if (is_wasm_exported_function && suspend) {
    // TODO(thibaudm): Support wasm-to-wasm calls with suspending behavior, and
    // also with combined promising+suspending behavior.
    UNIMPLEMENTED();
  }
  if (is_wasm_exported_function && promise) {
    auto wasm_exported_function = i::WasmExportedFunction::cast(*callable);
    i::SharedFunctionInfo sfi = wasm_exported_function.shared();
    i::WasmExportedFunctionData data = sfi.wasm_exported_function_data();
    if (!IsPromisingSignature(data.sig(), sig)) {
      thrower.TypeError("Incompatible signature for promising function");
      return;
    }
    i::Handle<i::WasmInstanceObject> instance(
        i::WasmInstanceObject::cast(data.internal().ref()), i_isolate);
    int func_index = data.function_index();
    i::Handle<i::CodeT> wrapper =
        BUILTIN_CODE(i_isolate, WasmReturnPromiseOnSuspend);
    i::Handle<i::JSFunction> result = i::WasmExportedFunction::New(
        i_isolate, instance, func_index,
        static_cast<int>(data.sig()->parameter_count()), wrapper);
    args.GetReturnValue().Set(Utils::ToLocal(result));
    return;
  }
  if (is_wasm_js_function && promise) {
    // TODO(thibaudm): This case has no practical use. The generated suspender
    // would be unusable since the stack would always contain at least one JS
    // frame. But for now the spec would require us to add specific JS-to-JS and
    // wasm-to-JS wrappers to support this case. Leave this unimplemented for
    // now.
    UNIMPLEMENTED();
  }
  if (is_wasm_js_function && suspend) {
    auto wasm_js_function = i::WasmJSFunction::cast(*callable);
    const i::wasm::FunctionSig* inner_sig =
        wasm_js_function.GetSignature(&zone);
    if (!IsSuspendingSignature(inner_sig, sig)) {
      thrower.TypeError("Incompatible signature for suspending function");
      return;
    }
  }
2166
  i::Handle<i::JSFunction> result =
2167
      i::WasmJSFunction::New(i_isolate, sig, callable, suspend);
2168
  args.GetReturnValue().Set(Utils::ToLocal(result));
2169 2170
}

2171 2172 2173 2174 2175 2176 2177
// WebAssembly.Function.type(WebAssembly.Function) -> FunctionType
void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Function.type()");

2178
  const i::wasm::FunctionSig* sig;
2179
  i::Zone zone(i_isolate->allocator(), ZONE_NAME);
2180 2181
  i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
  if (i::WasmExportedFunction::IsWasmExportedFunction(*arg0)) {
2182 2183 2184 2185 2186 2187
    auto wasm_exported_function =
        i::Handle<i::WasmExportedFunction>::cast(arg0);
    auto sfi = handle(wasm_exported_function->shared(), i_isolate);
    i::Handle<i::WasmExportedFunctionData> data =
        handle(sfi->wasm_exported_function_data(), i_isolate);
    sig = wasm_exported_function->sig();
2188 2189
    if (i::WasmFunctionData::PromiseField::decode(data->js_promise_flags())) {
      // If this export is "promising", the first parameter of the original
2190 2191
      // function is an externref (suspender) which does not appear in the
      // wrapper function's signature. The wrapper function also returns a
2192 2193
      // promise as an externref instead of the original return type.
      size_t param_count = sig->parameter_count();
2194
      DCHECK_GE(param_count, 1);
2195
      DCHECK_EQ(sig->GetParam(0), i::wasm::kWasmExternRef);
2196 2197
      i::wasm::FunctionSig::Builder builder(&zone, 1, param_count - 1);
      for (size_t i = 1; i < param_count; ++i) {
2198
        builder.AddParam(sig->GetParam(i));
2199
      }
2200
      builder.AddReturn(i::wasm::kWasmExternRef);
2201 2202
      sig = builder.Build();
    }
2203
  } else if (i::WasmJSFunction::IsWasmJSFunction(*arg0)) {
2204
    sig = i::Handle<i::WasmJSFunction>::cast(arg0)->GetSignature(&zone);
2205 2206 2207 2208 2209
  } else {
    thrower.TypeError("Argument 0 must be a WebAssembly.Function");
    return;
  }

2210 2211
  auto type = i::wasm::GetTypeForFunction(i_isolate, sig);
  args.GetReturnValue().Set(Utils::ToLocal(type));
2212 2213
}

2214
constexpr const char* kName_WasmGlobalObject = "WebAssembly.Global";
2215 2216 2217
constexpr const char* kName_WasmMemoryObject = "WebAssembly.Memory";
constexpr const char* kName_WasmInstanceObject = "WebAssembly.Instance";
constexpr const char* kName_WasmTableObject = "WebAssembly.Table";
2218
constexpr const char* kName_WasmTagObject = "WebAssembly.Tag";
2219
constexpr const char* kName_WasmExceptionPackage = "WebAssembly.Exception";
2220 2221 2222 2223 2224 2225

#define EXTRACT_THIS(var, WasmType)                                  \
  i::Handle<i::WasmType> var;                                        \
  {                                                                  \
    i::Handle<i::Object> this_arg = Utils::OpenHandle(*args.This()); \
    if (!this_arg->Is##WasmType()) {                                 \
2226
      thrower.TypeError("Receiver is not a %s", kName_##WasmType);   \
2227 2228 2229 2230 2231
      return;                                                        \
    }                                                                \
    var = i::Handle<i::WasmType>::cast(this_arg);                    \
  }

2232
void WebAssemblyInstanceGetExports(
2233
    const v8::FunctionCallbackInfo<v8::Value>& args) {
2234 2235 2236
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
2237
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Instance.exports()");
2238
  EXTRACT_THIS(receiver, WasmInstanceObject);
2239
  i::Handle<i::JSObject> exports_object(receiver->exports_object(), i_isolate);
2240 2241 2242
  args.GetReturnValue().Set(Utils::ToLocal(exports_object));
}

2243 2244
void WebAssemblyTableGetLength(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
2245
  v8::Isolate* isolate = args.GetIsolate();
2246 2247
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
2248
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.length()");
2249
  EXTRACT_THIS(receiver, WasmTableObject);
2250 2251
  args.GetReturnValue().Set(
      v8::Number::New(isolate, receiver->current_length()));
2252
}
2253

2254
// WebAssembly.Table.grow(num, init_value = null) -> num
2255
void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
2256
  v8::Isolate* isolate = args.GetIsolate();
2257 2258
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
2259
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.grow()");
2260
  Local<Context> context = isolate->GetCurrentContext();
2261
  EXTRACT_THIS(receiver, WasmTableObject);
2262

2263 2264 2265 2266 2267
  uint32_t grow_by;
  if (!EnforceUint32("Argument 0", args[0], context, &thrower, &grow_by)) {
    return;
  }

2268 2269 2270 2271
  i::Handle<i::Object> init_value;

  if (args.Length() >= 2 && !args[1]->IsUndefined()) {
    init_value = Utils::OpenHandle(*args[1]);
2272 2273
    if (!i::WasmTableObject::IsValidJSElement(i_isolate, receiver,
                                              init_value)) {
2274 2275 2276
      thrower.TypeError("Argument 1 must be a valid type for the table");
      return;
    }
2277 2278 2279 2280
  } else if (receiver->type().is_non_nullable()) {
    thrower.TypeError(
        "Argument 1 must be specified for non-nullable element type");
    return;
2281 2282
  } else {
    init_value = DefaultReferenceValue(i_isolate, receiver->type());
2283 2284
  }

2285 2286
  int old_size = i::WasmTableObject::Grow(i_isolate, receiver, grow_by,
                                          init_value, i::WasmTableObject::kJS);
2287

2288 2289
  if (old_size < 0) {
    thrower.RangeError("failed to grow table by %u", grow_by);
2290 2291
    return;
  }
2292 2293
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(old_size);
2294
}
2295

2296
// WebAssembly.Table.get(num) -> any
2297
void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) {
2298
  v8::Isolate* isolate = args.GetIsolate();
2299
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2300
  HandleScope scope(isolate);
2301
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.get()");
2302
  Local<Context> context = isolate->GetCurrentContext();
2303
  EXTRACT_THIS(receiver, WasmTableObject);
2304 2305 2306 2307 2308

  uint32_t index;
  if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) {
    return;
  }
2309 2310
  if (!i::WasmTableObject::IsInBounds(i_isolate, receiver, index)) {
    thrower.RangeError("invalid index %u into function table", index);
2311
    return;
2312
  }
2313

2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326
  if (receiver->type() == i::wasm::kWasmStringViewWtf8) {
    thrower.TypeError("stringview_wtf8 has no JS representation");
    return;
  }
  if (receiver->type() == i::wasm::kWasmStringViewWtf16) {
    thrower.TypeError("stringview_wtf16 has no JS representation");
    return;
  }
  if (receiver->type() == i::wasm::kWasmStringViewIter) {
    thrower.TypeError("stringview_iter has no JS representation");
    return;
  }

2327 2328
  i::Handle<i::Object> result = i::WasmTableObject::Get(
      i_isolate, receiver, index, i::WasmTableObject::kJS);
2329

2330
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
2331
  return_value.Set(Utils::ToLocal(result));
2332
}
2333

2334
// WebAssembly.Table.set(num, any)
2335
void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) {
2336
  v8::Isolate* isolate = args.GetIsolate();
2337
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2338
  HandleScope scope(isolate);
2339
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.set()");
2340
  Local<Context> context = isolate->GetCurrentContext();
2341
  EXTRACT_THIS(table_object, WasmTableObject);
2342

2343
  // Parameter 0.
2344 2345 2346 2347
  uint32_t index;
  if (!EnforceUint32("Argument 0", args[0], context, &thrower, &index)) {
    return;
  }
2348 2349
  if (!i::WasmTableObject::IsInBounds(i_isolate, table_object, index)) {
    thrower.RangeError("invalid index %u into function table", index);
2350 2351
    return;
  }
2352

2353 2354 2355 2356 2357
  i::Handle<i::Object> element =
      args.Length() >= 2
          ? Utils::OpenHandle(*args[1])
          : DefaultReferenceValue(i_isolate, table_object->type());

2358
  if (!i::WasmTableObject::IsValidJSElement(i_isolate, table_object, element)) {
2359 2360
    thrower.TypeError("Argument 1 is invalid for table of type %s",
                      table_object->type().name().c_str());
2361 2362
    return;
  }
2363

2364 2365
  i::WasmTableObject::Set(i_isolate, table_object, index, element,
                          i::WasmTableObject::kJS);
2366
}
2367

2368
// WebAssembly.Table.type() -> TableType
2369
void WebAssemblyTableType(const v8::FunctionCallbackInfo<v8::Value>& args) {
2370 2371 2372 2373 2374
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Table.type()");

2375
  EXTRACT_THIS(table, WasmTableObject);
2376
  base::Optional<uint32_t> max_size;
2377
  if (!table->maximum_length().IsUndefined()) {
2378 2379 2380
    uint64_t max_size64 = table->maximum_length().Number();
    DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max());
    max_size.emplace(static_cast<uint32_t>(max_size64));
2381
  }
2382 2383 2384
  auto type = i::wasm::GetTypeForTable(i_isolate, table->type(),
                                       table->current_length(), max_size);
  args.GetReturnValue().Set(Utils::ToLocal(type));
2385 2386
}

2387
// WebAssembly.Memory.grow(num) -> num
2388
void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) {
2389
  v8::Isolate* isolate = args.GetIsolate();
2390
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2391
  HandleScope scope(isolate);
2392
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.grow()");
2393
  Local<Context> context = isolate->GetCurrentContext();
2394 2395
  EXTRACT_THIS(receiver, WasmMemoryObject);

2396 2397
  uint32_t delta_pages;
  if (!EnforceUint32("Argument 0", args[0], context, &thrower, &delta_pages)) {
2398 2399
    return;
  }
2400

2401
  i::Handle<i::JSArrayBuffer> old_buffer(receiver->array_buffer(), i_isolate);
2402

2403 2404
  uint64_t old_pages64 = old_buffer->byte_length() / i::wasm::kWasmPageSize;
  uint64_t new_pages64 = old_pages64 + static_cast<uint64_t>(delta_pages);
2405

2406
  if (new_pages64 > static_cast<uint64_t>(receiver->maximum_pages())) {
2407
    thrower.RangeError("Maximum memory size exceeded");
2408 2409
    return;
  }
2410

2411
  int32_t ret = i::WasmMemoryObject::Grow(i_isolate, receiver, delta_pages);
2412
  if (ret == -1) {
2413
    thrower.RangeError("Unable to grow instance memory");
2414 2415 2416 2417
    return;
  }
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(ret);
2418
}
2419

2420
// WebAssembly.Memory.buffer -> ArrayBuffer
2421 2422 2423
void WebAssemblyMemoryGetBuffer(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
2424
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
2425
  HandleScope scope(isolate);
2426
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.buffer");
2427 2428
  EXTRACT_THIS(receiver, WasmMemoryObject);

2429 2430
  i::Handle<i::Object> buffer_obj(receiver->array_buffer(), i_isolate);
  DCHECK(buffer_obj->IsJSArrayBuffer());
2431 2432
  i::Handle<i::JSArrayBuffer> buffer(i::JSArrayBuffer::cast(*buffer_obj),
                                     i_isolate);
2433 2434 2435 2436 2437
  if (buffer->is_shared()) {
    // TODO(gdeepti): More needed here for when cached buffer, and current
    // buffer are out of sync, handle that here when bounds checks, and Grow
    // are handled correctly.
    Maybe<bool> result =
2438
        buffer->SetIntegrityLevel(buffer, i::FROZEN, i::kDontThrow);
2439 2440 2441 2442 2443
    if (!result.FromJust()) {
      thrower.TypeError(
          "Status of setting SetIntegrityLevel of buffer is false.");
    }
  }
2444 2445 2446
  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
  return_value.Set(Utils::ToLocal(buffer));
}
2447

2448
// WebAssembly.Memory.type() -> MemoryType
2449
void WebAssemblyMemoryType(const v8::FunctionCallbackInfo<v8::Value>& args) {
2450 2451 2452 2453 2454
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Memory.type()");

2455
  EXTRACT_THIS(memory, WasmMemoryObject);
2456 2457 2458
  i::Handle<i::JSArrayBuffer> buffer(memory->array_buffer(), i_isolate);
  size_t curr_size = buffer->byte_length() / i::wasm::kWasmPageSize;
  DCHECK_LE(curr_size, std::numeric_limits<uint32_t>::max());
2459 2460
  uint32_t min_size = static_cast<uint32_t>(curr_size);
  base::Optional<uint32_t> max_size;
2461
  if (memory->has_maximum_pages()) {
2462 2463 2464
    uint64_t max_size64 = memory->maximum_pages();
    DCHECK_LE(max_size64, std::numeric_limits<uint32_t>::max());
    max_size.emplace(static_cast<uint32_t>(max_size64));
2465
  }
2466 2467
  bool shared = buffer->is_shared();
  auto type = i::wasm::GetTypeForMemory(i_isolate, min_size, max_size, shared);
2468
  args.GetReturnValue().Set(Utils::ToLocal(type));
2469 2470
}

2471 2472 2473 2474 2475 2476 2477
// WebAssembly.Tag.type() -> FunctionType
void WebAssemblyTagType(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Tag.type()");

2478
  EXTRACT_THIS(tag, WasmTagObject);
2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491
  if (thrower.error()) return;

  int n = tag->serialized_signature().length();
  std::vector<i::wasm::ValueType> data(n);
  if (n > 0) {
    tag->serialized_signature().copy_out(0, data.data(), n);
  }
  const i::wasm::FunctionSig sig{0, data.size(), data.data()};
  constexpr bool kForException = true;
  auto type = i::wasm::GetTypeForFunction(i_isolate, &sig, kForException);
  args.GetReturnValue().Set(Utils::ToLocal(type));
}

2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512
void WebAssemblyExceptionGetArg(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception.getArg()");

  EXTRACT_THIS(exception, WasmExceptionPackage);
  if (thrower.error()) return;

  i::MaybeHandle<i::WasmTagObject> maybe_tag =
      GetFirstArgumentAsTag(args, &thrower);
  if (thrower.error()) return;
  auto tag = maybe_tag.ToHandleChecked();
  Local<Context> context = isolate->GetCurrentContext();
  uint32_t index;
  if (!EnforceUint32("Index", args[1], context, &thrower, &index)) {
    return;
  }
  auto maybe_values =
      i::WasmExceptionPackage::GetExceptionValues(i_isolate, exception);
2513 2514 2515 2516 2517 2518 2519 2520 2521 2522

  auto this_tag =
      i::WasmExceptionPackage::GetExceptionTag(i_isolate, exception);
  DCHECK(this_tag->IsWasmExceptionTag());
  if (tag->tag() != *this_tag) {
    thrower.TypeError("First argument does not match the exception tag");
    return;
  }

  DCHECK(!maybe_values->IsUndefined());
2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542
  auto values = i::Handle<i::FixedArray>::cast(maybe_values);
  auto signature = tag->serialized_signature();
  if (index >= static_cast<uint32_t>(signature.length())) {
    thrower.RangeError("Index out of range");
    return;
  }
  // First, find the index in the values array.
  uint32_t decode_index = 0;
  // Since the bounds check above passed, the cast to int is safe.
  for (int i = 0; i < static_cast<int>(index); ++i) {
    switch (signature.get(i).kind()) {
      case i::wasm::kI32:
      case i::wasm::kF32:
        decode_index += 2;
        break;
      case i::wasm::kI64:
      case i::wasm::kF64:
        decode_index += 4;
        break;
      case i::wasm::kRef:
2543
      case i::wasm::kRefNull:
2544 2545
        switch (signature.get(i).heap_representation()) {
          case i::wasm::HeapType::kFunc:
2546
          case i::wasm::HeapType::kExtern:
2547 2548 2549 2550
          case i::wasm::HeapType::kAny:
          case i::wasm::HeapType::kEq:
          case i::wasm::HeapType::kI31:
          case i::wasm::HeapType::kData:
2551
          case i::wasm::HeapType::kArray:
2552 2553 2554 2555
          case i::wasm::HeapType::kString:
          case i::wasm::HeapType::kStringViewWtf8:
          case i::wasm::HeapType::kStringViewWtf16:
          case i::wasm::HeapType::kStringViewIter:
2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593
            decode_index++;
            break;
          case i::wasm::HeapType::kBottom:
            UNREACHABLE();
          default:
            // TODO(7748): Add support for custom struct/array types.
            UNIMPLEMENTED();
        }
        break;
      case i::wasm::kRtt:
      case i::wasm::kI8:
      case i::wasm::kI16:
      case i::wasm::kVoid:
      case i::wasm::kBottom:
      case i::wasm::kS128:
        UNREACHABLE();
    }
  }
  // Decode the value at {decode_index}.
  Local<Value> result;
  switch (signature.get(index).kind()) {
    case i::wasm::kI32: {
      uint32_t u32_bits = 0;
      i::DecodeI32ExceptionValue(values, &decode_index, &u32_bits);
      int32_t i32 = static_cast<int32_t>(u32_bits);
      result = v8::Integer::New(isolate, i32);
      break;
    }
    case i::wasm::kI64: {
      uint64_t u64_bits = 0;
      i::DecodeI64ExceptionValue(values, &decode_index, &u64_bits);
      int64_t i64 = static_cast<int64_t>(u64_bits);
      result = v8::BigInt::New(isolate, i64);
      break;
    }
    case i::wasm::kF32: {
      uint32_t f32_bits = 0;
      DecodeI32ExceptionValue(values, &decode_index, &f32_bits);
2594
      float f32 = base::bit_cast<float>(f32_bits);
2595 2596 2597 2598 2599 2600
      result = v8::Number::New(isolate, f32);
      break;
    }
    case i::wasm::kF64: {
      uint64_t f64_bits = 0;
      DecodeI64ExceptionValue(values, &decode_index, &f64_bits);
2601
      double f64 = base::bit_cast<double>(f64_bits);
2602 2603 2604 2605
      result = v8::Number::New(isolate, f64);
      break;
    }
    case i::wasm::kRef:
2606
    case i::wasm::kRefNull:
2607 2608
      switch (signature.get(index).heap_representation()) {
        case i::wasm::HeapType::kFunc:
2609
        case i::wasm::HeapType::kExtern:
2610 2611 2612
        case i::wasm::HeapType::kAny:
        case i::wasm::HeapType::kEq:
        case i::wasm::HeapType::kI31:
2613
        case i::wasm::HeapType::kArray:
2614 2615 2616 2617 2618
        case i::wasm::HeapType::kData:
        case i::wasm::HeapType::kString:
        case i::wasm::HeapType::kStringViewWtf8:
        case i::wasm::HeapType::kStringViewWtf16:
        case i::wasm::HeapType::kStringViewIter: {
2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640
          auto obj = values->get(decode_index);
          result = Utils::ToLocal(i::Handle<i::Object>(obj, i_isolate));
          break;
        }
        case i::wasm::HeapType::kBottom:
          UNREACHABLE();
        default:
          // TODO(7748): Add support for custom struct/array types.
          UNIMPLEMENTED();
      }
      break;
    case i::wasm::kRtt:
    case i::wasm::kI8:
    case i::wasm::kI16:
    case i::wasm::kVoid:
    case i::wasm::kBottom:
    case i::wasm::kS128:
      UNREACHABLE();
  }
  args.GetReturnValue().Set(result);
}

2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660
void WebAssemblyExceptionIs(const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception.is()");

  EXTRACT_THIS(exception, WasmExceptionPackage);
  if (thrower.error()) return;

  auto tag = i::WasmExceptionPackage::GetExceptionTag(i_isolate, exception);
  DCHECK(tag->IsWasmExceptionTag());

  auto maybe_tag = GetFirstArgumentAsTag(args, &thrower);
  if (thrower.error()) {
    return;
  }
  auto tag_arg = maybe_tag.ToHandleChecked();
  args.GetReturnValue().Set(tag_arg->tag() == *tag);
}

2661 2662 2663 2664 2665 2666 2667 2668 2669 2670
void WebAssemblyGlobalGetValueCommon(
    const v8::FunctionCallbackInfo<v8::Value>& args, const char* name) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
  ScheduledErrorThrower thrower(i_isolate, name);
  EXTRACT_THIS(receiver, WasmGlobalObject);

  v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();

2671
  switch (receiver->type().kind()) {
2672
    case i::wasm::kI32:
2673 2674
      return_value.Set(receiver->GetI32());
      break;
2675
    case i::wasm::kI64: {
2676 2677
      Local<BigInt> value = BigInt::New(isolate, receiver->GetI64());
      return_value.Set(value);
2678
      break;
2679
    }
2680
    case i::wasm::kF32:
2681 2682
      return_value.Set(receiver->GetF32());
      break;
2683
    case i::wasm::kF64:
2684 2685
      return_value.Set(receiver->GetF64());
      break;
2686
    case i::wasm::kS128:
2687 2688
      thrower.TypeError("Can't get the value of s128 WebAssembly.Global");
      break;
2689
    case i::wasm::kRef:
2690
    case i::wasm::kRefNull:
2691
      switch (receiver->type().heap_representation()) {
2692
        case i::wasm::HeapType::kExtern:
2693
        case i::wasm::HeapType::kString:
2694 2695
          return_value.Set(Utils::ToLocal(receiver->GetRef()));
          break;
2696
        case i::wasm::HeapType::kFunc: {
2697 2698 2699 2700 2701 2702 2703 2704 2705
          i::Handle<i::Object> result = receiver->GetRef();
          if (result->IsWasmInternalFunction()) {
            result = handle(
                i::Handle<i::WasmInternalFunction>::cast(result)->external(),
                i_isolate);
          }
          return_value.Set(Utils::ToLocal(result));
          break;
        }
2706 2707 2708 2709 2710 2711 2712 2713 2714
        case i::wasm::HeapType::kStringViewWtf8:
          thrower.TypeError("stringview_wtf8 has no JS representation");
          break;
        case i::wasm::HeapType::kStringViewWtf16:
          thrower.TypeError("stringview_wtf16 has no JS representation");
          break;
        case i::wasm::HeapType::kStringViewIter:
          thrower.TypeError("stringview_iter has no JS representation");
          break;
2715
        case i::wasm::HeapType::kBottom:
2716
          UNREACHABLE();
2717 2718 2719
        case i::wasm::HeapType::kI31:
        case i::wasm::HeapType::kData:
        case i::wasm::HeapType::kArray:
2720
        case i::wasm::HeapType::kEq:
2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735
        case i::wasm::HeapType::kAny: {
          i::Handle<i::Object> result = receiver->GetRef();
          if (!i::v8_flags.wasm_gc_js_interop && result->IsWasmObject()) {
            // Transform wasm object into JS-compliant representation.
            i::Handle<i::JSObject> wrapper =
                i_isolate->factory()->NewJSObject(i_isolate->object_function());
            i::JSObject::AddProperty(
                i_isolate, wrapper,
                i_isolate->factory()->wasm_wrapped_object_symbol(), result,
                i::NONE);
            result = wrapper;
          }
          return_value.Set(Utils::ToLocal(result));
          break;
        }
2736 2737 2738 2739 2740
        default:
          // TODO(7748): Implement these.
          UNIMPLEMENTED();
      }
      break;
2741
    case i::wasm::kRtt:
2742
      UNIMPLEMENTED();  // TODO(7748): Implement.
2743 2744 2745
    case i::wasm::kI8:
    case i::wasm::kI16:
    case i::wasm::kBottom:
2746
    case i::wasm::kVoid:
2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771
      UNREACHABLE();
  }
}

// WebAssembly.Global.valueOf() -> num
void WebAssemblyGlobalValueOf(const v8::FunctionCallbackInfo<v8::Value>& args) {
  return WebAssemblyGlobalGetValueCommon(args, "WebAssembly.Global.valueOf()");
}

// get WebAssembly.Global.value -> num
void WebAssemblyGlobalGetValue(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  return WebAssemblyGlobalGetValueCommon(args, "get WebAssembly.Global.value");
}

// set WebAssembly.Global.value(num)
void WebAssemblyGlobalSetValue(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  HandleScope scope(isolate);
  Local<Context> context = isolate->GetCurrentContext();
  ScheduledErrorThrower thrower(i_isolate, "set WebAssembly.Global.value");
  EXTRACT_THIS(receiver, WasmGlobalObject);

2772 2773 2774 2775
  if (!receiver->is_mutable()) {
    thrower.TypeError("Can't set the value of an immutable global.");
    return;
  }
2776
  if (args.Length() == 0) {
2777
    thrower.TypeError("Argument 0 is required");
2778 2779
    return;
  }
2780

2781
  switch (receiver->type().kind()) {
2782
    case i::wasm::kI32: {
2783 2784 2785 2786 2787
      int32_t i32_value = 0;
      if (!args[0]->Int32Value(context).To(&i32_value)) return;
      receiver->SetI32(i32_value);
      break;
    }
2788
    case i::wasm::kI64: {
2789 2790 2791
      v8::Local<v8::BigInt> bigint_value;
      if (!args[0]->ToBigInt(context).ToLocal(&bigint_value)) return;
      receiver->SetI64(bigint_value->Int64Value());
2792
      break;
2793
    }
2794
    case i::wasm::kF32: {
2795 2796
      double f64_value = 0;
      if (!args[0]->NumberValue(context).To(&f64_value)) return;
2797
      receiver->SetF32(i::DoubleToFloat32(f64_value));
2798 2799
      break;
    }
2800
    case i::wasm::kF64: {
2801 2802 2803 2804 2805
      double f64_value = 0;
      if (!args[0]->NumberValue(context).To(&f64_value)) return;
      receiver->SetF64(f64_value);
      break;
    }
2806
    case i::wasm::kS128:
2807 2808
      thrower.TypeError("Can't set the value of s128 WebAssembly.Global");
      break;
2809
    case i::wasm::kRef:
2810
    case i::wasm::kRefNull:
2811
      switch (receiver->type().heap_representation()) {
2812
        case i::wasm::HeapType::kExtern:
2813 2814
          receiver->SetExternRef(Utils::OpenHandle(*args[0]));
          break;
2815
        case i::wasm::HeapType::kFunc: {
2816 2817 2818 2819 2820 2821 2822
          if (!receiver->SetFuncRef(i_isolate, Utils::OpenHandle(*args[0]))) {
            thrower.TypeError(
                "value of an funcref reference must be either null or an "
                "exported function");
          }
          break;
        }
2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850
        case i::wasm::HeapType::kString: {
          if (!args[0]->IsString()) {
            if (args[0]->IsNull()) {
              if (receiver->type().nullability() == i::wasm::kNonNullable) {
                thrower.TypeError(
                    "Can't set non-nullable stringref global to null");
                break;
              }
            } else {
              thrower.TypeError(
                  receiver->type().nullability() == i::wasm::kNonNullable
                      ? "Non-nullable stringref global can only hold a string"
                      : "Stringref global can only hold null or a string");
              break;
            }
          }
          receiver->SetStringRef(Utils::OpenHandle(*args[0]));
          break;
        }
        case i::wasm::HeapType::kStringViewWtf8:
          thrower.TypeError("stringview_wtf8 has no JS representation");
          break;
        case i::wasm::HeapType::kStringViewWtf16:
          thrower.TypeError("stringview_wtf16 has no JS representation");
          break;
        case i::wasm::HeapType::kStringViewIter:
          thrower.TypeError("stringview_iter has no JS representation");
          break;
2851
        case i::wasm::HeapType::kBottom:
2852
          UNREACHABLE();
2853 2854 2855
        case i::wasm::HeapType::kI31:
        case i::wasm::HeapType::kData:
        case i::wasm::HeapType::kArray:
2856
        case i::wasm::HeapType::kEq:
2857 2858 2859 2860 2861 2862 2863 2864 2865
        case i::wasm::HeapType::kAny: {
          internal::Handle<internal::Object> value_handle =
              Utils::OpenHandle(*args[0]);
          if (checkAndUnpackAnyRef(i_isolate, value_handle, receiver->type(),
                                   thrower)) {
            receiver->SetAnyRef(value_handle);
          }
          break;
        }
2866 2867 2868 2869 2870
        default:
          // TODO(7748): Implement these.
          UNIMPLEMENTED();
      }
      break;
2871
    case i::wasm::kRtt:
2872
      // TODO(7748): Implement.
2873
      UNIMPLEMENTED();
2874 2875 2876
    case i::wasm::kI8:
    case i::wasm::kI16:
    case i::wasm::kBottom:
2877
    case i::wasm::kVoid:
2878 2879 2880 2881
      UNREACHABLE();
  }
}

2882
// WebAssembly.Global.type() -> GlobalType
2883
void WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value>& args) {
2884 2885 2886 2887 2888
  v8::Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
  ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Global.type()");

2889
  EXTRACT_THIS(global, WasmGlobalObject);
2890 2891 2892
  auto type = i::wasm::GetTypeForGlobal(i_isolate, global->is_mutable(),
                                        global->type());
  args.GetReturnValue().Set(Utils::ToLocal(type));
2893 2894
}

2895 2896 2897 2898
}  // namespace

// TODO(titzer): we use the API to create the function template because the
// internal guts are too ugly to replicate here.
2899
static i::Handle<i::FunctionTemplateInfo> NewFunctionTemplate(
2900 2901
    i::Isolate* i_isolate, FunctionCallback func, bool has_prototype,
    SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
2902
  Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
2903 2904
  ConstructorBehavior behavior =
      has_prototype ? ConstructorBehavior::kAllow : ConstructorBehavior::kThrow;
2905
  Local<FunctionTemplate> templ = FunctionTemplate::New(
2906 2907
      isolate, func, {}, {}, 0, behavior, side_effect_type);
  if (has_prototype) templ->ReadOnlyPrototype();
2908
  return v8::Utils::OpenHandle(*templ);
2909 2910
}

2911 2912 2913 2914 2915 2916 2917
static i::Handle<i::ObjectTemplateInfo> NewObjectTemplate(
    i::Isolate* i_isolate) {
  Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
  Local<ObjectTemplate> templ = ObjectTemplate::New(isolate);
  return v8::Utils::OpenHandle(*templ);
}

2918 2919
namespace internal {

2920 2921 2922 2923
Handle<JSFunction> CreateFunc(
    Isolate* isolate, Handle<String> name, FunctionCallback func,
    bool has_prototype,
    SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
2924
  Handle<FunctionTemplateInfo> temp =
2925
      NewFunctionTemplate(isolate, func, has_prototype, side_effect_type);
2926
  Handle<JSFunction> function =
2927
      ApiNatives::InstantiateFunction(temp, name).ToHandleChecked();
2928
  DCHECK(function->shared().HasSharedName());
2929 2930 2931
  return function;
}

2932 2933 2934 2935 2936
Handle<JSFunction> InstallFunc(
    Isolate* isolate, Handle<JSObject> object, const char* str,
    FunctionCallback func, int length, bool has_prototype = false,
    PropertyAttributes attributes = NONE,
    SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
2937
  Handle<String> name = v8_str(isolate, str);
2938 2939
  Handle<JSFunction> function =
      CreateFunc(isolate, name, func, has_prototype, side_effect_type);
2940
  function->shared().set_length(length);
2941
  JSObject::AddProperty(isolate, object, name, function, attributes);
rossberg's avatar
rossberg committed
2942
  return function;
2943 2944
}

2945
Handle<JSFunction> InstallConstructorFunc(Isolate* isolate,
2946 2947 2948
                                          Handle<JSObject> object,
                                          const char* str,
                                          FunctionCallback func) {
2949 2950
  return InstallFunc(isolate, object, str, func, 1, true, DONT_ENUM,
                     SideEffectType::kHasNoSideEffect);
2951 2952
}

2953
Handle<String> GetterName(Isolate* isolate, Handle<String> name) {
2954
  return Name::ToFunctionName(isolate, name, isolate->factory()->get_string())
2955 2956 2957
      .ToHandleChecked();
}

2958 2959
void InstallGetter(Isolate* isolate, Handle<JSObject> object, const char* str,
                   FunctionCallback func) {
2960 2961
  Handle<String> name = v8_str(isolate, str);
  Handle<JSFunction> function =
2962 2963
      CreateFunc(isolate, GetterName(isolate, name), func, false,
                 SideEffectType::kHasNoSideEffect);
2964

2965 2966
  Utils::ToLocal(object)->SetAccessorProperty(Utils::ToLocal(name),
                                              Utils::ToLocal(function),
2967
                                              Local<Function>(), v8::None);
2968 2969
}

2970
Handle<String> SetterName(Isolate* isolate, Handle<String> name) {
2971
  return Name::ToFunctionName(isolate, name, isolate->factory()->set_string())
2972 2973 2974 2975 2976 2977 2978 2979
      .ToHandleChecked();
}

void InstallGetterSetter(Isolate* isolate, Handle<JSObject> object,
                         const char* str, FunctionCallback getter,
                         FunctionCallback setter) {
  Handle<String> name = v8_str(isolate, str);
  Handle<JSFunction> getter_func =
2980 2981
      CreateFunc(isolate, GetterName(isolate, name), getter, false,
                 SideEffectType::kHasNoSideEffect);
2982
  Handle<JSFunction> setter_func =
2983
      CreateFunc(isolate, SetterName(isolate, name), setter, false);
2984
  setter_func->shared().set_length(1);
2985 2986 2987

  Utils::ToLocal(object)->SetAccessorProperty(
      Utils::ToLocal(name), Utils::ToLocal(getter_func),
2988
      Utils::ToLocal(setter_func), v8::None);
2989 2990
}

2991 2992 2993 2994 2995 2996
// Assigns a dummy instance template to the given constructor function. Used to
// make sure the implicit receivers for the constructors in this file have an
// instance type different from the internal one, they allocate the resulting
// object explicitly and ignore implicit receiver.
void SetDummyInstanceTemplate(Isolate* isolate, Handle<JSFunction> fun) {
  Handle<ObjectTemplateInfo> instance_template = NewObjectTemplate(isolate);
2997
  FunctionTemplateInfo::SetInstanceTemplate(
2998
      isolate, handle(fun->shared().get_api_func_data(), isolate),
2999
      instance_template);
3000 3001
}

3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021
Handle<JSObject> SetupConstructor(Isolate* isolate,
                                  Handle<JSFunction> constructor,
                                  InstanceType instance_type, int instance_size,
                                  const char* name = nullptr) {
  SetDummyInstanceTemplate(isolate, constructor);
  JSFunction::EnsureHasInitialMap(constructor);
  Handle<JSObject> proto(JSObject::cast(constructor->instance_prototype()),
                         isolate);
  Handle<Map> map = isolate->factory()->NewMap(instance_type, instance_size);
  JSFunction::SetInitialMap(isolate, constructor, map, proto);
  constexpr PropertyAttributes ro_attributes =
      static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
  if (name) {
    JSObject::AddProperty(isolate, proto,
                          isolate->factory()->to_string_tag_symbol(),
                          v8_str(isolate, name), ro_attributes);
  }
  return proto;
}

3022
// static
3023
void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
3024 3025
  Handle<JSGlobalObject> global = isolate->global_object();
  Handle<Context> context(global->native_context(), isolate);
3026
  // Install the JS API once only.
3027
  Object prev = context->get(Context::WASM_MODULE_CONSTRUCTOR_INDEX);
3028 3029
  if (!prev.IsUndefined(isolate)) {
    DCHECK(prev.IsJSFunction());
3030 3031
    return;
  }
3032

3033 3034
  Factory* factory = isolate->factory();

3035
  // Setup WebAssembly
3036
  Handle<String> name = v8_str(isolate, "WebAssembly");
3037 3038
  // Not supposed to be called, hence using the kIllegal builtin as code.
  Handle<SharedFunctionInfo> info =
3039
      factory->NewSharedFunctionInfoForBuiltin(name, Builtin::kIllegal);
3040 3041
  info->set_language_mode(LanguageMode::kStrict);

3042 3043
  Handle<JSFunction> cons =
      Factory::JSFunctionBuilder{isolate, info, context}.Build();
3044
  JSFunction::SetPrototype(cons, isolate->initial_object_prototype());
3045 3046
  Handle<JSObject> webassembly =
      factory->NewJSObject(cons, AllocationType::kOld);
3047

3048 3049
  PropertyAttributes ro_attributes =
      static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
3050 3051
  JSObject::AddProperty(isolate, webassembly, factory->to_string_tag_symbol(),
                        name, ro_attributes);
3052 3053
  InstallFunc(isolate, webassembly, "compile", WebAssemblyCompile, 1);
  InstallFunc(isolate, webassembly, "validate", WebAssemblyValidate, 1);
3054
  InstallFunc(isolate, webassembly, "instantiate", WebAssemblyInstantiate, 1);
3055

3056 3057
  // TODO(tebbi): Put this behind its own flag once --wasm-gc-js-interop gets
  // closer to shipping.
3058
  if (v8_flags.wasm_gc_js_interop) {
3059 3060 3061 3062 3063 3064 3065 3066
    SimpleInstallFunction(
        isolate, webassembly, "experimentalConvertArrayToString",
        Builtin::kExperimentalWasmConvertArrayToString, 0, true);
    SimpleInstallFunction(
        isolate, webassembly, "experimentalConvertStringToArray",
        Builtin::kExperimentalWasmConvertStringToArray, 0, true);
  }

3067
  if (v8_flags.wasm_test_streaming) {
3068
    isolate->set_wasm_streaming_callback(WasmStreamingCallbackForTesting);
3069 3070
  }

3071
  if (isolate->wasm_streaming_callback() != nullptr) {
3072 3073 3074 3075 3076
    InstallFunc(isolate, webassembly, "compileStreaming",
                WebAssemblyCompileStreaming, 1);
    InstallFunc(isolate, webassembly, "instantiateStreaming",
                WebAssemblyInstantiateStreaming, 1);
  }
3077

3078 3079
  // Expose the API on the global object if configured to do so.
  if (exposed_on_global_object) {
3080
    JSObject::AddProperty(isolate, global, name, webassembly, DONT_ENUM);
3081 3082
  }

3083 3084
  // Setup Module
  Handle<JSFunction> module_constructor =
3085
      InstallConstructorFunc(isolate, webassembly, "Module", WebAssemblyModule);
3086 3087
  SetupConstructor(isolate, module_constructor, i::WASM_MODULE_OBJECT_TYPE,
                   WasmModuleObject::kHeaderSize, "WebAssembly.Module");
3088
  context->set_wasm_module_constructor(*module_constructor);
3089
  InstallFunc(isolate, module_constructor, "imports", WebAssemblyModuleImports,
3090
              1, false, NONE, SideEffectType::kHasNoSideEffect);
3091
  InstallFunc(isolate, module_constructor, "exports", WebAssemblyModuleExports,
3092
              1, false, NONE, SideEffectType::kHasNoSideEffect);
3093
  InstallFunc(isolate, module_constructor, "customSections",
3094 3095
              WebAssemblyModuleCustomSections, 2, false, NONE,
              SideEffectType::kHasNoSideEffect);
3096 3097

  // Setup Instance
3098 3099
  Handle<JSFunction> instance_constructor = InstallConstructorFunc(
      isolate, webassembly, "Instance", WebAssemblyInstance);
3100 3101 3102
  Handle<JSObject> instance_proto = SetupConstructor(
      isolate, instance_constructor, i::WASM_INSTANCE_OBJECT_TYPE,
      WasmInstanceObject::kHeaderSize, "WebAssembly.Instance");
3103
  context->set_wasm_instance_constructor(*instance_constructor);
3104 3105
  InstallGetter(isolate, instance_proto, "exports",
                WebAssemblyInstanceGetExports);
3106

3107
  // The context is not set up completely yet. That's why we cannot use
3108 3109 3110
  // {WasmFeatures::FromIsolate} and have to use {WasmFeatures::FromFlags}
  // instead.
  auto enabled_features = i::wasm::WasmFeatures::FromFlags();
3111

3112 3113
  // Setup Table
  Handle<JSFunction> table_constructor =
3114
      InstallConstructorFunc(isolate, webassembly, "Table", WebAssemblyTable);
3115 3116 3117
  Handle<JSObject> table_proto =
      SetupConstructor(isolate, table_constructor, i::WASM_TABLE_OBJECT_TYPE,
                       WasmTableObject::kHeaderSize, "WebAssembly.Table");
3118 3119
  context->set_wasm_table_constructor(*table_constructor);
  InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength);
3120
  InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow, 1);
3121
  InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet, 1);
3122 3123
  InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet, 1, false, NONE,
              SideEffectType::kHasNoSideEffect);
3124
  if (enabled_features.has_type_reflection()) {
3125 3126
    InstallFunc(isolate, table_proto, "type", WebAssemblyTableType, 0, false,
                NONE, SideEffectType::kHasNoSideEffect);
3127
  }
3128 3129 3130

  // Setup Memory
  Handle<JSFunction> memory_constructor =
3131
      InstallConstructorFunc(isolate, webassembly, "Memory", WebAssemblyMemory);
3132 3133 3134
  Handle<JSObject> memory_proto =
      SetupConstructor(isolate, memory_constructor, i::WASM_MEMORY_OBJECT_TYPE,
                       WasmMemoryObject::kHeaderSize, "WebAssembly.Memory");
3135
  context->set_wasm_memory_constructor(*memory_constructor);
3136
  InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow, 1);
3137
  InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer);
3138
  if (enabled_features.has_type_reflection()) {
3139 3140
    InstallFunc(isolate, memory_proto, "type", WebAssemblyMemoryType, 0, false,
                NONE, SideEffectType::kHasNoSideEffect);
3141
  }
3142

3143
  // Setup Global
3144 3145
  Handle<JSFunction> global_constructor =
      InstallConstructorFunc(isolate, webassembly, "Global", WebAssemblyGlobal);
3146 3147 3148
  Handle<JSObject> global_proto =
      SetupConstructor(isolate, global_constructor, i::WASM_GLOBAL_OBJECT_TYPE,
                       WasmGlobalObject::kHeaderSize, "WebAssembly.Global");
3149
  context->set_wasm_global_constructor(*global_constructor);
3150 3151
  InstallFunc(isolate, global_proto, "valueOf", WebAssemblyGlobalValueOf, 0,
              false, NONE, SideEffectType::kHasNoSideEffect);
3152 3153
  InstallGetterSetter(isolate, global_proto, "value", WebAssemblyGlobalGetValue,
                      WebAssemblyGlobalSetValue);
3154
  if (enabled_features.has_type_reflection()) {
3155 3156
    InstallFunc(isolate, global_proto, "type", WebAssemblyGlobalType, 0, false,
                NONE, SideEffectType::kHasNoSideEffect);
3157
  }
3158

3159
  // Setup Exception
3160
  if (enabled_features.has_eh()) {
3161
    Handle<JSFunction> tag_constructor =
3162
        InstallConstructorFunc(isolate, webassembly, "Tag", WebAssemblyTag);
3163 3164 3165
    Handle<JSObject> tag_proto =
        SetupConstructor(isolate, tag_constructor, i::WASM_TAG_OBJECT_TYPE,
                         WasmTagObject::kHeaderSize, "WebAssembly.Tag");
3166
    context->set_wasm_tag_constructor(*tag_constructor);
3167 3168

    if (enabled_features.has_type_reflection()) {
3169
      InstallFunc(isolate, tag_proto, "type", WebAssemblyTagType, 0);
3170
    }
3171
    // Set up runtime exception constructor.
3172
    Handle<JSFunction> exception_constructor = InstallConstructorFunc(
3173
        isolate, webassembly, "Exception", WebAssemblyException);
3174
    SetDummyInstanceTemplate(isolate, exception_constructor);
3175 3176 3177
    Handle<JSObject> exception_proto = SetupConstructor(
        isolate, exception_constructor, i::WASM_EXCEPTION_PACKAGE_TYPE,
        WasmExceptionPackage::kHeaderSize, "WebAssembly.Exception");
3178 3179
    InstallFunc(isolate, exception_proto, "getArg", WebAssemblyExceptionGetArg,
                2);
3180
    InstallFunc(isolate, exception_proto, "is", WebAssemblyExceptionIs, 1);
3181
    context->set_wasm_exception_constructor(*exception_constructor);
3182
  }
3183

3184 3185 3186 3187 3188
  // Setup Suspender.
  if (enabled_features.has_stack_switching()) {
    Handle<JSFunction> suspender_constructor = InstallConstructorFunc(
        isolate, webassembly, "Suspender", WebAssemblySuspender);
    context->set_wasm_suspender_constructor(*suspender_constructor);
3189 3190 3191
    SetupConstructor(isolate, suspender_constructor,
                     i::WASM_SUSPENDER_OBJECT_TYPE,
                     WasmSuspenderObject::kHeaderSize, "WebAssembly.Suspender");
3192 3193
  }

3194
  // Setup Function
3195
  if (enabled_features.has_type_reflection()) {
3196 3197 3198 3199 3200 3201 3202 3203 3204
    Handle<JSFunction> function_constructor = InstallConstructorFunc(
        isolate, webassembly, "Function", WebAssemblyFunction);
    SetDummyInstanceTemplate(isolate, function_constructor);
    JSFunction::EnsureHasInitialMap(function_constructor);
    Handle<JSObject> function_proto(
        JSObject::cast(function_constructor->instance_prototype()), isolate);
    Handle<Map> function_map = isolate->factory()->CreateSloppyFunctionMap(
        FUNCTION_WITHOUT_PROTOTYPE, MaybeHandle<JSFunction>());
    CHECK(JSObject::SetPrototype(
3205
              isolate, function_proto,
3206
              handle(context->function_function().prototype(), isolate), false,
3207 3208
              kDontThrow)
              .FromJust());
3209
    JSFunction::SetInitialMap(isolate, function_constructor, function_map,
3210
                              function_proto);
3211 3212
    InstallFunc(isolate, function_constructor, "type", WebAssemblyFunctionType,
                1);
3213 3214 3215 3216 3217 3218 3219 3220
    // Make all exported functions an instance of {WebAssembly.Function}.
    context->set_wasm_exported_function_map(*function_map);
  } else {
    // Make all exported functions an instance of {Function}.
    Handle<Map> function_map = isolate->sloppy_function_without_prototype_map();
    context->set_wasm_exported_function_map(*function_map);
  }

3221 3222
  // Setup errors
  Handle<JSFunction> compile_error(
3223
      isolate->native_context()->wasm_compile_error_function(), isolate);
3224 3225
  JSObject::AddProperty(isolate, webassembly,
                        isolate->factory()->CompileError_string(),
3226
                        compile_error, DONT_ENUM);
3227
  Handle<JSFunction> link_error(
3228
      isolate->native_context()->wasm_link_error_function(), isolate);
3229 3230
  JSObject::AddProperty(isolate, webassembly,
                        isolate->factory()->LinkError_string(), link_error,
3231
                        DONT_ENUM);
3232
  Handle<JSFunction> runtime_error(
3233
      isolate->native_context()->wasm_runtime_error_function(), isolate);
3234 3235
  JSObject::AddProperty(isolate, webassembly,
                        isolate->factory()->RuntimeError_string(),
3236
                        runtime_error, DONT_ENUM);
3237
}
3238

3239 3240 3241 3242
// static
void WasmJs::InstallConditionalFeatures(Isolate* isolate,
                                        Handle<Context> context) {
  // Exception handling may have been enabled by an origin trial. If so, make
3243
  // sure that the {WebAssembly.Tag} constructor is set up.
3244 3245 3246 3247 3248
  auto enabled_features = i::wasm::WasmFeatures::FromContext(isolate, context);
  if (enabled_features.has_eh()) {
    Handle<JSGlobalObject> global = handle(context->global_object(), isolate);
    MaybeHandle<Object> maybe_webassembly =
        JSObject::GetProperty(isolate, global, "WebAssembly");
3249
    Handle<Object> webassembly_obj;
3250 3251 3252 3253
    if (!maybe_webassembly.ToHandle(&webassembly_obj) ||
        !webassembly_obj->IsJSObject()) {
      // There is no {WebAssembly} object, or it's not what we expect.
      // Just return without adding the {Tag} constructor.
3254 3255 3256
      return;
    }
    Handle<JSObject> webassembly = Handle<JSObject>::cast(webassembly_obj);
3257
    // Setup Tag.
3258
    Handle<String> tag_name = v8_str(isolate, "Tag");
3259 3260 3261 3262 3263 3264 3265
    // The {WebAssembly} object may already have been modified. The following
    // code is designed to:
    //  - check for existing {Tag} properties on the object itself, and avoid
    //    overwriting them or adding duplicate properties
    //  - disregard any setters or read-only properties on the prototype chain
    //  - only make objects accessible to user code after all internal setup
    //    has been completed.
3266 3267
    if (JSObject::HasOwnProperty(isolate, webassembly, tag_name)
            .FromMaybe(true)) {
3268
      // Existing property, or exception.
3269
      return;
3270
    }
3271 3272

    bool has_prototype = true;
3273 3274
    Handle<JSFunction> tag_constructor =
        CreateFunc(isolate, tag_name, WebAssemblyTag, has_prototype,
3275
                   SideEffectType::kHasNoSideEffect);
3276 3277
    tag_constructor->shared().set_length(1);
    context->set_wasm_tag_constructor(*tag_constructor);
3278 3279 3280
    Handle<JSObject> tag_proto =
        SetupConstructor(isolate, tag_constructor, i::WASM_TAG_OBJECT_TYPE,
                         WasmTagObject::kHeaderSize, "WebAssembly.Tag");
3281
    if (enabled_features.has_type_reflection()) {
3282
      InstallFunc(isolate, tag_proto, "type", WebAssemblyTagType, 0);
3283
    }
3284 3285 3286 3287 3288 3289
    LookupIterator it(isolate, webassembly, tag_name, LookupIterator::OWN);
    Maybe<bool> result = JSObject::DefineOwnPropertyIgnoreAttributes(
        &it, tag_constructor, DONT_ENUM, Just(kDontThrow));
    // This could still fail if the object was non-extensible, but now we
    // return anyway so there's no need to even check.
    USE(result);
3290 3291
  }
}
3292 3293 3294
#undef ASSIGN
#undef EXTRACT_THIS

3295 3296
}  // namespace internal
}  // namespace v8