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