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-objects.h" 6 7#include "src/base/iterator.h" 8#include "src/base/vector.h" 9#include "src/codegen/assembler-inl.h" 10#include "src/codegen/code-factory.h" 11#include "src/compiler/wasm-compiler.h" 12#include "src/debug/debug-interface.h" 13#include "src/logging/counters.h" 14#include "src/objects/debug-objects-inl.h" 15#include "src/objects/managed-inl.h" 16#include "src/objects/objects-inl.h" 17#include "src/objects/shared-function-info.h" 18#include "src/objects/struct-inl.h" 19#include "src/trap-handler/trap-handler.h" 20#include "src/utils/utils.h" 21#include "src/wasm/code-space-access.h" 22#include "src/wasm/jump-table-assembler.h" 23#include "src/wasm/module-compiler.h" 24#include "src/wasm/module-decoder.h" 25#include "src/wasm/module-instantiate.h" 26#include "src/wasm/value-type.h" 27#include "src/wasm/wasm-code-manager.h" 28#include "src/wasm/wasm-engine.h" 29#include "src/wasm/wasm-limits.h" 30#include "src/wasm/wasm-module.h" 31#include "src/wasm/wasm-objects-inl.h" 32#include "src/wasm/wasm-subtyping.h" 33#include "src/wasm/wasm-value.h" 34 35#define TRACE_IFT(...) \ 36 do { \ 37 if (false) PrintF(__VA_ARGS__); \ 38 } while (false) 39 40namespace v8 { 41namespace internal { 42 43// Import a few often used types from the wasm namespace. 44using WasmFunction = wasm::WasmFunction; 45using WasmModule = wasm::WasmModule; 46 47namespace { 48 49// Manages the natively-allocated memory for a WasmInstanceObject. Since 50// an instance finalizer is not guaranteed to run upon isolate shutdown, 51// we must use a Managed<WasmInstanceNativeAllocations> to guarantee 52// it is freed. 53class WasmInstanceNativeAllocations { 54 public: 55 WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance, 56 size_t num_imported_functions, 57 size_t num_imported_mutable_globals, 58 size_t num_data_segments, 59 size_t num_elem_segments) 60 : imported_function_targets_(new Address[num_imported_functions]), 61 imported_mutable_globals_(new Address[num_imported_mutable_globals]), 62 data_segment_starts_(new Address[num_data_segments]), 63 data_segment_sizes_(new uint32_t[num_data_segments]), 64 dropped_elem_segments_(new uint8_t[num_elem_segments]) { 65 instance->set_imported_function_targets(imported_function_targets_.get()); 66 instance->set_imported_mutable_globals(imported_mutable_globals_.get()); 67 instance->set_data_segment_starts(data_segment_starts_.get()); 68 instance->set_data_segment_sizes(data_segment_sizes_.get()); 69 instance->set_dropped_elem_segments(dropped_elem_segments_.get()); 70 } 71 72 private: 73 const std::unique_ptr<Address[]> imported_function_targets_; 74 const std::unique_ptr<Address[]> imported_mutable_globals_; 75 const std::unique_ptr<Address[]> data_segment_starts_; 76 const std::unique_ptr<uint32_t[]> data_segment_sizes_; 77 const std::unique_ptr<uint8_t[]> dropped_elem_segments_; 78}; 79 80size_t EstimateNativeAllocationsSize(const WasmModule* module) { 81 size_t estimate = 82 sizeof(WasmInstanceNativeAllocations) + 83 (1 * kSystemPointerSize * module->num_imported_mutable_globals) + 84 (2 * kSystemPointerSize * module->num_imported_functions) + 85 ((kSystemPointerSize + sizeof(uint32_t) + sizeof(uint8_t)) * 86 module->num_declared_data_segments); 87 return estimate; 88} 89 90enum DispatchTableElements : int { 91 kDispatchTableInstanceOffset, 92 kDispatchTableIndexOffset, 93 // Marker: 94 kDispatchTableNumElements 95}; 96 97} // namespace 98 99// static 100Handle<WasmModuleObject> WasmModuleObject::New( 101 Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, 102 Handle<Script> script) { 103 Handle<FixedArray> export_wrappers = isolate->factory()->NewFixedArray(0); 104 return New(isolate, std::move(native_module), script, export_wrappers); 105} 106 107// static 108Handle<WasmModuleObject> WasmModuleObject::New( 109 Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, 110 Handle<Script> script, Handle<FixedArray> export_wrappers) { 111 Handle<Managed<wasm::NativeModule>> managed_native_module; 112 if (script->type() == Script::TYPE_WASM) { 113 managed_native_module = handle( 114 Managed<wasm::NativeModule>::cast(script->wasm_managed_native_module()), 115 isolate); 116 } else { 117 const WasmModule* module = native_module->module(); 118 size_t memory_estimate = 119 native_module->committed_code_space() + 120 wasm::WasmCodeManager::EstimateNativeModuleMetaDataSize(module); 121 managed_native_module = Managed<wasm::NativeModule>::FromSharedPtr( 122 isolate, memory_estimate, std::move(native_module)); 123 } 124 Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast( 125 isolate->factory()->NewJSObject(isolate->wasm_module_constructor())); 126 module_object->set_export_wrappers(*export_wrappers); 127 module_object->set_managed_native_module(*managed_native_module); 128 module_object->set_script(*script); 129 return module_object; 130} 131 132Handle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes( 133 Isolate* isolate, Handle<WasmModuleObject> module_object, 134 wasm::WireBytesRef ref, InternalizeString internalize) { 135 base::Vector<const uint8_t> wire_bytes = 136 module_object->native_module()->wire_bytes(); 137 return ExtractUtf8StringFromModuleBytes(isolate, wire_bytes, ref, 138 internalize); 139} 140 141Handle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes( 142 Isolate* isolate, base::Vector<const uint8_t> wire_bytes, 143 wasm::WireBytesRef ref, InternalizeString internalize) { 144 base::Vector<const uint8_t> name_vec = 145 wire_bytes.SubVector(ref.offset(), ref.end_offset()); 146 // UTF8 validation happens at decode time. 147 DCHECK(unibrow::Utf8::ValidateEncoding(name_vec.begin(), name_vec.length())); 148 auto* factory = isolate->factory(); 149 return internalize 150 ? factory->InternalizeUtf8String( 151 base::Vector<const char>::cast(name_vec)) 152 : factory 153 ->NewStringFromUtf8(base::Vector<const char>::cast(name_vec)) 154 .ToHandleChecked(); 155} 156 157MaybeHandle<String> WasmModuleObject::GetModuleNameOrNull( 158 Isolate* isolate, Handle<WasmModuleObject> module_object) { 159 const WasmModule* module = module_object->module(); 160 if (!module->name.is_set()) return {}; 161 return ExtractUtf8StringFromModuleBytes(isolate, module_object, module->name, 162 kNoInternalize); 163} 164 165MaybeHandle<String> WasmModuleObject::GetFunctionNameOrNull( 166 Isolate* isolate, Handle<WasmModuleObject> module_object, 167 uint32_t func_index) { 168 DCHECK_LT(func_index, module_object->module()->functions.size()); 169 wasm::WireBytesRef name = 170 module_object->module()->lazily_generated_names.LookupFunctionName( 171 wasm::ModuleWireBytes(module_object->native_module()->wire_bytes()), 172 func_index); 173 if (!name.is_set()) return {}; 174 return ExtractUtf8StringFromModuleBytes(isolate, module_object, name, 175 kNoInternalize); 176} 177 178base::Vector<const uint8_t> WasmModuleObject::GetRawFunctionName( 179 int func_index) { 180 if (func_index == wasm::kAnonymousFuncIndex) { 181 return base::Vector<const uint8_t>({nullptr, 0}); 182 } 183 DCHECK_GT(module()->functions.size(), func_index); 184 wasm::ModuleWireBytes wire_bytes(native_module()->wire_bytes()); 185 wasm::WireBytesRef name_ref = 186 module()->lazily_generated_names.LookupFunctionName(wire_bytes, 187 func_index); 188 wasm::WasmName name = wire_bytes.GetNameOrNull(name_ref); 189 return base::Vector<const uint8_t>::cast(name); 190} 191 192Handle<WasmTableObject> WasmTableObject::New( 193 Isolate* isolate, Handle<WasmInstanceObject> instance, wasm::ValueType type, 194 uint32_t initial, bool has_maximum, uint32_t maximum, 195 Handle<FixedArray>* entries, Handle<Object> initial_value) { 196 // TODO(7748): Make this work with other types when spec clears up. 197 { 198 const WasmModule* module = 199 instance.is_null() ? nullptr : instance->module(); 200 CHECK(wasm::WasmTable::IsValidTableType(type, module)); 201 } 202 203 Handle<FixedArray> backing_store = isolate->factory()->NewFixedArray(initial); 204 for (int i = 0; i < static_cast<int>(initial); ++i) { 205 backing_store->set(i, *initial_value); 206 } 207 208 Handle<Object> max; 209 if (has_maximum) { 210 max = isolate->factory()->NewNumberFromUint(maximum); 211 } else { 212 max = isolate->factory()->undefined_value(); 213 } 214 215 Handle<JSFunction> table_ctor( 216 isolate->native_context()->wasm_table_constructor(), isolate); 217 auto table_obj = Handle<WasmTableObject>::cast( 218 isolate->factory()->NewJSObject(table_ctor)); 219 DisallowGarbageCollection no_gc; 220 221 if (!instance.is_null()) table_obj->set_instance(*instance); 222 table_obj->set_entries(*backing_store); 223 table_obj->set_current_length(initial); 224 table_obj->set_maximum_length(*max); 225 table_obj->set_raw_type(static_cast<int>(type.raw_bit_field())); 226 227 table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array()); 228 if (entries != nullptr) { 229 *entries = backing_store; 230 } 231 return Handle<WasmTableObject>::cast(table_obj); 232} 233 234void WasmTableObject::AddDispatchTable(Isolate* isolate, 235 Handle<WasmTableObject> table_obj, 236 Handle<WasmInstanceObject> instance, 237 int table_index) { 238 Handle<FixedArray> dispatch_tables(table_obj->dispatch_tables(), isolate); 239 int old_length = dispatch_tables->length(); 240 DCHECK_EQ(0, old_length % kDispatchTableNumElements); 241 242 if (instance.is_null()) return; 243 // TODO(titzer): use weak cells here to avoid leaking instances. 244 245 // Grow the dispatch table and add a new entry at the end. 246 Handle<FixedArray> new_dispatch_tables = 247 isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 248 kDispatchTableNumElements); 249 250 new_dispatch_tables->set(old_length + kDispatchTableInstanceOffset, 251 *instance); 252 new_dispatch_tables->set(old_length + kDispatchTableIndexOffset, 253 Smi::FromInt(table_index)); 254 255 table_obj->set_dispatch_tables(*new_dispatch_tables); 256} 257 258int WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table, 259 uint32_t count, Handle<Object> init_value) { 260 uint32_t old_size = table->current_length(); 261 if (count == 0) return old_size; // Degenerate case: nothing to do. 262 263 // Check if growing by {count} is valid. 264 uint32_t max_size; 265 if (!table->maximum_length().ToUint32(&max_size)) { 266 max_size = FLAG_wasm_max_table_size; 267 } 268 max_size = std::min(max_size, FLAG_wasm_max_table_size); 269 DCHECK_LE(old_size, max_size); 270 if (max_size - old_size < count) return -1; 271 272 uint32_t new_size = old_size + count; 273 // Even with 2x over-allocation, there should not be an integer overflow. 274 STATIC_ASSERT(wasm::kV8MaxWasmTableSize <= kMaxInt / 2); 275 DCHECK_GE(kMaxInt, new_size); 276 int old_capacity = table->entries().length(); 277 if (new_size > static_cast<uint32_t>(old_capacity)) { 278 int grow = static_cast<int>(new_size) - old_capacity; 279 // Grow at least by the old capacity, to implement exponential growing. 280 grow = std::max(grow, old_capacity); 281 // Never grow larger than the max size. 282 grow = std::min(grow, static_cast<int>(max_size - old_capacity)); 283 auto new_store = isolate->factory()->CopyFixedArrayAndGrow( 284 handle(table->entries(), isolate), grow); 285 table->set_entries(*new_store, WriteBarrierMode::UPDATE_WRITE_BARRIER); 286 } 287 table->set_current_length(new_size); 288 289 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate); 290 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); 291 // Tables are stored in the instance object, no code patching is 292 // necessary. We simply have to grow the raw tables in each instance 293 // that has imported this table. 294 295 // TODO(titzer): replace the dispatch table with a weak list of all 296 // the instances that import a given table. 297 for (int i = 0; i < dispatch_tables->length(); 298 i += kDispatchTableNumElements) { 299 int table_index = 300 Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value(); 301 302 Handle<WasmInstanceObject> instance( 303 WasmInstanceObject::cast(dispatch_tables->get(i)), isolate); 304 305 DCHECK_EQ(old_size, 306 instance->GetIndirectFunctionTable(isolate, table_index)->size()); 307 WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( 308 instance, table_index, new_size); 309 } 310 311 for (uint32_t entry = old_size; entry < new_size; ++entry) { 312 WasmTableObject::Set(isolate, table, entry, init_value); 313 } 314 return old_size; 315} 316 317bool WasmTableObject::IsInBounds(Isolate* isolate, 318 Handle<WasmTableObject> table, 319 uint32_t entry_index) { 320 return entry_index < static_cast<uint32_t>(table->current_length()); 321} 322 323bool WasmTableObject::IsValidElement(Isolate* isolate, 324 Handle<WasmTableObject> table, 325 Handle<Object> entry) { 326 const char* error_message; 327 const WasmModule* module = 328 !table->instance().IsUndefined() 329 ? WasmInstanceObject::cast(table->instance()).module() 330 : nullptr; 331 if (entry->IsWasmInternalFunction()) { 332 entry = 333 handle(Handle<WasmInternalFunction>::cast(entry)->external(), isolate); 334 } 335 return wasm::TypecheckJSObject(isolate, module, entry, table->type(), 336 &error_message); 337} 338 339void WasmTableObject::SetFunctionTableEntry(Isolate* isolate, 340 Handle<WasmTableObject> table, 341 Handle<FixedArray> entries, 342 int entry_index, 343 Handle<Object> entry) { 344 if (entry->IsNull(isolate)) { 345 ClearDispatchTables(isolate, table, entry_index); // Degenerate case. 346 entries->set(entry_index, ReadOnlyRoots(isolate).null_value()); 347 return; 348 } 349 350 Handle<Object> external = 351 handle(Handle<WasmInternalFunction>::cast(entry)->external(), isolate); 352 353 if (WasmExportedFunction::IsWasmExportedFunction(*external)) { 354 auto exported_function = Handle<WasmExportedFunction>::cast(external); 355 Handle<WasmInstanceObject> target_instance(exported_function->instance(), 356 isolate); 357 int func_index = exported_function->function_index(); 358 auto* wasm_function = &target_instance->module()->functions[func_index]; 359 UpdateDispatchTables(isolate, *table, entry_index, wasm_function, 360 *target_instance); 361 } else if (WasmJSFunction::IsWasmJSFunction(*external)) { 362 UpdateDispatchTables(isolate, table, entry_index, 363 Handle<WasmJSFunction>::cast(external)); 364 } else { 365 DCHECK(WasmCapiFunction::IsWasmCapiFunction(*external)); 366 UpdateDispatchTables(isolate, table, entry_index, 367 Handle<WasmCapiFunction>::cast(external)); 368 } 369 entries->set(entry_index, *entry); 370} 371 372void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table, 373 uint32_t index, Handle<Object> entry) { 374 // Callers need to perform bounds checks, type check, and error handling. 375 DCHECK(IsInBounds(isolate, table, index)); 376 DCHECK(IsValidElement(isolate, table, entry)); 377 378 Handle<FixedArray> entries(table->entries(), isolate); 379 // The FixedArray is addressed with int's. 380 int entry_index = static_cast<int>(index); 381 382 switch (table->type().heap_representation()) { 383 case wasm::HeapType::kAny: 384 entries->set(entry_index, *entry); 385 return; 386 case wasm::HeapType::kFunc: 387 SetFunctionTableEntry(isolate, table, entries, entry_index, entry); 388 return; 389 case wasm::HeapType::kEq: 390 case wasm::HeapType::kData: 391 case wasm::HeapType::kArray: 392 case wasm::HeapType::kI31: 393 // TODO(7748): Implement once we have struct/arrays/i31ref tables. 394 UNREACHABLE(); 395 case wasm::HeapType::kBottom: 396 UNREACHABLE(); 397 default: 398 DCHECK(!table->instance().IsUndefined()); 399 // TODO(7748): Relax this once we have struct/array/i31ref tables. 400 DCHECK(WasmInstanceObject::cast(table->instance()) 401 .module() 402 ->has_signature(table->type().ref_index())); 403 SetFunctionTableEntry(isolate, table, entries, entry_index, entry); 404 return; 405 } 406} 407 408Handle<Object> WasmTableObject::Get(Isolate* isolate, 409 Handle<WasmTableObject> table, 410 uint32_t index) { 411 Handle<FixedArray> entries(table->entries(), isolate); 412 // Callers need to perform bounds checks and error handling. 413 DCHECK(IsInBounds(isolate, table, index)); 414 415 // The FixedArray is addressed with int's. 416 int entry_index = static_cast<int>(index); 417 418 Handle<Object> entry(entries->get(entry_index), isolate); 419 420 if (entry->IsNull(isolate)) { 421 return entry; 422 } 423 424 switch (table->type().heap_representation()) { 425 case wasm::HeapType::kAny: 426 return entry; 427 case wasm::HeapType::kFunc: 428 if (entry->IsWasmInternalFunction()) return entry; 429 break; 430 case wasm::HeapType::kEq: 431 case wasm::HeapType::kI31: 432 case wasm::HeapType::kData: 433 case wasm::HeapType::kArray: 434 // TODO(7748): Implement once we have a story for struct/arrays/i31ref in 435 // JS. 436 UNIMPLEMENTED(); 437 case wasm::HeapType::kBottom: 438 UNREACHABLE(); 439 default: 440 DCHECK(!table->instance().IsUndefined()); 441 // TODO(7748): Relax this once we have struct/array/i31ref tables. 442 DCHECK(WasmInstanceObject::cast(table->instance()) 443 .module() 444 ->has_signature(table->type().ref_index())); 445 if (entry->IsWasmInternalFunction()) return entry; 446 break; 447 } 448 449 // {entry} is not a valid entry in the table. It has to be a placeholder 450 // for lazy initialization. 451 Handle<Tuple2> tuple = Handle<Tuple2>::cast(entry); 452 auto instance = handle(WasmInstanceObject::cast(tuple->value1()), isolate); 453 int function_index = Smi::cast(tuple->value2()).value(); 454 455 // Check if we already compiled a wrapper for the function but did not store 456 // it in the table slot yet. 457 Handle<WasmInternalFunction> internal = 458 WasmInstanceObject::GetOrCreateWasmInternalFunction(isolate, instance, 459 function_index); 460 entries->set(entry_index, *internal); 461 return internal; 462} 463 464void WasmTableObject::Fill(Isolate* isolate, Handle<WasmTableObject> table, 465 uint32_t start, Handle<Object> entry, 466 uint32_t count) { 467 // Bounds checks must be done by the caller. 468 DCHECK_LE(start, table->current_length()); 469 DCHECK_LE(count, table->current_length()); 470 DCHECK_LE(start + count, table->current_length()); 471 472 for (uint32_t i = 0; i < count; i++) { 473 WasmTableObject::Set(isolate, table, start + i, entry); 474 } 475} 476 477void WasmTableObject::UpdateDispatchTables(Isolate* isolate, 478 WasmTableObject table, 479 int entry_index, 480 const wasm::WasmFunction* func, 481 WasmInstanceObject target_instance) { 482 DisallowGarbageCollection no_gc; 483 484 // We simply need to update the IFTs for each instance that imports 485 // this table. 486 FixedArray dispatch_tables = table.dispatch_tables(); 487 DCHECK_EQ(0, dispatch_tables.length() % kDispatchTableNumElements); 488 489 Object call_ref = 490 func->imported 491 // The function in the target instance was imported. Use its imports 492 // table, which contains a tuple needed by the import wrapper. 493 ? target_instance.imported_function_refs().get(func->func_index) 494 // For wasm functions, just pass the target instance. 495 : target_instance; 496 Address call_target = target_instance.GetCallTarget(func->func_index); 497 498 int original_sig_id = func->sig_index; 499 500 for (int i = 0, len = dispatch_tables.length(); i < len; 501 i += kDispatchTableNumElements) { 502 int table_index = 503 Smi::cast(dispatch_tables.get(i + kDispatchTableIndexOffset)).value(); 504 WasmInstanceObject instance = WasmInstanceObject::cast( 505 dispatch_tables.get(i + kDispatchTableInstanceOffset)); 506 const WasmModule* module = instance.module(); 507 // Try to avoid the signature map lookup by checking if the signature in 508 // {module} at {original_sig_id} matches {func->sig}. 509 int sig_id; 510 // TODO(7748): wasm-gc signatures cannot be canonicalized this way because 511 // references could wrongly be detected as identical. 512 if (module->has_signature(original_sig_id) && 513 *module->signature(original_sig_id) == *func->sig) { 514 sig_id = module->canonicalized_type_ids[original_sig_id]; 515 DCHECK_EQ(sig_id, module->signature_map.Find(*func->sig)); 516 } else { 517 // Note that {SignatureMap::Find} may return {-1} if the signature is 518 // not found; it will simply never match any check. 519 sig_id = module->signature_map.Find(*func->sig); 520 } 521 WasmIndirectFunctionTable ift = WasmIndirectFunctionTable::cast( 522 instance.indirect_function_tables().get(table_index)); 523 ift.Set(entry_index, sig_id, call_target, call_ref); 524 } 525} 526 527void WasmTableObject::UpdateDispatchTables(Isolate* isolate, 528 Handle<WasmTableObject> table, 529 int entry_index, 530 Handle<WasmJSFunction> function) { 531 // We simply need to update the IFTs for each instance that imports 532 // this table. 533 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate); 534 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); 535 536 for (int i = 0; i < dispatch_tables->length(); 537 i += kDispatchTableNumElements) { 538 int table_index = 539 Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value(); 540 Handle<WasmInstanceObject> instance( 541 WasmInstanceObject::cast( 542 dispatch_tables->get(i + kDispatchTableInstanceOffset)), 543 isolate); 544 WasmInstanceObject::ImportWasmJSFunctionIntoTable( 545 isolate, instance, table_index, entry_index, function); 546 } 547} 548 549void WasmTableObject::UpdateDispatchTables( 550 Isolate* isolate, Handle<WasmTableObject> table, int entry_index, 551 Handle<WasmCapiFunction> capi_function) { 552 // We simply need to update the IFTs for each instance that imports 553 // this table. 554 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate); 555 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); 556 557 // Reconstruct signature. 558 // TODO(jkummerow): Unify with "SignatureHelper" in c-api.cc. 559 PodArray<wasm::ValueType> serialized_sig = 560 capi_function->GetSerializedSignature(); 561 int total_count = serialized_sig.length() - 1; 562 std::unique_ptr<wasm::ValueType[]> reps(new wasm::ValueType[total_count]); 563 int result_count; 564 static const wasm::ValueType kMarker = wasm::kWasmVoid; 565 for (int i = 0, j = 0; i <= total_count; i++) { 566 if (serialized_sig.get(i) == kMarker) { 567 result_count = i; 568 continue; 569 } 570 reps[j++] = serialized_sig.get(i); 571 } 572 int param_count = total_count - result_count; 573 wasm::FunctionSig sig(result_count, param_count, reps.get()); 574 575 for (int i = 0; i < dispatch_tables->length(); 576 i += kDispatchTableNumElements) { 577 int table_index = 578 Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value(); 579 Handle<WasmInstanceObject> instance( 580 WasmInstanceObject::cast( 581 dispatch_tables->get(i + kDispatchTableInstanceOffset)), 582 isolate); 583 wasm::NativeModule* native_module = 584 instance->module_object().native_module(); 585 wasm::WasmImportWrapperCache* cache = native_module->import_wrapper_cache(); 586 auto kind = compiler::WasmImportCallKind::kWasmToCapi; 587 wasm::WasmCode* wasm_code = 588 cache->MaybeGet(kind, &sig, param_count, wasm::kNoSuspend); 589 if (wasm_code == nullptr) { 590 wasm::WasmCodeRefScope code_ref_scope; 591 wasm::WasmImportWrapperCache::ModificationScope cache_scope(cache); 592 wasm_code = compiler::CompileWasmCapiCallWrapper(native_module, &sig); 593 wasm::WasmImportWrapperCache::CacheKey key(kind, &sig, param_count, 594 wasm::kNoSuspend); 595 cache_scope[key] = wasm_code; 596 wasm_code->IncRef(); 597 isolate->counters()->wasm_generated_code_size()->Increment( 598 wasm_code->instructions().length()); 599 isolate->counters()->wasm_reloc_size()->Increment( 600 wasm_code->reloc_info().length()); 601 } 602 // Note that {SignatureMap::Find} may return {-1} if the signature is 603 // not found; it will simply never match any check. 604 auto sig_id = instance->module()->signature_map.Find(sig); 605 instance->GetIndirectFunctionTable(isolate, table_index) 606 ->Set(entry_index, sig_id, wasm_code->instruction_start(), 607 WasmCapiFunctionData::cast( 608 capi_function->shared().function_data(kAcquireLoad)) 609 .internal() 610 .ref()); 611 } 612} 613 614void WasmTableObject::ClearDispatchTables(Isolate* isolate, 615 Handle<WasmTableObject> table, 616 int index) { 617 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate); 618 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); 619 for (int i = 0; i < dispatch_tables->length(); 620 i += kDispatchTableNumElements) { 621 int table_index = 622 Smi::cast(dispatch_tables->get(i + kDispatchTableIndexOffset)).value(); 623 Handle<WasmInstanceObject> target_instance( 624 WasmInstanceObject::cast( 625 dispatch_tables->get(i + kDispatchTableInstanceOffset)), 626 isolate); 627 Handle<WasmIndirectFunctionTable> function_table = 628 target_instance->GetIndirectFunctionTable(isolate, table_index); 629 DCHECK_LT(index, function_table->size()); 630 function_table->Clear(index); 631 } 632} 633 634void WasmTableObject::SetFunctionTablePlaceholder( 635 Isolate* isolate, Handle<WasmTableObject> table, int entry_index, 636 Handle<WasmInstanceObject> instance, int func_index) { 637 // Put (instance, func_index) as a Tuple2 into the entry_index. 638 // The {WasmExportedFunction} will be created lazily. 639 // Allocate directly in old space as the tuples are typically long-lived, and 640 // we create many of them, which would result in lots of GC when initializing 641 // large tables. 642 Handle<Tuple2> tuple = isolate->factory()->NewTuple2( 643 instance, Handle<Smi>(Smi::FromInt(func_index), isolate), 644 AllocationType::kOld); 645 table->entries().set(entry_index, *tuple); 646} 647 648void WasmTableObject::GetFunctionTableEntry( 649 Isolate* isolate, const WasmModule* module, Handle<WasmTableObject> table, 650 int entry_index, bool* is_valid, bool* is_null, 651 MaybeHandle<WasmInstanceObject>* instance, int* function_index, 652 MaybeHandle<WasmJSFunction>* maybe_js_function) { 653 DCHECK(wasm::IsSubtypeOf(table->type(), wasm::kWasmFuncRef, module)); 654 DCHECK_LT(entry_index, table->current_length()); 655 // We initialize {is_valid} with {true}. We may change it later. 656 *is_valid = true; 657 Handle<Object> element(table->entries().get(entry_index), isolate); 658 659 *is_null = element->IsNull(isolate); 660 if (*is_null) return; 661 662 if (element->IsWasmInternalFunction()) { 663 element = handle(Handle<WasmInternalFunction>::cast(element)->external(), 664 isolate); 665 } 666 if (WasmExportedFunction::IsWasmExportedFunction(*element)) { 667 auto target_func = Handle<WasmExportedFunction>::cast(element); 668 *instance = handle(target_func->instance(), isolate); 669 *function_index = target_func->function_index(); 670 *maybe_js_function = MaybeHandle<WasmJSFunction>(); 671 return; 672 } 673 if (WasmJSFunction::IsWasmJSFunction(*element)) { 674 *instance = MaybeHandle<WasmInstanceObject>(); 675 *maybe_js_function = Handle<WasmJSFunction>::cast(element); 676 return; 677 } 678 if (element->IsTuple2()) { 679 auto tuple = Handle<Tuple2>::cast(element); 680 *instance = handle(WasmInstanceObject::cast(tuple->value1()), isolate); 681 *function_index = Smi::cast(tuple->value2()).value(); 682 *maybe_js_function = MaybeHandle<WasmJSFunction>(); 683 return; 684 } 685 *is_valid = false; 686} 687 688namespace { 689class IftNativeAllocations { 690 public: 691 IftNativeAllocations(Handle<WasmIndirectFunctionTable> table, uint32_t size) 692 : sig_ids_(size), targets_(size) { 693 table->set_sig_ids(sig_ids_.data()); 694 table->set_targets(targets_.data()); 695 } 696 697 static size_t SizeInMemory(uint32_t size) { 698 return size * (sizeof(Address) + sizeof(uint32_t)); 699 } 700 701 void resize(Handle<WasmIndirectFunctionTable> table, uint32_t new_size) { 702 DCHECK_GE(new_size, sig_ids_.size()); 703 DCHECK_EQ(this, Managed<IftNativeAllocations>::cast( 704 table->managed_native_allocations()) 705 .raw()); 706 sig_ids_.resize(new_size); 707 targets_.resize(new_size); 708 table->set_sig_ids(sig_ids_.data()); 709 table->set_targets(targets_.data()); 710 } 711 712 private: 713 std::vector<uint32_t> sig_ids_; 714 std::vector<Address> targets_; 715}; 716} // namespace 717 718Handle<WasmIndirectFunctionTable> WasmIndirectFunctionTable::New( 719 Isolate* isolate, uint32_t size) { 720 auto refs = isolate->factory()->NewFixedArray(static_cast<int>(size)); 721 auto table = Handle<WasmIndirectFunctionTable>::cast( 722 isolate->factory()->NewStruct(WASM_INDIRECT_FUNCTION_TABLE_TYPE)); 723 table->set_size(size); 724 table->set_refs(*refs); 725 auto native_allocations = Managed<IftNativeAllocations>::Allocate( 726 isolate, IftNativeAllocations::SizeInMemory(size), table, size); 727 table->set_managed_native_allocations(*native_allocations); 728 for (uint32_t i = 0; i < size; ++i) { 729 table->Clear(i); 730 } 731 return table; 732} 733void WasmIndirectFunctionTable::Set(uint32_t index, int sig_id, 734 Address call_target, Object ref) { 735 sig_ids()[index] = sig_id; 736 targets()[index] = call_target; 737 refs().set(index, ref); 738} 739 740void WasmIndirectFunctionTable::Clear(uint32_t index) { 741 sig_ids()[index] = -1; 742 targets()[index] = 0; 743 refs().set( 744 index, 745 ReadOnlyRoots(GetIsolateFromWritableObject(*this)).undefined_value()); 746} 747 748void WasmIndirectFunctionTable::Resize(Isolate* isolate, 749 Handle<WasmIndirectFunctionTable> table, 750 uint32_t new_size) { 751 uint32_t old_size = table->size(); 752 if (old_size >= new_size) return; // Nothing to do. 753 754 table->set_size(new_size); 755 756 // Grow table exponentially to guarantee amortized constant allocation and gc 757 // time. 758 Handle<FixedArray> old_refs(table->refs(), isolate); 759 // Since we might have overallocated, {old_capacity} might be different than 760 // {old_size}. 761 uint32_t old_capacity = old_refs->length(); 762 // If we have enough capacity, there is no need to reallocate. 763 if (new_size <= old_capacity) return; 764 uint32_t new_capacity = std::max(2 * old_capacity, new_size); 765 766 Managed<IftNativeAllocations>::cast(table->managed_native_allocations()) 767 .raw() 768 ->resize(table, new_capacity); 769 770 Handle<FixedArray> new_refs = isolate->factory()->CopyFixedArrayAndGrow( 771 old_refs, static_cast<int>(new_capacity - old_capacity)); 772 table->set_refs(*new_refs); 773 for (uint32_t i = old_capacity; i < new_capacity; ++i) { 774 table->Clear(i); 775 } 776} 777 778namespace { 779 780void SetInstanceMemory(Handle<WasmInstanceObject> instance, 781 Handle<JSArrayBuffer> buffer) { 782 bool is_wasm_module = instance->module()->origin == wasm::kWasmOrigin; 783 bool use_trap_handler = 784 instance->module_object().native_module()->bounds_checks() == 785 wasm::kTrapHandler; 786 // Wasm modules compiled to use the trap handler don't have bounds checks, 787 // so they must have a memory that has guard regions. 788 CHECK_IMPLIES(is_wasm_module && use_trap_handler, 789 buffer->GetBackingStore()->has_guard_regions()); 790 791 instance->SetRawMemory(reinterpret_cast<byte*>(buffer->backing_store()), 792 buffer->byte_length()); 793#if DEBUG 794 if (!FLAG_mock_arraybuffer_allocator) { 795 // To flush out bugs earlier, in DEBUG mode, check that all pages of the 796 // memory are accessible by reading and writing one byte on each page. 797 // Don't do this if the mock ArrayBuffer allocator is enabled. 798 byte* mem_start = instance->memory_start(); 799 size_t mem_size = instance->memory_size(); 800 for (size_t offset = 0; offset < mem_size; offset += wasm::kWasmPageSize) { 801 byte val = mem_start[offset]; 802 USE(val); 803 mem_start[offset] = val; 804 } 805 } 806#endif 807} 808} // namespace 809 810MaybeHandle<WasmMemoryObject> WasmMemoryObject::New( 811 Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer, int maximum) { 812 Handle<JSArrayBuffer> buffer; 813 if (!maybe_buffer.ToHandle(&buffer)) { 814 // If no buffer was provided, create a zero-length one. 815 auto backing_store = 816 BackingStore::AllocateWasmMemory(isolate, 0, 0, SharedFlag::kNotShared); 817 if (!backing_store) return {}; 818 buffer = isolate->factory()->NewJSArrayBuffer(std::move(backing_store)); 819 } 820 821 Handle<JSFunction> memory_ctor( 822 isolate->native_context()->wasm_memory_constructor(), isolate); 823 824 auto memory_object = Handle<WasmMemoryObject>::cast( 825 isolate->factory()->NewJSObject(memory_ctor, AllocationType::kOld)); 826 memory_object->set_array_buffer(*buffer); 827 memory_object->set_maximum_pages(maximum); 828 829 if (buffer->is_shared()) { 830 auto backing_store = buffer->GetBackingStore(); 831 backing_store->AttachSharedWasmMemoryObject(isolate, memory_object); 832 } 833 834 // For debugging purposes we memorize a link from the JSArrayBuffer 835 // to it's owning WasmMemoryObject instance. 836 Handle<Symbol> symbol = isolate->factory()->array_buffer_wasm_memory_symbol(); 837 JSObject::SetProperty(isolate, buffer, symbol, memory_object).Check(); 838 839 return memory_object; 840} 841 842MaybeHandle<WasmMemoryObject> WasmMemoryObject::New(Isolate* isolate, 843 int initial, int maximum, 844 SharedFlag shared) { 845 bool has_maximum = maximum != kNoMaximum; 846 int heuristic_maximum = maximum; 847 if (!has_maximum) { 848 heuristic_maximum = static_cast<int>(wasm::max_mem_pages()); 849 } 850 851#ifdef V8_TARGET_ARCH_32_BIT 852 // On 32-bit platforms we need an heuristic here to balance overall memory 853 // and address space consumption. 854 constexpr int kGBPages = 1024 * 1024 * 1024 / wasm::kWasmPageSize; 855 if (initial > kGBPages) { 856 // We always allocate at least the initial size. 857 heuristic_maximum = initial; 858 } else if (has_maximum) { 859 // We try to reserve the maximum, but at most 1GB to avoid OOMs. 860 heuristic_maximum = std::min(maximum, kGBPages); 861 } else if (shared == SharedFlag::kShared) { 862 // If shared memory has no maximum, we use an implicit maximum of 1GB. 863 heuristic_maximum = kGBPages; 864 } else { 865 // If non-shared memory has no maximum, we only allocate the initial size 866 // and then grow with realloc. 867 heuristic_maximum = initial; 868 } 869#endif 870 871 auto backing_store = BackingStore::AllocateWasmMemory( 872 isolate, initial, heuristic_maximum, shared); 873 874 if (!backing_store) return {}; 875 876 Handle<JSArrayBuffer> buffer = 877 (shared == SharedFlag::kShared) 878 ? isolate->factory()->NewJSSharedArrayBuffer(std::move(backing_store)) 879 : isolate->factory()->NewJSArrayBuffer(std::move(backing_store)); 880 881 return New(isolate, buffer, maximum); 882} 883 884void WasmMemoryObject::AddInstance(Isolate* isolate, 885 Handle<WasmMemoryObject> memory, 886 Handle<WasmInstanceObject> instance) { 887 Handle<WeakArrayList> old_instances = 888 memory->has_instances() 889 ? Handle<WeakArrayList>(memory->instances(), isolate) 890 : handle(ReadOnlyRoots(isolate->heap()).empty_weak_array_list(), 891 isolate); 892 Handle<WeakArrayList> new_instances = WeakArrayList::Append( 893 isolate, old_instances, MaybeObjectHandle::Weak(instance)); 894 memory->set_instances(*new_instances); 895 Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate); 896 SetInstanceMemory(instance, buffer); 897} 898 899void WasmMemoryObject::update_instances(Isolate* isolate, 900 Handle<JSArrayBuffer> buffer) { 901 if (has_instances()) { 902 Handle<WeakArrayList> instances(this->instances(), isolate); 903 for (int i = 0; i < instances->length(); i++) { 904 MaybeObject elem = instances->Get(i); 905 HeapObject heap_object; 906 if (elem->GetHeapObjectIfWeak(&heap_object)) { 907 Handle<WasmInstanceObject> instance( 908 WasmInstanceObject::cast(heap_object), isolate); 909 SetInstanceMemory(instance, buffer); 910 } else { 911 DCHECK(elem->IsCleared()); 912 } 913 } 914 } 915 set_array_buffer(*buffer); 916} 917 918// static 919int32_t WasmMemoryObject::Grow(Isolate* isolate, 920 Handle<WasmMemoryObject> memory_object, 921 uint32_t pages) { 922 TRACE_EVENT0("v8.wasm", "wasm.GrowMemory"); 923 Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer(), isolate); 924 // Any buffer used as an asmjs memory cannot be detached, and 925 // therefore this memory cannot be grown. 926 if (old_buffer->is_asmjs_memory()) return -1; 927 928 std::shared_ptr<BackingStore> backing_store = old_buffer->GetBackingStore(); 929 if (!backing_store) return -1; 930 931 // Check for maximum memory size. 932 // Note: The {wasm::max_mem_pages()} limit is already checked in 933 // {BackingStore::CopyWasmMemory}, and is irrelevant for 934 // {GrowWasmMemoryInPlace} because memory is never allocated with more 935 // capacity than that limit. 936 size_t old_size = old_buffer->byte_length(); 937 DCHECK_EQ(0, old_size % wasm::kWasmPageSize); 938 size_t old_pages = old_size / wasm::kWasmPageSize; 939 uint32_t max_pages = wasm::kSpecMaxMemoryPages; 940 if (memory_object->has_maximum_pages()) { 941 DCHECK_GE(max_pages, memory_object->maximum_pages()); 942 max_pages = static_cast<uint32_t>(memory_object->maximum_pages()); 943 } 944 DCHECK_GE(max_pages, old_pages); 945 if (pages > max_pages - old_pages) return -1; 946 947 base::Optional<size_t> result_inplace = 948 backing_store->GrowWasmMemoryInPlace(isolate, pages, max_pages); 949 // Handle shared memory first. 950 if (old_buffer->is_shared()) { 951 // Shared memories can only be grown in place; no copying. 952 if (!result_inplace.has_value()) { 953 // There are different limits per platform, thus crash if the correctness 954 // fuzzer is running. 955 if (FLAG_correctness_fuzzer_suppressions) { 956 FATAL("could not grow wasm memory"); 957 } 958 return -1; 959 } 960 961 BackingStore::BroadcastSharedWasmMemoryGrow(isolate, backing_store); 962 // Broadcasting the update should update this memory object too. 963 CHECK_NE(*old_buffer, memory_object->array_buffer()); 964 size_t new_pages = result_inplace.value() + pages; 965 // If the allocation succeeded, then this can't possibly overflow: 966 size_t new_byte_length = new_pages * wasm::kWasmPageSize; 967 // This is a less than check, as it is not guaranteed that the SAB 968 // length here will be equal to the stashed length above as calls to 969 // grow the same memory object can come in from different workers. 970 // It is also possible that a call to Grow was in progress when 971 // handling this call. 972 CHECK_LE(new_byte_length, memory_object->array_buffer().byte_length()); 973 // As {old_pages} was read racefully, we return here the synchronized 974 // value provided by {GrowWasmMemoryInPlace}, to provide the atomic 975 // read-modify-write behavior required by the spec. 976 return static_cast<int32_t>(result_inplace.value()); // success 977 } 978 979 // Check if the non-shared memory could grow in-place. 980 if (result_inplace.has_value()) { 981 // Detach old and create a new one with the grown backing store. 982 old_buffer->Detach(true); 983 Handle<JSArrayBuffer> new_buffer = 984 isolate->factory()->NewJSArrayBuffer(std::move(backing_store)); 985 memory_object->update_instances(isolate, new_buffer); 986 // For debugging purposes we memorize a link from the JSArrayBuffer 987 // to it's owning WasmMemoryObject instance. 988 Handle<Symbol> symbol = 989 isolate->factory()->array_buffer_wasm_memory_symbol(); 990 JSObject::SetProperty(isolate, new_buffer, symbol, memory_object).Check(); 991 DCHECK_EQ(result_inplace.value(), old_pages); 992 return static_cast<int32_t>(result_inplace.value()); // success 993 } 994 995 size_t new_pages = old_pages + pages; 996 DCHECK_LT(old_pages, new_pages); 997 // Try allocating a new backing store and copying. 998 // To avoid overall quadratic complexity of many small grow operations, we 999 // grow by at least 0.5 MB + 12.5% of the existing memory size. 1000 // These numbers are kept small because we must be careful about address 1001 // space consumption on 32-bit platforms. 1002 size_t min_growth = old_pages + 8 + (old_pages >> 3); 1003 size_t new_capacity = std::max(new_pages, min_growth); 1004 std::unique_ptr<BackingStore> new_backing_store = 1005 backing_store->CopyWasmMemory(isolate, new_pages, new_capacity); 1006 if (!new_backing_store) { 1007 // Crash on out-of-memory if the correctness fuzzer is running. 1008 if (FLAG_correctness_fuzzer_suppressions) { 1009 FATAL("could not grow wasm memory"); 1010 } 1011 return -1; 1012 } 1013 1014 // Detach old and create a new one with the new backing store. 1015 old_buffer->Detach(true); 1016 Handle<JSArrayBuffer> new_buffer = 1017 isolate->factory()->NewJSArrayBuffer(std::move(new_backing_store)); 1018 memory_object->update_instances(isolate, new_buffer); 1019 // For debugging purposes we memorize a link from the JSArrayBuffer 1020 // to it's owning WasmMemoryObject instance. 1021 Handle<Symbol> symbol = isolate->factory()->array_buffer_wasm_memory_symbol(); 1022 JSObject::SetProperty(isolate, new_buffer, symbol, memory_object).Check(); 1023 return static_cast<int32_t>(old_pages); // success 1024} 1025 1026// static 1027MaybeHandle<WasmGlobalObject> WasmGlobalObject::New( 1028 Isolate* isolate, Handle<WasmInstanceObject> instance, 1029 MaybeHandle<JSArrayBuffer> maybe_untagged_buffer, 1030 MaybeHandle<FixedArray> maybe_tagged_buffer, wasm::ValueType type, 1031 int32_t offset, bool is_mutable) { 1032 Handle<JSFunction> global_ctor( 1033 isolate->native_context()->wasm_global_constructor(), isolate); 1034 auto global_obj = Handle<WasmGlobalObject>::cast( 1035 isolate->factory()->NewJSObject(global_ctor)); 1036 { 1037 // Disallow GC until all fields have acceptable types. 1038 DisallowGarbageCollection no_gc; 1039 if (!instance.is_null()) global_obj->set_instance(*instance); 1040 global_obj->set_type(type); 1041 global_obj->set_offset(offset); 1042 global_obj->set_is_mutable(is_mutable); 1043 } 1044 1045 if (type.is_reference()) { 1046 DCHECK(maybe_untagged_buffer.is_null()); 1047 Handle<FixedArray> tagged_buffer; 1048 if (!maybe_tagged_buffer.ToHandle(&tagged_buffer)) { 1049 // If no buffer was provided, create one. 1050 tagged_buffer = 1051 isolate->factory()->NewFixedArray(1, AllocationType::kOld); 1052 CHECK_EQ(offset, 0); 1053 } 1054 global_obj->set_tagged_buffer(*tagged_buffer); 1055 } else { 1056 DCHECK(maybe_tagged_buffer.is_null()); 1057 uint32_t type_size = type.value_kind_size(); 1058 1059 Handle<JSArrayBuffer> untagged_buffer; 1060 if (!maybe_untagged_buffer.ToHandle(&untagged_buffer)) { 1061 MaybeHandle<JSArrayBuffer> result = 1062 isolate->factory()->NewJSArrayBufferAndBackingStore( 1063 offset + type_size, InitializedFlag::kZeroInitialized); 1064 1065 if (!result.ToHandle(&untagged_buffer)) return {}; 1066 } 1067 1068 // Check that the offset is in bounds. 1069 CHECK_LE(offset + type_size, untagged_buffer->byte_length()); 1070 1071 global_obj->set_untagged_buffer(*untagged_buffer); 1072 } 1073 1074 return global_obj; 1075} 1076 1077FunctionTargetAndRef::FunctionTargetAndRef( 1078 Handle<WasmInstanceObject> target_instance, int target_func_index) { 1079 Isolate* isolate = target_instance->native_context().GetIsolate(); 1080 if (target_func_index < 1081 static_cast<int>(target_instance->module()->num_imported_functions)) { 1082 // The function in the target instance was imported. Use its imports table, 1083 // which contains a tuple needed by the import wrapper. 1084 ImportedFunctionEntry entry(target_instance, target_func_index); 1085 ref_ = handle(entry.object_ref(), isolate); 1086 call_target_ = entry.target(); 1087 } else { 1088 // The function in the target instance was not imported. 1089 ref_ = target_instance; 1090 call_target_ = target_instance->GetCallTarget(target_func_index); 1091 } 1092} 1093 1094void ImportedFunctionEntry::SetWasmToJs( 1095 Isolate* isolate, Handle<JSReceiver> callable, 1096 const wasm::WasmCode* wasm_to_js_wrapper, Handle<HeapObject> suspender) { 1097 TRACE_IFT("Import callable 0x%" PRIxPTR "[%d] = {callable=0x%" PRIxPTR 1098 ", target=%p}\n", 1099 instance_->ptr(), index_, callable->ptr(), 1100 wasm_to_js_wrapper->instructions().begin()); 1101 DCHECK(wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToJsWrapper || 1102 wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper); 1103 Handle<WasmApiFunctionRef> ref = 1104 isolate->factory()->NewWasmApiFunctionRef(callable, suspender); 1105 instance_->imported_function_refs().set(index_, *ref); 1106 instance_->imported_function_targets()[index_] = 1107 wasm_to_js_wrapper->instruction_start(); 1108} 1109 1110void ImportedFunctionEntry::SetWasmToWasm(WasmInstanceObject instance, 1111 Address call_target) { 1112 TRACE_IFT("Import Wasm 0x%" PRIxPTR "[%d] = {instance=0x%" PRIxPTR 1113 ", target=0x%" PRIxPTR "}\n", 1114 instance_->ptr(), index_, instance.ptr(), call_target); 1115 instance_->imported_function_refs().set(index_, instance); 1116 instance_->imported_function_targets()[index_] = call_target; 1117} 1118 1119// Returns an empty Object() if no callable is available, a JSReceiver 1120// otherwise. 1121Object ImportedFunctionEntry::maybe_callable() { 1122 Object value = object_ref(); 1123 if (!value.IsWasmApiFunctionRef()) return Object(); 1124 return JSReceiver::cast(WasmApiFunctionRef::cast(value).callable()); 1125} 1126 1127JSReceiver ImportedFunctionEntry::callable() { 1128 return JSReceiver::cast(WasmApiFunctionRef::cast(object_ref()).callable()); 1129} 1130 1131Object ImportedFunctionEntry::object_ref() { 1132 return instance_->imported_function_refs().get(index_); 1133} 1134 1135Address ImportedFunctionEntry::target() { 1136 return instance_->imported_function_targets()[index_]; 1137} 1138 1139// static 1140constexpr uint16_t WasmInstanceObject::kTaggedFieldOffsets[]; 1141 1142// static 1143bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( 1144 Handle<WasmInstanceObject> instance, int table_index, 1145 uint32_t minimum_size) { 1146 Isolate* isolate = instance->GetIsolate(); 1147 DCHECK_LT(table_index, instance->indirect_function_tables().length()); 1148 Handle<WasmIndirectFunctionTable> table = 1149 instance->GetIndirectFunctionTable(isolate, table_index); 1150 WasmIndirectFunctionTable::Resize(isolate, table, minimum_size); 1151 if (table_index == 0) { 1152 instance->SetIndirectFunctionTableShortcuts(isolate); 1153 } 1154 return true; 1155} 1156 1157void WasmInstanceObject::SetRawMemory(byte* mem_start, size_t mem_size) { 1158 CHECK_LE(mem_size, wasm::max_mem_bytes()); 1159#if V8_HOST_ARCH_64_BIT 1160 set_memory_start(mem_start); 1161 set_memory_size(mem_size); 1162#else 1163 // Must handle memory > 2GiB specially. 1164 CHECK_LE(mem_size, size_t{kMaxUInt32}); 1165 set_memory_start(mem_start); 1166 set_memory_size(mem_size); 1167#endif 1168} 1169 1170const WasmModule* WasmInstanceObject::module() { 1171 return module_object().module(); 1172} 1173 1174Handle<WasmInstanceObject> WasmInstanceObject::New( 1175 Isolate* isolate, Handle<WasmModuleObject> module_object) { 1176 Handle<JSFunction> instance_cons( 1177 isolate->native_context()->wasm_instance_constructor(), isolate); 1178 Handle<JSObject> instance_object = 1179 isolate->factory()->NewJSObject(instance_cons, AllocationType::kOld); 1180 1181 Handle<WasmInstanceObject> instance( 1182 WasmInstanceObject::cast(*instance_object), isolate); 1183 instance->clear_padding(); 1184 1185 // Initialize the imported function arrays. 1186 auto module = module_object->module(); 1187 auto num_imported_functions = module->num_imported_functions; 1188 auto num_imported_mutable_globals = module->num_imported_mutable_globals; 1189 auto num_data_segments = module->num_declared_data_segments; 1190 size_t native_allocations_size = EstimateNativeAllocationsSize(module); 1191 auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate( 1192 isolate, native_allocations_size, instance, num_imported_functions, 1193 num_imported_mutable_globals, num_data_segments, 1194 module->elem_segments.size()); 1195 instance->set_managed_native_allocations(*native_allocations); 1196 1197 Handle<FixedArray> imported_function_refs = 1198 isolate->factory()->NewFixedArray(num_imported_functions); 1199 instance->set_imported_function_refs(*imported_function_refs); 1200 1201 instance->SetRawMemory(reinterpret_cast<byte*>(EmptyBackingStoreBuffer()), 0); 1202 instance->set_isolate_root(isolate->isolate_root()); 1203 instance->set_stack_limit_address( 1204 isolate->stack_guard()->address_of_jslimit()); 1205 instance->set_real_stack_limit_address( 1206 isolate->stack_guard()->address_of_real_jslimit()); 1207 instance->set_new_allocation_limit_address( 1208 isolate->heap()->NewSpaceAllocationLimitAddress()); 1209 instance->set_new_allocation_top_address( 1210 isolate->heap()->NewSpaceAllocationTopAddress()); 1211 instance->set_old_allocation_limit_address( 1212 isolate->heap()->OldSpaceAllocationLimitAddress()); 1213 instance->set_old_allocation_top_address( 1214 isolate->heap()->OldSpaceAllocationTopAddress()); 1215 instance->set_globals_start(nullptr); 1216 instance->set_indirect_function_table_size(0); 1217 instance->set_indirect_function_table_refs( 1218 ReadOnlyRoots(isolate).empty_fixed_array()); 1219 instance->set_indirect_function_table_sig_ids(nullptr); 1220 instance->set_indirect_function_table_targets(nullptr); 1221 instance->set_native_context(*isolate->native_context()); 1222 instance->set_module_object(*module_object); 1223 instance->set_jump_table_start( 1224 module_object->native_module()->jump_table_start()); 1225 instance->set_hook_on_function_call_address( 1226 isolate->debug()->hook_on_function_call_address()); 1227 instance->set_managed_object_maps(*isolate->factory()->empty_fixed_array()); 1228 instance->set_feedback_vectors(*isolate->factory()->empty_fixed_array()); 1229 instance->set_tiering_budget_array( 1230 module_object->native_module()->tiering_budget_array()); 1231 instance->set_break_on_entry(module_object->script().break_on_entry()); 1232 1233 // Insert the new instance into the scripts weak list of instances. This list 1234 // is used for breakpoints affecting all instances belonging to the script. 1235 if (module_object->script().type() == Script::TYPE_WASM) { 1236 Handle<WeakArrayList> weak_instance_list( 1237 module_object->script().wasm_weak_instance_list(), isolate); 1238 weak_instance_list = WeakArrayList::Append( 1239 isolate, weak_instance_list, MaybeObjectHandle::Weak(instance)); 1240 module_object->script().set_wasm_weak_instance_list(*weak_instance_list); 1241 } 1242 1243 InitDataSegmentArrays(instance, module_object); 1244 InitElemSegmentArrays(instance, module_object); 1245 1246 return instance; 1247} 1248 1249// static 1250void WasmInstanceObject::InitDataSegmentArrays( 1251 Handle<WasmInstanceObject> instance, 1252 Handle<WasmModuleObject> module_object) { 1253 auto module = module_object->module(); 1254 auto wire_bytes = module_object->native_module()->wire_bytes(); 1255 auto num_data_segments = module->num_declared_data_segments; 1256 // The number of declared data segments will be zero if there is no DataCount 1257 // section. These arrays will not be allocated nor initialized in that case, 1258 // since they cannot be used (since the validator checks that number of 1259 // declared data segments when validating the memory.init and memory.drop 1260 // instructions). 1261 DCHECK(num_data_segments == 0 || 1262 num_data_segments == module->data_segments.size()); 1263 for (size_t i = 0; i < num_data_segments; ++i) { 1264 const wasm::WasmDataSegment& segment = module->data_segments[i]; 1265 // Initialize the pointer and size of passive segments. 1266 auto source_bytes = wire_bytes.SubVector(segment.source.offset(), 1267 segment.source.end_offset()); 1268 instance->data_segment_starts()[i] = 1269 reinterpret_cast<Address>(source_bytes.begin()); 1270 // Set the active segments to being already dropped, since memory.init on 1271 // a dropped passive segment and an active segment have the same 1272 // behavior. 1273 instance->data_segment_sizes()[i] = 1274 segment.active ? 0 : source_bytes.length(); 1275 } 1276} 1277 1278void WasmInstanceObject::InitElemSegmentArrays( 1279 Handle<WasmInstanceObject> instance, 1280 Handle<WasmModuleObject> module_object) { 1281 auto module = module_object->module(); 1282 auto num_elem_segments = module->elem_segments.size(); 1283 for (size_t i = 0; i < num_elem_segments; ++i) { 1284 instance->dropped_elem_segments()[i] = 1285 module->elem_segments[i].status == 1286 wasm::WasmElemSegment::kStatusDeclarative 1287 ? 1 1288 : 0; 1289 } 1290} 1291 1292Address WasmInstanceObject::GetCallTarget(uint32_t func_index) { 1293 wasm::NativeModule* native_module = module_object().native_module(); 1294 if (func_index < native_module->num_imported_functions()) { 1295 return imported_function_targets()[func_index]; 1296 } 1297 return native_module->GetCallTargetForFunction(func_index); 1298} 1299 1300Handle<WasmIndirectFunctionTable> WasmInstanceObject::GetIndirectFunctionTable( 1301 Isolate* isolate, uint32_t table_index) { 1302 DCHECK_LT(table_index, indirect_function_tables().length()); 1303 return handle(WasmIndirectFunctionTable::cast( 1304 indirect_function_tables().get(table_index)), 1305 isolate); 1306} 1307 1308void WasmInstanceObject::SetIndirectFunctionTableShortcuts(Isolate* isolate) { 1309 if (indirect_function_tables().length() > 0 && 1310 indirect_function_tables().get(0).IsWasmIndirectFunctionTable()) { 1311 HandleScope scope(isolate); 1312 Handle<WasmIndirectFunctionTable> table0 = 1313 GetIndirectFunctionTable(isolate, 0); 1314 set_indirect_function_table_size(table0->size()); 1315 set_indirect_function_table_refs(table0->refs()); 1316 set_indirect_function_table_sig_ids(table0->sig_ids()); 1317 set_indirect_function_table_targets(table0->targets()); 1318 } 1319} 1320 1321// static 1322bool WasmInstanceObject::CopyTableEntries(Isolate* isolate, 1323 Handle<WasmInstanceObject> instance, 1324 uint32_t table_dst_index, 1325 uint32_t table_src_index, 1326 uint32_t dst, uint32_t src, 1327 uint32_t count) { 1328 CHECK_LT(table_dst_index, instance->tables().length()); 1329 CHECK_LT(table_src_index, instance->tables().length()); 1330 auto table_dst = handle( 1331 WasmTableObject::cast(instance->tables().get(table_dst_index)), isolate); 1332 auto table_src = handle( 1333 WasmTableObject::cast(instance->tables().get(table_src_index)), isolate); 1334 uint32_t max_dst = table_dst->current_length(); 1335 uint32_t max_src = table_src->current_length(); 1336 bool copy_backward = src < dst; 1337 if (!base::IsInBounds(dst, count, max_dst) || 1338 !base::IsInBounds(src, count, max_src)) { 1339 return false; 1340 } 1341 1342 // no-op 1343 if ((dst == src && table_dst_index == table_src_index) || count == 0) { 1344 return true; 1345 } 1346 1347 for (uint32_t i = 0; i < count; ++i) { 1348 uint32_t src_index = copy_backward ? (src + count - i - 1) : src + i; 1349 uint32_t dst_index = copy_backward ? (dst + count - i - 1) : dst + i; 1350 auto value = WasmTableObject::Get(isolate, table_src, src_index); 1351 WasmTableObject::Set(isolate, table_dst, dst_index, value); 1352 } 1353 return true; 1354} 1355 1356// static 1357bool WasmInstanceObject::InitTableEntries(Isolate* isolate, 1358 Handle<WasmInstanceObject> instance, 1359 uint32_t table_index, 1360 uint32_t segment_index, uint32_t dst, 1361 uint32_t src, uint32_t count) { 1362 // Note that this implementation just calls through to module instantiation. 1363 // This is intentional, so that the runtime only depends on the object 1364 // methods, and not the module instantiation logic. 1365 return wasm::LoadElemSegment(isolate, instance, table_index, segment_index, 1366 dst, src, count); 1367} 1368 1369MaybeHandle<WasmInternalFunction> WasmInstanceObject::GetWasmInternalFunction( 1370 Isolate* isolate, Handle<WasmInstanceObject> instance, int index) { 1371 MaybeHandle<WasmInternalFunction> result; 1372 if (instance->has_wasm_internal_functions()) { 1373 Object val = instance->wasm_internal_functions().get(index); 1374 if (!val.IsUndefined(isolate)) { 1375 result = Handle<WasmInternalFunction>(WasmInternalFunction::cast(val), 1376 isolate); 1377 } 1378 } 1379 return result; 1380} 1381 1382Handle<WasmInternalFunction> 1383WasmInstanceObject::GetOrCreateWasmInternalFunction( 1384 Isolate* isolate, Handle<WasmInstanceObject> instance, int function_index) { 1385 MaybeHandle<WasmInternalFunction> maybe_result = 1386 WasmInstanceObject::GetWasmInternalFunction(isolate, instance, 1387 function_index); 1388 1389 Handle<WasmInternalFunction> result; 1390 if (maybe_result.ToHandle(&result)) { 1391 return result; 1392 } 1393 1394 Handle<WasmModuleObject> module_object(instance->module_object(), isolate); 1395 const WasmModule* module = module_object->module(); 1396 const WasmFunction& function = module->functions[function_index]; 1397 int wrapper_index = 1398 GetExportWrapperIndex(module, function.sig_index, function.imported); 1399 DCHECK_EQ(wrapper_index, 1400 GetExportWrapperIndex(module, function.sig, function.imported)); 1401 1402 Handle<Object> entry = 1403 FixedArray::get(module_object->export_wrappers(), wrapper_index, isolate); 1404 1405 Handle<CodeT> wrapper; 1406 if (entry->IsCodeT()) { 1407 wrapper = Handle<CodeT>::cast(entry); 1408 } else { 1409 // The wrapper may not exist yet if no function in the exports section has 1410 // this signature. We compile it and store the wrapper in the module for 1411 // later use. 1412 wrapper = ToCodeT( 1413 wasm::JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper( 1414 isolate, function.sig, instance->module(), function.imported), 1415 isolate); 1416 module_object->export_wrappers().set(wrapper_index, *wrapper); 1417 } 1418 auto external = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New( 1419 isolate, instance, function_index, 1420 static_cast<int>(function.sig->parameter_count()), wrapper)); 1421 result = 1422 WasmInternalFunction::FromExternal(external, isolate).ToHandleChecked(); 1423 1424 WasmInstanceObject::SetWasmInternalFunction(isolate, instance, function_index, 1425 result); 1426 return result; 1427} 1428 1429void WasmInstanceObject::SetWasmInternalFunction( 1430 Isolate* isolate, Handle<WasmInstanceObject> instance, int index, 1431 Handle<WasmInternalFunction> val) { 1432 Handle<FixedArray> functions; 1433 if (!instance->has_wasm_internal_functions()) { 1434 // Lazily allocate the wasm external functions array. 1435 functions = isolate->factory()->NewFixedArray( 1436 static_cast<int>(instance->module()->functions.size())); 1437 instance->set_wasm_internal_functions(*functions); 1438 } else { 1439 functions = 1440 Handle<FixedArray>(instance->wasm_internal_functions(), isolate); 1441 } 1442 functions->set(index, *val); 1443} 1444 1445// static 1446void WasmInstanceObject::ImportWasmJSFunctionIntoTable( 1447 Isolate* isolate, Handle<WasmInstanceObject> instance, int table_index, 1448 int entry_index, Handle<WasmJSFunction> js_function) { 1449 // Deserialize the signature encapsulated with the {WasmJSFunction}. 1450 // Note that {SignatureMap::Find} may return {-1} if the signature is 1451 // not found; it will simply never match any check. 1452 Zone zone(isolate->allocator(), ZONE_NAME); 1453 const wasm::FunctionSig* sig = js_function->GetSignature(&zone); 1454 auto sig_id = instance->module()->signature_map.Find(*sig); 1455 1456 // Compile a wrapper for the target callable. 1457 Handle<JSReceiver> callable(js_function->GetCallable(), isolate); 1458 wasm::WasmCodeRefScope code_ref_scope; 1459 Address call_target = kNullAddress; 1460 if (sig_id >= 0) { 1461 wasm::NativeModule* native_module = 1462 instance->module_object().native_module(); 1463 // TODO(wasm): Cache and reuse wrapper code, to avoid repeated compilation 1464 // and permissions switching. 1465 const wasm::WasmFeatures enabled = native_module->enabled_features(); 1466 auto resolved = compiler::ResolveWasmImportCall( 1467 callable, sig, instance->module(), enabled); 1468 compiler::WasmImportCallKind kind = resolved.kind; 1469 callable = resolved.callable; // Update to ultimate target. 1470 DCHECK_NE(compiler::WasmImportCallKind::kLinkError, kind); 1471 wasm::CompilationEnv env = native_module->CreateCompilationEnv(); 1472 // {expected_arity} should only be used if kind != kJSFunctionArityMismatch. 1473 int expected_arity = -1; 1474 if (kind == compiler::WasmImportCallKind ::kJSFunctionArityMismatch) { 1475 expected_arity = Handle<JSFunction>::cast(callable) 1476 ->shared() 1477 .internal_formal_parameter_count_without_receiver(); 1478 } 1479 wasm::Suspend suspend = 1480 resolved.suspender.is_null() || resolved.suspender->IsUndefined() 1481 ? wasm::kNoSuspend 1482 : wasm::kSuspend; 1483 // TODO(manoskouk): Reuse js_function->wasm_to_js_wrapper_code(). 1484 wasm::WasmCompilationResult result = compiler::CompileWasmImportCallWrapper( 1485 &env, kind, sig, false, expected_arity, suspend); 1486 wasm::CodeSpaceWriteScope write_scope(native_module); 1487 std::unique_ptr<wasm::WasmCode> wasm_code = native_module->AddCode( 1488 result.func_index, result.code_desc, result.frame_slot_count, 1489 result.tagged_parameter_slots, 1490 result.protected_instructions_data.as_vector(), 1491 result.source_positions.as_vector(), GetCodeKind(result), 1492 wasm::ExecutionTier::kNone, wasm::kNoDebugging); 1493 wasm::WasmCode* published_code = 1494 native_module->PublishCode(std::move(wasm_code)); 1495 isolate->counters()->wasm_generated_code_size()->Increment( 1496 published_code->instructions().length()); 1497 isolate->counters()->wasm_reloc_size()->Increment( 1498 published_code->reloc_info().length()); 1499 call_target = published_code->instruction_start(); 1500 } 1501 1502 // Update the dispatch table. 1503 Handle<HeapObject> suspender = handle(js_function->GetSuspender(), isolate); 1504 Handle<WasmApiFunctionRef> ref = 1505 isolate->factory()->NewWasmApiFunctionRef(callable, suspender); 1506 WasmIndirectFunctionTable::cast( 1507 instance->indirect_function_tables().get(table_index)) 1508 .Set(entry_index, sig_id, call_target, *ref); 1509} 1510 1511// static 1512uint8_t* WasmInstanceObject::GetGlobalStorage( 1513 Handle<WasmInstanceObject> instance, const wasm::WasmGlobal& global) { 1514 DCHECK(!global.type.is_reference()); 1515 if (global.mutability && global.imported) { 1516 return reinterpret_cast<byte*>( 1517 instance->imported_mutable_globals()[global.index]); 1518 } else { 1519 return instance->globals_start() + global.offset; 1520 } 1521} 1522 1523// static 1524std::pair<Handle<FixedArray>, uint32_t> 1525WasmInstanceObject::GetGlobalBufferAndIndex(Handle<WasmInstanceObject> instance, 1526 const wasm::WasmGlobal& global) { 1527 DCHECK(global.type.is_reference()); 1528 Isolate* isolate = instance->GetIsolate(); 1529 if (global.mutability && global.imported) { 1530 Handle<FixedArray> buffer( 1531 FixedArray::cast( 1532 instance->imported_mutable_globals_buffers().get(global.index)), 1533 isolate); 1534 Address idx = instance->imported_mutable_globals()[global.index]; 1535 DCHECK_LE(idx, std::numeric_limits<uint32_t>::max()); 1536 return {buffer, static_cast<uint32_t>(idx)}; 1537 } 1538 return {handle(instance->tagged_globals_buffer(), isolate), global.offset}; 1539} 1540 1541// static 1542wasm::WasmValue WasmInstanceObject::GetGlobalValue( 1543 Handle<WasmInstanceObject> instance, const wasm::WasmGlobal& global) { 1544 Isolate* isolate = instance->GetIsolate(); 1545 if (global.type.is_reference()) { 1546 Handle<FixedArray> global_buffer; // The buffer of the global. 1547 uint32_t global_index = 0; // The index into the buffer. 1548 std::tie(global_buffer, global_index) = 1549 GetGlobalBufferAndIndex(instance, global); 1550 return wasm::WasmValue(handle(global_buffer->get(global_index), isolate), 1551 global.type); 1552 } 1553 Address ptr = reinterpret_cast<Address>(GetGlobalStorage(instance, global)); 1554 using wasm::Simd128; 1555 switch (global.type.kind()) { 1556#define CASE_TYPE(valuetype, ctype) \ 1557 case wasm::valuetype: \ 1558 return wasm::WasmValue(base::ReadUnalignedValue<ctype>(ptr)); 1559 FOREACH_WASMVALUE_CTYPES(CASE_TYPE) 1560#undef CASE_TYPE 1561 default: 1562 UNREACHABLE(); 1563 } 1564} 1565 1566wasm::WasmValue WasmStruct::GetFieldValue(uint32_t index) { 1567 wasm::ValueType field_type = type()->field(index); 1568 int field_offset = WasmStruct::kHeaderSize + type()->field_offset(index); 1569 Address field_address = GetFieldAddress(field_offset); 1570 using wasm::Simd128; 1571 switch (field_type.kind()) { 1572#define CASE_TYPE(valuetype, ctype) \ 1573 case wasm::valuetype: \ 1574 return wasm::WasmValue(base::ReadUnalignedValue<ctype>(field_address)); 1575 CASE_TYPE(kI8, int8_t) 1576 CASE_TYPE(kI16, int16_t) 1577 FOREACH_WASMVALUE_CTYPES(CASE_TYPE) 1578#undef CASE_TYPE 1579 case wasm::kRef: 1580 case wasm::kOptRef: { 1581 Handle<Object> ref(TaggedField<Object>::load(*this, field_offset), 1582 GetIsolateFromWritableObject(*this)); 1583 return wasm::WasmValue(ref, field_type); 1584 } 1585 case wasm::kRtt: 1586 // TODO(7748): Expose RTTs to DevTools. 1587 UNIMPLEMENTED(); 1588 case wasm::kVoid: 1589 case wasm::kBottom: 1590 UNREACHABLE(); 1591 } 1592} 1593 1594wasm::WasmValue WasmArray::GetElement(uint32_t index) { 1595 wasm::ValueType element_type = type()->element_type(); 1596 int element_offset = 1597 WasmArray::kHeaderSize + index * element_type.value_kind_size(); 1598 Address element_address = GetFieldAddress(element_offset); 1599 using wasm::Simd128; 1600 switch (element_type.kind()) { 1601#define CASE_TYPE(value_type, ctype) \ 1602 case wasm::value_type: \ 1603 return wasm::WasmValue(base::ReadUnalignedValue<ctype>(element_address)); 1604 CASE_TYPE(kI8, int8_t) 1605 CASE_TYPE(kI16, int16_t) 1606 FOREACH_WASMVALUE_CTYPES(CASE_TYPE) 1607#undef CASE_TYPE 1608 case wasm::kRef: 1609 case wasm::kOptRef: { 1610 Handle<Object> ref(TaggedField<Object>::load(*this, element_offset), 1611 GetIsolateFromWritableObject(*this)); 1612 return wasm::WasmValue(ref, element_type); 1613 } 1614 case wasm::kRtt: 1615 // TODO(7748): Expose RTTs to DevTools. 1616 UNIMPLEMENTED(); 1617 case wasm::kVoid: 1618 case wasm::kBottom: 1619 UNREACHABLE(); 1620 } 1621} 1622 1623// static 1624Handle<WasmTagObject> WasmTagObject::New(Isolate* isolate, 1625 const wasm::FunctionSig* sig, 1626 Handle<HeapObject> tag) { 1627 Handle<JSFunction> tag_cons(isolate->native_context()->wasm_tag_constructor(), 1628 isolate); 1629 1630 // Serialize the signature. 1631 DCHECK_EQ(0, sig->return_count()); 1632 DCHECK_LE(sig->parameter_count(), std::numeric_limits<int>::max()); 1633 int sig_size = static_cast<int>(sig->parameter_count()); 1634 Handle<PodArray<wasm::ValueType>> serialized_sig = 1635 PodArray<wasm::ValueType>::New(isolate, sig_size, AllocationType::kOld); 1636 int index = 0; // Index into the {PodArray} above. 1637 for (wasm::ValueType param : sig->parameters()) { 1638 serialized_sig->set(index++, param); 1639 } 1640 1641 Handle<JSObject> tag_object = 1642 isolate->factory()->NewJSObject(tag_cons, AllocationType::kOld); 1643 Handle<WasmTagObject> tag_wrapper = Handle<WasmTagObject>::cast(tag_object); 1644 tag_wrapper->set_serialized_signature(*serialized_sig); 1645 tag_wrapper->set_tag(*tag); 1646 1647 return tag_wrapper; 1648} 1649 1650// TODO(9495): Update this if function type variance is introduced. 1651bool WasmTagObject::MatchesSignature(const wasm::FunctionSig* sig) { 1652 DCHECK_EQ(0, sig->return_count()); 1653 DCHECK_LE(sig->parameter_count(), std::numeric_limits<int>::max()); 1654 int sig_size = static_cast<int>(sig->parameter_count()); 1655 if (sig_size != serialized_signature().length()) return false; 1656 for (int index = 0; index < sig_size; ++index) { 1657 if (sig->GetParam(index) != serialized_signature().get(index)) { 1658 return false; 1659 } 1660 } 1661 return true; 1662} 1663 1664// TODO(9495): Update this if function type variance is introduced. 1665bool WasmCapiFunction::MatchesSignature(const wasm::FunctionSig* sig) const { 1666 // TODO(jkummerow): Unify with "SignatureHelper" in c-api.cc. 1667 int param_count = static_cast<int>(sig->parameter_count()); 1668 int result_count = static_cast<int>(sig->return_count()); 1669 PodArray<wasm::ValueType> serialized_sig = 1670 shared().wasm_capi_function_data().serialized_signature(); 1671 if (param_count + result_count + 1 != serialized_sig.length()) return false; 1672 int serialized_index = 0; 1673 for (int i = 0; i < result_count; i++, serialized_index++) { 1674 if (sig->GetReturn(i) != serialized_sig.get(serialized_index)) { 1675 return false; 1676 } 1677 } 1678 if (serialized_sig.get(serialized_index) != wasm::kWasmVoid) return false; 1679 serialized_index++; 1680 for (int i = 0; i < param_count; i++, serialized_index++) { 1681 if (sig->GetParam(i) != serialized_sig.get(serialized_index)) return false; 1682 } 1683 return true; 1684} 1685 1686// static 1687Handle<WasmExceptionPackage> WasmExceptionPackage::New( 1688 Isolate* isolate, Handle<WasmExceptionTag> exception_tag, int size) { 1689 Handle<FixedArray> values = isolate->factory()->NewFixedArray(size); 1690 return New(isolate, exception_tag, values); 1691} 1692 1693Handle<WasmExceptionPackage> WasmExceptionPackage::New( 1694 Isolate* isolate, Handle<WasmExceptionTag> exception_tag, 1695 Handle<FixedArray> values) { 1696 Handle<JSObject> exception = isolate->factory()->NewWasmExceptionError( 1697 MessageTemplate::kWasmExceptionError); 1698 CHECK(!Object::SetProperty(isolate, exception, 1699 isolate->factory()->wasm_exception_tag_symbol(), 1700 exception_tag, StoreOrigin::kMaybeKeyed, 1701 Just(ShouldThrow::kThrowOnError)) 1702 .is_null()); 1703 CHECK(!Object::SetProperty(isolate, exception, 1704 isolate->factory()->wasm_exception_values_symbol(), 1705 values, StoreOrigin::kMaybeKeyed, 1706 Just(ShouldThrow::kThrowOnError)) 1707 .is_null()); 1708 return Handle<WasmExceptionPackage>::cast(exception); 1709} 1710 1711// static 1712Handle<Object> WasmExceptionPackage::GetExceptionTag( 1713 Isolate* isolate, Handle<WasmExceptionPackage> exception_package) { 1714 Handle<Object> tag; 1715 if (JSReceiver::GetProperty(isolate, exception_package, 1716 isolate->factory()->wasm_exception_tag_symbol()) 1717 .ToHandle(&tag)) { 1718 return tag; 1719 } 1720 return ReadOnlyRoots(isolate).undefined_value_handle(); 1721} 1722 1723// static 1724Handle<Object> WasmExceptionPackage::GetExceptionValues( 1725 Isolate* isolate, Handle<WasmExceptionPackage> exception_package) { 1726 Handle<Object> values; 1727 if (JSReceiver::GetProperty( 1728 isolate, exception_package, 1729 isolate->factory()->wasm_exception_values_symbol()) 1730 .ToHandle(&values)) { 1731 DCHECK_IMPLIES(!values->IsUndefined(), values->IsFixedArray()); 1732 return values; 1733 } 1734 return ReadOnlyRoots(isolate).undefined_value_handle(); 1735} 1736 1737void EncodeI32ExceptionValue(Handle<FixedArray> encoded_values, 1738 uint32_t* encoded_index, uint32_t value) { 1739 encoded_values->set((*encoded_index)++, Smi::FromInt(value >> 16)); 1740 encoded_values->set((*encoded_index)++, Smi::FromInt(value & 0xffff)); 1741} 1742 1743void EncodeI64ExceptionValue(Handle<FixedArray> encoded_values, 1744 uint32_t* encoded_index, uint64_t value) { 1745 EncodeI32ExceptionValue(encoded_values, encoded_index, 1746 static_cast<uint32_t>(value >> 32)); 1747 EncodeI32ExceptionValue(encoded_values, encoded_index, 1748 static_cast<uint32_t>(value)); 1749} 1750 1751void DecodeI32ExceptionValue(Handle<FixedArray> encoded_values, 1752 uint32_t* encoded_index, uint32_t* value) { 1753 uint32_t msb = Smi::cast(encoded_values->get((*encoded_index)++)).value(); 1754 uint32_t lsb = Smi::cast(encoded_values->get((*encoded_index)++)).value(); 1755 *value = (msb << 16) | (lsb & 0xffff); 1756} 1757 1758void DecodeI64ExceptionValue(Handle<FixedArray> encoded_values, 1759 uint32_t* encoded_index, uint64_t* value) { 1760 uint32_t lsb = 0, msb = 0; 1761 DecodeI32ExceptionValue(encoded_values, encoded_index, &msb); 1762 DecodeI32ExceptionValue(encoded_values, encoded_index, &lsb); 1763 *value = (static_cast<uint64_t>(msb) << 32) | static_cast<uint64_t>(lsb); 1764} 1765 1766// static 1767Handle<WasmContinuationObject> WasmContinuationObject::New( 1768 Isolate* isolate, std::unique_ptr<wasm::StackMemory> stack, 1769 Handle<HeapObject> parent) { 1770 stack->jmpbuf()->stack_limit = stack->jslimit(); 1771 stack->jmpbuf()->sp = stack->base(); 1772 stack->jmpbuf()->fp = kNullAddress; 1773 wasm::JumpBuffer* jmpbuf = stack->jmpbuf(); 1774 size_t external_size = stack->owned_size(); 1775 Handle<Foreign> managed_stack = Managed<wasm::StackMemory>::FromUniquePtr( 1776 isolate, external_size, std::move(stack)); 1777 Handle<Foreign> foreign_jmpbuf = 1778 isolate->factory()->NewForeign(reinterpret_cast<Address>(jmpbuf)); 1779 Handle<WasmContinuationObject> result = Handle<WasmContinuationObject>::cast( 1780 isolate->factory()->NewStruct(WASM_CONTINUATION_OBJECT_TYPE)); 1781 result->set_jmpbuf(*foreign_jmpbuf); 1782 result->set_stack(*managed_stack); 1783 result->set_parent(*parent); 1784 return result; 1785} 1786 1787// static 1788Handle<WasmContinuationObject> WasmContinuationObject::New( 1789 Isolate* isolate, std::unique_ptr<wasm::StackMemory> stack) { 1790 auto parent = ReadOnlyRoots(isolate).undefined_value(); 1791 return New(isolate, std::move(stack), handle(parent, isolate)); 1792} 1793 1794// static 1795Handle<WasmContinuationObject> WasmContinuationObject::New( 1796 Isolate* isolate, Handle<WasmContinuationObject> parent) { 1797 auto stack = 1798 std::unique_ptr<wasm::StackMemory>(wasm::StackMemory::New(isolate)); 1799 return New(isolate, std::move(stack), parent); 1800} 1801 1802// static 1803Handle<WasmSuspenderObject> WasmSuspenderObject::New(Isolate* isolate) { 1804 Handle<JSFunction> suspender_cons( 1805 isolate->native_context()->wasm_suspender_constructor(), isolate); 1806 // Suspender objects should be at least as long-lived as the instances of 1807 // which it will wrap the imports/exports, allocate in old space too. 1808 auto suspender = Handle<WasmSuspenderObject>::cast( 1809 isolate->factory()->NewJSObject(suspender_cons, AllocationType::kOld)); 1810 suspender->set_continuation(ReadOnlyRoots(isolate).undefined_value()); 1811 suspender->set_parent(ReadOnlyRoots(isolate).undefined_value()); 1812 suspender->set_state(Inactive); 1813 // Instantiate the callable object which resumes this Suspender. This will be 1814 // used implicitly as the onFulfilled callback of the returned JS promise. 1815 Handle<WasmOnFulfilledData> function_data = 1816 isolate->factory()->NewWasmOnFulfilledData(suspender); 1817 Handle<SharedFunctionInfo> shared = 1818 isolate->factory()->NewSharedFunctionInfoForWasmOnFulfilled( 1819 function_data); 1820 Handle<Context> context(isolate->native_context()); 1821 Handle<JSObject> resume = 1822 Factory::JSFunctionBuilder{isolate, shared, context}.Build(); 1823 suspender->set_resume(*resume); 1824 return suspender; 1825} 1826 1827#ifdef DEBUG 1828 1829namespace { 1830 1831constexpr uint32_t kBytesPerExceptionValuesArrayElement = 2; 1832 1833size_t ComputeEncodedElementSize(wasm::ValueType type) { 1834 size_t byte_size = type.value_kind_size(); 1835 DCHECK_EQ(byte_size % kBytesPerExceptionValuesArrayElement, 0); 1836 DCHECK_LE(1, byte_size / kBytesPerExceptionValuesArrayElement); 1837 return byte_size / kBytesPerExceptionValuesArrayElement; 1838} 1839 1840} // namespace 1841 1842#endif // DEBUG 1843 1844// static 1845uint32_t WasmExceptionPackage::GetEncodedSize(const wasm::WasmTag* tag) { 1846 const wasm::WasmTagSig* sig = tag->sig; 1847 uint32_t encoded_size = 0; 1848 for (size_t i = 0; i < sig->parameter_count(); ++i) { 1849 switch (sig->GetParam(i).kind()) { 1850 case wasm::kI32: 1851 case wasm::kF32: 1852 DCHECK_EQ(2, ComputeEncodedElementSize(sig->GetParam(i))); 1853 encoded_size += 2; 1854 break; 1855 case wasm::kI64: 1856 case wasm::kF64: 1857 DCHECK_EQ(4, ComputeEncodedElementSize(sig->GetParam(i))); 1858 encoded_size += 4; 1859 break; 1860 case wasm::kS128: 1861 DCHECK_EQ(8, ComputeEncodedElementSize(sig->GetParam(i))); 1862 encoded_size += 8; 1863 break; 1864 case wasm::kRef: 1865 case wasm::kOptRef: 1866 encoded_size += 1; 1867 break; 1868 case wasm::kRtt: 1869 case wasm::kVoid: 1870 case wasm::kBottom: 1871 case wasm::kI8: 1872 case wasm::kI16: 1873 UNREACHABLE(); 1874 } 1875 } 1876 return encoded_size; 1877} 1878 1879bool WasmExportedFunction::IsWasmExportedFunction(Object object) { 1880 if (!object.IsJSFunction()) return false; 1881 JSFunction js_function = JSFunction::cast(object); 1882 CodeT code = js_function.code(); 1883 if (CodeKind::JS_TO_WASM_FUNCTION != code.kind() && 1884 code.builtin_id() != Builtin::kGenericJSToWasmWrapper && 1885 code.builtin_id() != Builtin::kWasmReturnPromiseOnSuspend) { 1886 return false; 1887 } 1888 DCHECK(js_function.shared().HasWasmExportedFunctionData()); 1889 return true; 1890} 1891 1892bool WasmCapiFunction::IsWasmCapiFunction(Object object) { 1893 if (!object.IsJSFunction()) return false; 1894 JSFunction js_function = JSFunction::cast(object); 1895 // TODO(jkummerow): Enable this when there is a JavaScript wrapper 1896 // able to call this function. 1897 // if (js_function->code()->kind() != CodeKind::WASM_TO_CAPI_FUNCTION) { 1898 // return false; 1899 // } 1900 // DCHECK(js_function->shared()->HasWasmCapiFunctionData()); 1901 // return true; 1902 return js_function.shared().HasWasmCapiFunctionData(); 1903} 1904 1905Handle<WasmCapiFunction> WasmCapiFunction::New( 1906 Isolate* isolate, Address call_target, Handle<Foreign> embedder_data, 1907 Handle<PodArray<wasm::ValueType>> serialized_signature) { 1908 // TODO(jkummerow): Install a JavaScript wrapper. For now, calling 1909 // these functions directly is unsupported; they can only be called 1910 // from Wasm code. 1911 1912 // To support simulator builds, we potentially have to redirect the 1913 // call target (which is an address pointing into the C++ binary). 1914 call_target = ExternalReference::Create(call_target).address(); 1915 1916 // TODO(7748): Support proper typing for external functions. That requires 1917 // global (cross-module) canonicalization of signatures/RTTs. 1918 Handle<Map> rtt = isolate->factory()->wasm_internal_function_map(); 1919 Handle<WasmCapiFunctionData> fun_data = 1920 isolate->factory()->NewWasmCapiFunctionData( 1921 call_target, embedder_data, BUILTIN_CODE(isolate, Illegal), rtt, 1922 serialized_signature); 1923 Handle<SharedFunctionInfo> shared = 1924 isolate->factory()->NewSharedFunctionInfoForWasmCapiFunction(fun_data); 1925 Handle<JSFunction> result = 1926 Factory::JSFunctionBuilder{isolate, shared, isolate->native_context()} 1927 .Build(); 1928 fun_data->internal().set_external(*result); 1929 return Handle<WasmCapiFunction>::cast(result); 1930} 1931 1932WasmInstanceObject WasmExportedFunction::instance() { 1933 return shared().wasm_exported_function_data().instance(); 1934} 1935 1936int WasmExportedFunction::function_index() { 1937 return shared().wasm_exported_function_data().function_index(); 1938} 1939 1940Handle<WasmExportedFunction> WasmExportedFunction::New( 1941 Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index, 1942 int arity, Handle<CodeT> export_wrapper) { 1943 DCHECK( 1944 CodeKind::JS_TO_WASM_FUNCTION == export_wrapper->kind() || 1945 (export_wrapper->is_builtin() && 1946 (export_wrapper->builtin_id() == Builtin::kGenericJSToWasmWrapper || 1947 export_wrapper->builtin_id() == Builtin::kWasmReturnPromiseOnSuspend))); 1948 int num_imported_functions = instance->module()->num_imported_functions; 1949 Handle<Object> ref = 1950 func_index >= num_imported_functions 1951 ? instance 1952 : handle(instance->imported_function_refs().get(func_index), isolate); 1953 1954 Factory* factory = isolate->factory(); 1955 const wasm::FunctionSig* sig = instance->module()->functions[func_index].sig; 1956 Address call_target = instance->GetCallTarget(func_index); 1957 Handle<Map> rtt; 1958 bool has_gc = 1959 instance->module_object().native_module()->enabled_features().has_gc(); 1960 if (has_gc) { 1961 int sig_index = instance->module()->functions[func_index].sig_index; 1962 // TODO(7748): Create funcref RTTs lazily? 1963 rtt = handle(Map::cast(instance->managed_object_maps().get(sig_index)), 1964 isolate); 1965 } else { 1966 rtt = factory->wasm_internal_function_map(); 1967 } 1968 Handle<WasmExportedFunctionData> function_data = 1969 factory->NewWasmExportedFunctionData( 1970 export_wrapper, instance, call_target, ref, func_index, 1971 reinterpret_cast<Address>(sig), wasm::kGenericWrapperBudget, rtt); 1972 1973 MaybeHandle<String> maybe_name; 1974 bool is_asm_js_module = instance->module_object().is_asm_js(); 1975 if (is_asm_js_module) { 1976 // We can use the function name only for asm.js. For WebAssembly, the 1977 // function name is specified as the function_index.toString(). 1978 maybe_name = WasmModuleObject::GetFunctionNameOrNull( 1979 isolate, handle(instance->module_object(), isolate), func_index); 1980 } 1981 Handle<String> name; 1982 if (!maybe_name.ToHandle(&name)) { 1983 base::EmbeddedVector<char, 16> buffer; 1984 int length = SNPrintF(buffer, "%d", func_index); 1985 name = factory 1986 ->NewStringFromOneByte( 1987 base::Vector<uint8_t>::cast(buffer.SubVector(0, length))) 1988 .ToHandleChecked(); 1989 } 1990 Handle<Map> function_map; 1991 switch (instance->module()->origin) { 1992 case wasm::kWasmOrigin: 1993 function_map = isolate->wasm_exported_function_map(); 1994 break; 1995 case wasm::kAsmJsSloppyOrigin: 1996 function_map = isolate->sloppy_function_map(); 1997 break; 1998 case wasm::kAsmJsStrictOrigin: 1999 function_map = isolate->strict_function_map(); 2000 break; 2001 } 2002 2003 Handle<NativeContext> context(isolate->native_context()); 2004 Handle<SharedFunctionInfo> shared = 2005 factory->NewSharedFunctionInfoForWasmExportedFunction(name, 2006 function_data); 2007 Handle<JSFunction> js_function = 2008 Factory::JSFunctionBuilder{isolate, shared, context} 2009 .set_map(function_map) 2010 .Build(); 2011 2012 // According to the spec, exported functions should not have a [[Construct]] 2013 // method. This does not apply to functions exported from asm.js however. 2014 DCHECK_EQ(is_asm_js_module, js_function->IsConstructor()); 2015 shared->set_length(arity); 2016 shared->set_internal_formal_parameter_count(JSParameterCount(arity)); 2017 shared->set_script(instance->module_object().script()); 2018 function_data->internal().set_external(*js_function); 2019 return Handle<WasmExportedFunction>::cast(js_function); 2020} 2021 2022Address WasmExportedFunction::GetWasmCallTarget() { 2023 return instance().GetCallTarget(function_index()); 2024} 2025 2026const wasm::FunctionSig* WasmExportedFunction::sig() { 2027 return instance().module()->functions[function_index()].sig; 2028} 2029 2030bool WasmExportedFunction::MatchesSignature( 2031 const WasmModule* other_module, const wasm::FunctionSig* other_sig) { 2032 const wasm::FunctionSig* sig = this->sig(); 2033 if (sig->parameter_count() != other_sig->parameter_count() || 2034 sig->return_count() != other_sig->return_count()) { 2035 return false; 2036 } 2037 2038 for (int i = 0; i < sig->all().size(); i++) { 2039 if (!wasm::EquivalentTypes(sig->all()[i], other_sig->all()[i], 2040 this->instance().module(), other_module)) { 2041 return false; 2042 } 2043 } 2044 return true; 2045} 2046 2047// static 2048std::unique_ptr<char[]> WasmExportedFunction::GetDebugName( 2049 const wasm::FunctionSig* sig) { 2050 constexpr const char kPrefix[] = "js-to-wasm:"; 2051 // prefix + parameters + delimiter + returns + zero byte 2052 size_t len = strlen(kPrefix) + sig->all().size() + 2; 2053 auto buffer = base::OwnedVector<char>::New(len); 2054 memcpy(buffer.start(), kPrefix, strlen(kPrefix)); 2055 PrintSignature(buffer.as_vector() + strlen(kPrefix), sig); 2056 return buffer.ReleaseData(); 2057} 2058 2059// static 2060bool WasmJSFunction::IsWasmJSFunction(Object object) { 2061 if (!object.IsJSFunction()) return false; 2062 JSFunction js_function = JSFunction::cast(object); 2063 return js_function.shared().HasWasmJSFunctionData(); 2064} 2065 2066Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate, 2067 const wasm::FunctionSig* sig, 2068 Handle<JSReceiver> callable, 2069 Handle<HeapObject> suspender) { 2070 DCHECK_LE(sig->all().size(), kMaxInt); 2071 int sig_size = static_cast<int>(sig->all().size()); 2072 int return_count = static_cast<int>(sig->return_count()); 2073 int parameter_count = static_cast<int>(sig->parameter_count()); 2074 Handle<PodArray<wasm::ValueType>> serialized_sig = 2075 PodArray<wasm::ValueType>::New(isolate, sig_size, AllocationType::kOld); 2076 if (sig_size > 0) { 2077 serialized_sig->copy_in(0, sig->all().begin(), sig_size); 2078 } 2079 // TODO(wasm): Think about caching and sharing the JS-to-JS wrappers per 2080 // signature instead of compiling a new one for every instantiation. 2081 Handle<CodeT> wrapper_code = ToCodeT( 2082 compiler::CompileJSToJSWrapper(isolate, sig, nullptr).ToHandleChecked(), 2083 isolate); 2084 2085 // WasmJSFunctions use on-heap Code objects as call targets, so we can't 2086 // cache the target address, unless the WasmJSFunction wraps a 2087 // WasmExportedFunction. 2088 Address call_target = kNullAddress; 2089 if (WasmExportedFunction::IsWasmExportedFunction(*callable)) { 2090 call_target = WasmExportedFunction::cast(*callable).GetWasmCallTarget(); 2091 } 2092 2093 Factory* factory = isolate->factory(); 2094 // TODO(7748): Support proper typing for external functions. That requires 2095 // global (cross-module) canonicalization of signatures/RTTs. 2096 Handle<Map> rtt = factory->wasm_internal_function_map(); 2097 Handle<WasmJSFunctionData> function_data = factory->NewWasmJSFunctionData( 2098 call_target, callable, return_count, parameter_count, serialized_sig, 2099 wrapper_code, rtt, suspender); 2100 2101 if (wasm::WasmFeatures::FromIsolate(isolate).has_typed_funcref()) { 2102 using CK = compiler::WasmImportCallKind; 2103 int expected_arity = parameter_count; 2104 CK kind = compiler::kDefaultImportCallKind; 2105 if (callable->IsJSFunction()) { 2106 SharedFunctionInfo shared = Handle<JSFunction>::cast(callable)->shared(); 2107 expected_arity = 2108 shared.internal_formal_parameter_count_without_receiver(); 2109 if (expected_arity != parameter_count) { 2110 kind = CK::kJSFunctionArityMismatch; 2111 } 2112 } 2113 // TODO(wasm): Think about caching and sharing the wasm-to-JS wrappers per 2114 // signature instead of compiling a new one for every instantiation. 2115 wasm::Suspend suspend = 2116 suspender.is_null() ? wasm::kNoSuspend : wasm::kSuspend; 2117 DCHECK_IMPLIES(!suspender.is_null(), !suspender->IsUndefined()); 2118 Handle<CodeT> wasm_to_js_wrapper_code = 2119 ToCodeT(compiler::CompileWasmToJSWrapper(isolate, sig, kind, 2120 expected_arity, suspend) 2121 .ToHandleChecked(), 2122 isolate); 2123 function_data->internal().set_code(*wasm_to_js_wrapper_code); 2124 } 2125 2126 Handle<String> name = factory->Function_string(); 2127 if (callable->IsJSFunction()) { 2128 name = JSFunction::GetDebugName(Handle<JSFunction>::cast(callable)); 2129 name = String::Flatten(isolate, name); 2130 } 2131 Handle<NativeContext> context(isolate->native_context()); 2132 Handle<SharedFunctionInfo> shared = 2133 factory->NewSharedFunctionInfoForWasmJSFunction(name, function_data); 2134 Handle<JSFunction> js_function = 2135 Factory::JSFunctionBuilder{isolate, shared, context} 2136 .set_map(isolate->wasm_exported_function_map()) 2137 .Build(); 2138 js_function->shared().set_internal_formal_parameter_count( 2139 JSParameterCount(parameter_count)); 2140 function_data->internal().set_external(*js_function); 2141 return Handle<WasmJSFunction>::cast(js_function); 2142} 2143 2144JSReceiver WasmJSFunction::GetCallable() const { 2145 return JSReceiver::cast(WasmApiFunctionRef::cast( 2146 shared().wasm_js_function_data().internal().ref()) 2147 .callable()); 2148} 2149 2150HeapObject WasmJSFunction::GetSuspender() const { 2151 return WasmApiFunctionRef::cast( 2152 shared().wasm_js_function_data().internal().ref()) 2153 .suspender(); 2154} 2155 2156const wasm::FunctionSig* WasmJSFunction::GetSignature(Zone* zone) { 2157 WasmJSFunctionData function_data = shared().wasm_js_function_data(); 2158 int sig_size = function_data.serialized_signature().length(); 2159 wasm::ValueType* types = zone->NewArray<wasm::ValueType>(sig_size); 2160 if (sig_size > 0) { 2161 function_data.serialized_signature().copy_out(0, types, sig_size); 2162 } 2163 int return_count = function_data.serialized_return_count(); 2164 int parameter_count = function_data.serialized_parameter_count(); 2165 return zone->New<wasm::FunctionSig>(return_count, parameter_count, types); 2166} 2167 2168bool WasmJSFunction::MatchesSignatureForSuspend(const wasm::FunctionSig* sig) { 2169 DCHECK_LE(sig->all().size(), kMaxInt); 2170 int sig_size = static_cast<int>(sig->all().size()); 2171 int parameter_count = static_cast<int>(sig->parameter_count()); 2172 int return_count = static_cast<int>(sig->return_count()); 2173 DisallowHeapAllocation no_alloc; 2174 WasmJSFunctionData function_data = shared().wasm_js_function_data(); 2175 if (parameter_count != function_data.serialized_parameter_count()) { 2176 return false; 2177 } 2178 if (sig_size == 0) return true; // Prevent undefined behavior. 2179 // This function is only called for functions wrapped by a 2180 // WebAssembly.Suspender object, so the return type has to be externref. 2181 CHECK_EQ(function_data.serialized_return_count(), 1); 2182 CHECK_EQ(function_data.serialized_signature().get(0), wasm::kWasmAnyRef); 2183 const wasm::ValueType* expected = sig->all().begin(); 2184 return function_data.serialized_signature().matches( 2185 1, expected + return_count, parameter_count); 2186} 2187 2188// TODO(9495): Update this if function type variance is introduced. 2189bool WasmJSFunction::MatchesSignature(const wasm::FunctionSig* sig) { 2190 DCHECK_LE(sig->all().size(), kMaxInt); 2191 int sig_size = static_cast<int>(sig->all().size()); 2192 int return_count = static_cast<int>(sig->return_count()); 2193 int parameter_count = static_cast<int>(sig->parameter_count()); 2194 DisallowHeapAllocation no_alloc; 2195 WasmJSFunctionData function_data = shared().wasm_js_function_data(); 2196 if (return_count != function_data.serialized_return_count() || 2197 parameter_count != function_data.serialized_parameter_count()) { 2198 return false; 2199 } 2200 if (sig_size == 0) return true; // Prevent undefined behavior. 2201 const wasm::ValueType* expected = sig->all().begin(); 2202 return function_data.serialized_signature().matches(expected, sig_size); 2203} 2204 2205PodArray<wasm::ValueType> WasmCapiFunction::GetSerializedSignature() const { 2206 return shared().wasm_capi_function_data().serialized_signature(); 2207} 2208 2209bool WasmExternalFunction::IsWasmExternalFunction(Object object) { 2210 return WasmExportedFunction::IsWasmExportedFunction(object) || 2211 WasmJSFunction::IsWasmJSFunction(object); 2212} 2213 2214// static 2215MaybeHandle<WasmInternalFunction> WasmInternalFunction::FromExternal( 2216 Handle<Object> external, Isolate* isolate) { 2217 if (WasmExportedFunction::IsWasmExportedFunction(*external) || 2218 WasmJSFunction::IsWasmJSFunction(*external) || 2219 WasmCapiFunction::IsWasmCapiFunction(*external)) { 2220 WasmFunctionData data = WasmFunctionData::cast( 2221 Handle<JSFunction>::cast(external)->shared().function_data( 2222 kAcquireLoad)); 2223 return handle(data.internal(), isolate); 2224 } 2225 return MaybeHandle<WasmInternalFunction>(); 2226} 2227 2228Handle<WasmExceptionTag> WasmExceptionTag::New(Isolate* isolate, int index) { 2229 Handle<WasmExceptionTag> result = 2230 Handle<WasmExceptionTag>::cast(isolate->factory()->NewStruct( 2231 WASM_EXCEPTION_TAG_TYPE, AllocationType::kOld)); 2232 result->set_index(index); 2233 return result; 2234} 2235 2236Handle<AsmWasmData> AsmWasmData::New( 2237 Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, 2238 Handle<FixedArray> export_wrappers, Handle<HeapNumber> uses_bitset) { 2239 const WasmModule* module = native_module->module(); 2240 const bool kUsesLiftoff = false; 2241 size_t memory_estimate = 2242 wasm::WasmCodeManager::EstimateNativeModuleCodeSize( 2243 module, kUsesLiftoff, wasm::DynamicTiering::kDisabled) + 2244 wasm::WasmCodeManager::EstimateNativeModuleMetaDataSize(module); 2245 Handle<Managed<wasm::NativeModule>> managed_native_module = 2246 Managed<wasm::NativeModule>::FromSharedPtr(isolate, memory_estimate, 2247 std::move(native_module)); 2248 Handle<AsmWasmData> result = Handle<AsmWasmData>::cast( 2249 isolate->factory()->NewStruct(ASM_WASM_DATA_TYPE, AllocationType::kOld)); 2250 result->set_managed_native_module(*managed_native_module); 2251 result->set_export_wrappers(*export_wrappers); 2252 result->set_uses_bitset(*uses_bitset); 2253 return result; 2254} 2255 2256namespace wasm { 2257 2258bool TypecheckJSObject(Isolate* isolate, const WasmModule* module, 2259 Handle<Object> value, ValueType expected, 2260 const char** error_message) { 2261 DCHECK(expected.is_reference()); 2262 switch (expected.kind()) { 2263 case kOptRef: 2264 if (value->IsNull(isolate)) return true; 2265 V8_FALLTHROUGH; 2266 case kRef: { 2267 HeapType::Representation repr = expected.heap_representation(); 2268 switch (repr) { 2269 case HeapType::kFunc: { 2270 if (!(WasmExternalFunction::IsWasmExternalFunction(*value) || 2271 WasmCapiFunction::IsWasmCapiFunction(*value))) { 2272 *error_message = 2273 "function-typed object must be null (if nullable) or a Wasm " 2274 "function object"; 2275 return false; 2276 } 2277 return true; 2278 } 2279 case HeapType::kAny: 2280 return true; 2281 case HeapType::kData: 2282 case HeapType::kArray: 2283 case HeapType::kEq: 2284 case HeapType::kI31: { 2285 // TODO(7748): Change this when we have a decision on the JS API for 2286 // structs/arrays. 2287 if (!FLAG_wasm_gc_js_interop) { 2288 Handle<Name> key = isolate->factory()->wasm_wrapped_object_symbol(); 2289 LookupIterator it(isolate, value, key, 2290 LookupIterator::OWN_SKIP_INTERCEPTOR); 2291 if (it.state() != LookupIterator::DATA) { 2292 *error_message = 2293 "eqref/dataref/i31ref object must be null (if nullable) or " 2294 "wrapped with the wasm object wrapper"; 2295 return false; 2296 } 2297 value = it.GetDataValue(); 2298 } 2299 2300 if (repr == HeapType::kI31) { 2301 if (!value->IsSmi()) { 2302 *error_message = "i31ref-typed object cannot be a heap object"; 2303 return false; 2304 } 2305 return true; 2306 } 2307 2308 if (!((repr == HeapType::kEq && value->IsSmi()) || 2309 (repr != HeapType::kArray && value->IsWasmStruct()) || 2310 value->IsWasmArray())) { 2311 *error_message = "object incompatible with wasm type"; 2312 return false; 2313 } 2314 return true; 2315 } 2316 default: 2317 if (module == nullptr) { 2318 *error_message = 2319 "an object defined in JavaScript cannot be compatible with a " 2320 "type defined in a Webassembly module"; 2321 return false; 2322 } 2323 DCHECK(module->has_type(expected.ref_index())); 2324 if (module->has_signature(expected.ref_index())) { 2325 if (WasmExportedFunction::IsWasmExportedFunction(*value)) { 2326 WasmExportedFunction function = 2327 WasmExportedFunction::cast(*value); 2328 const WasmModule* exporting_module = function.instance().module(); 2329 ValueType real_type = ValueType::Ref( 2330 exporting_module->functions[function.function_index()] 2331 .sig_index, 2332 kNonNullable); 2333 if (!IsSubtypeOf(real_type, expected, exporting_module, module)) { 2334 *error_message = 2335 "assigned exported function has to be a subtype of the " 2336 "expected type"; 2337 return false; 2338 } 2339 return true; 2340 } 2341 2342 if (WasmJSFunction::IsWasmJSFunction(*value)) { 2343 // Since a WasmJSFunction cannot refer to indexed types (definable 2344 // only in a module), we do not need full function subtyping. 2345 // TODO(manoskouk): Change this if wasm types can be exported. 2346 if (!WasmJSFunction::cast(*value).MatchesSignature( 2347 module->signature(expected.ref_index()))) { 2348 *error_message = 2349 "assigned WasmJSFunction has to be a subtype of the " 2350 "expected type"; 2351 return false; 2352 } 2353 return true; 2354 } 2355 2356 if (WasmCapiFunction::IsWasmCapiFunction(*value)) { 2357 // Since a WasmCapiFunction cannot refer to indexed types 2358 // (definable only in a module), we do not need full function 2359 // subtyping. 2360 // TODO(manoskouk): Change this if wasm types can be exported. 2361 if (!WasmCapiFunction::cast(*value).MatchesSignature( 2362 module->signature(expected.ref_index()))) { 2363 *error_message = 2364 "assigned WasmCapiFunction has to be a subtype of the " 2365 "expected type"; 2366 return false; 2367 } 2368 return true; 2369 } 2370 2371 *error_message = 2372 "function-typed object must be null (if nullable) or a Wasm " 2373 "function object"; 2374 2375 return false; 2376 } 2377 // TODO(7748): Implement when the JS API for structs/arrays is decided 2378 // on. 2379 *error_message = 2380 "passing struct/array-typed objects between Webassembly and " 2381 "Javascript is not supported yet."; 2382 return false; 2383 } 2384 } 2385 case kRtt: 2386 // TODO(7748): Implement when the JS API for rtts is decided on. 2387 *error_message = 2388 "passing rtts between Webassembly and Javascript is not supported " 2389 "yet."; 2390 return false; 2391 case kI8: 2392 case kI16: 2393 case kI32: 2394 case kI64: 2395 case kF32: 2396 case kF64: 2397 case kS128: 2398 case kVoid: 2399 case kBottom: 2400 UNREACHABLE(); 2401 } 2402} 2403 2404} // namespace wasm 2405 2406} // namespace internal 2407} // namespace v8 2408 2409#undef TRACE_IFT 2410