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 #include "src/utils.h" 7 8 #include "src/assembler-inl.h" 9 #include "src/base/iterator.h" 10 #include "src/code-factory.h" 11 #include "src/compiler/wasm-compiler.h" 12 #include "src/debug/debug-interface.h" 13 #include "src/objects-inl.h" 14 #include "src/objects/debug-objects-inl.h" 15 #include "src/objects/shared-function-info.h" 16 #include "src/trap-handler/trap-handler.h" 17 #include "src/wasm/jump-table-assembler.h" 18 #include "src/wasm/module-compiler.h" 19 #include "src/wasm/module-decoder.h" 20 #include "src/wasm/wasm-code-manager.h" 21 #include "src/wasm/wasm-engine.h" 22 #include "src/wasm/wasm-limits.h" 23 #include "src/wasm/wasm-memory.h" 24 #include "src/wasm/wasm-module.h" 25 #include "src/wasm/wasm-objects-inl.h" 26 #include "src/wasm/wasm-text.h" 27 28 #define TRACE(...) \ 29 do { \ 30 if (FLAG_trace_wasm_instances) PrintF(__VA_ARGS__); \ 31 } while (false) 32 33 #define TRACE_IFT(...) \ 34 do { \ 35 if (false) PrintF(__VA_ARGS__); \ 36 } while (false) 37 38 namespace v8 { 39 namespace internal { 40 41 // Import a few often used types from the wasm namespace. 42 using WasmFunction = wasm::WasmFunction; 43 using WasmModule = wasm::WasmModule; 44 45 namespace { 46 47 // Manages the natively-allocated memory for a WasmInstanceObject. Since 48 // an instance finalizer is not guaranteed to run upon isolate shutdown, 49 // we must use a Managed<WasmInstanceNativeAllocations> to guarantee 50 // it is freed. 51 // Native allocations are the signature ids and targets for indirect call 52 // targets, as well as the call targets for imported functions. 53 class WasmInstanceNativeAllocations { 54 public: 55 // Helper macro to set an internal field and the corresponding field 56 // on an instance. 57 #define SET(instance, field, value) \ 58 { \ 59 auto v = value; \ 60 this->field##_ = v; \ 61 instance->set_##field(v); \ 62 } 63 64 // Allocates initial native storage for a given instance. 65 WasmInstanceNativeAllocations(Handle<WasmInstanceObject> instance, 66 size_t num_imported_functions, 67 size_t num_imported_mutable_globals) { 68 SET(instance, imported_function_targets, 69 reinterpret_cast<Address*>( 70 calloc(num_imported_functions, sizeof(Address)))); 71 SET(instance, imported_mutable_globals, 72 reinterpret_cast<Address*>( 73 calloc(num_imported_mutable_globals, sizeof(Address)))); 74 } 75 ~WasmInstanceNativeAllocations() { free(); } 76 // Frees natively-allocated storage. 77 void free() { 78 ::free(indirect_function_table_sig_ids_); 79 ::free(indirect_function_table_targets_); 80 ::free(imported_function_targets_); 81 ::free(imported_mutable_globals_); 82 indirect_function_table_sig_ids_ = nullptr; 83 indirect_function_table_targets_ = nullptr; 84 imported_function_targets_ = nullptr; 85 imported_mutable_globals_ = nullptr; 86 } 87 // Resizes the indirect function table. 88 void resize_indirect_function_table(Isolate* isolate, 89 Handle<WasmInstanceObject> instance, 90 uint32_t new_size) { 91 uint32_t old_size = instance->indirect_function_table_size(); 92 void* new_sig_ids = nullptr; 93 void* new_targets = nullptr; 94 Handle<FixedArray> new_instances; 95 if (indirect_function_table_sig_ids_) { 96 // Reallocate the old storage. 97 new_sig_ids = realloc(indirect_function_table_sig_ids_, 98 new_size * sizeof(uint32_t)); 99 new_targets = 100 realloc(indirect_function_table_targets_, new_size * sizeof(Address)); 101 102 Handle<FixedArray> old(instance->indirect_function_table_instances(), 103 isolate); 104 new_instances = isolate->factory()->CopyFixedArrayAndGrow( 105 old, static_cast<int>(new_size - old_size)); 106 } else { 107 // Allocate new storage. 108 new_sig_ids = malloc(new_size * sizeof(uint32_t)); 109 new_targets = malloc(new_size * sizeof(Address)); 110 new_instances = 111 isolate->factory()->NewFixedArray(static_cast<int>(new_size)); 112 } 113 // Initialize new entries. 114 instance->set_indirect_function_table_size(new_size); 115 SET(instance, indirect_function_table_sig_ids, 116 reinterpret_cast<uint32_t*>(new_sig_ids)); 117 SET(instance, indirect_function_table_targets, 118 reinterpret_cast<Address*>(new_targets)); 119 120 instance->set_indirect_function_table_instances(*new_instances); 121 for (uint32_t j = old_size; j < new_size; j++) { 122 IndirectFunctionTableEntry(instance, static_cast<int>(j)).clear(); 123 } 124 } 125 uint32_t* indirect_function_table_sig_ids_ = nullptr; 126 Address* indirect_function_table_targets_ = nullptr; 127 Address* imported_function_targets_ = nullptr; 128 Address* imported_mutable_globals_ = nullptr; 129 #undef SET 130 }; 131 132 size_t EstimateNativeAllocationsSize(const WasmModule* module) { 133 size_t estimate = sizeof(WasmInstanceNativeAllocations) + 134 (1 * kPointerSize * module->num_imported_mutable_globals) + 135 (2 * kPointerSize * module->num_imported_functions); 136 for (auto& table : module->tables) { 137 estimate += 3 * kPointerSize * table.initial_size; 138 } 139 return estimate; 140 } 141 142 WasmInstanceNativeAllocations* GetNativeAllocations( 143 WasmInstanceObject* instance) { 144 return reinterpret_cast<Managed<WasmInstanceNativeAllocations>*>( 145 instance->managed_native_allocations()) 146 ->raw(); 147 } 148 149 #ifdef DEBUG 150 bool IsBreakablePosition(wasm::NativeModule* native_module, int func_index, 151 int offset_in_func) { 152 AccountingAllocator alloc; 153 Zone tmp(&alloc, ZONE_NAME); 154 wasm::BodyLocalDecls locals(&tmp); 155 const byte* module_start = native_module->wire_bytes().start(); 156 const WasmFunction& func = native_module->module()->functions[func_index]; 157 wasm::BytecodeIterator iterator(module_start + func.code.offset(), 158 module_start + func.code.end_offset(), 159 &locals); 160 DCHECK_LT(0, locals.encoded_size); 161 for (uint32_t offset : iterator.offsets()) { 162 if (offset > static_cast<uint32_t>(offset_in_func)) break; 163 if (offset == static_cast<uint32_t>(offset_in_func)) return true; 164 } 165 return false; 166 } 167 #endif // DEBUG 168 169 enum DispatchTableElements : int { 170 kDispatchTableInstanceOffset, 171 kDispatchTableIndexOffset, 172 kDispatchTableFunctionTableOffset, 173 // Marker: 174 kDispatchTableNumElements 175 }; 176 177 } // namespace 178 179 // static 180 Handle<WasmModuleObject> WasmModuleObject::New( 181 Isolate* isolate, const wasm::WasmFeatures& enabled, 182 std::shared_ptr<const wasm::WasmModule> shared_module, wasm::ModuleEnv& env, 183 OwnedVector<const uint8_t> wire_bytes, Handle<Script> script, 184 Handle<ByteArray> asm_js_offset_table) { 185 DCHECK_EQ(shared_module.get(), env.module); 186 187 // Create a new {NativeModule} first. 188 size_t native_memory_estimate = 189 isolate->wasm_engine()->code_manager()->EstimateNativeModuleSize( 190 env.module); 191 auto native_module = isolate->wasm_engine()->code_manager()->NewNativeModule( 192 isolate, enabled, native_memory_estimate, 193 wasm::NativeModule::kCanAllocateMoreMemory, std::move(shared_module), 194 env); 195 native_module->set_wire_bytes(std::move(wire_bytes)); 196 native_module->SetRuntimeStubs(isolate); 197 198 // Delegate to the shared {WasmModuleObject::New} allocator. 199 Handle<WasmModuleObject> module_object = 200 New(isolate, std::move(native_module), script); 201 if (!asm_js_offset_table.is_null()) { 202 module_object->set_asm_js_offset_table(*asm_js_offset_table); 203 } 204 return module_object; 205 } 206 207 // static 208 Handle<WasmModuleObject> WasmModuleObject::New( 209 Isolate* isolate, std::shared_ptr<wasm::NativeModule> native_module, 210 Handle<Script> script) { 211 int export_wrapper_size = 212 static_cast<int>(native_module->module()->num_exported_functions); 213 Handle<FixedArray> export_wrappers = 214 isolate->factory()->NewFixedArray(export_wrapper_size, TENURED); 215 216 // Use the given shared {NativeModule}, but increase its reference count by 217 // allocating a new {Managed<T>} that the {WasmModuleObject} references. 218 size_t native_memory_estimate = 219 isolate->wasm_engine()->code_manager()->EstimateNativeModuleSize( 220 native_module->module()); 221 size_t memory_estimate = 222 EstimateWasmModuleSize(native_module->module()) + native_memory_estimate; 223 Handle<Managed<wasm::NativeModule>> managed_native_module = 224 Managed<wasm::NativeModule>::FromSharedPtr(isolate, memory_estimate, 225 std::move(native_module)); 226 227 Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast( 228 isolate->factory()->NewJSObject(isolate->wasm_module_constructor())); 229 module_object->set_export_wrappers(*export_wrappers); 230 if (script->type() == Script::TYPE_WASM) { 231 script->set_wasm_module_object(*module_object); 232 } 233 module_object->set_script(*script); 234 module_object->set_weak_instance_list( 235 ReadOnlyRoots(isolate).empty_weak_array_list()); 236 module_object->set_managed_native_module(*managed_native_module); 237 return module_object; 238 } 239 240 bool WasmModuleObject::SetBreakPoint(Handle<WasmModuleObject> module_object, 241 int* position, 242 Handle<BreakPoint> break_point) { 243 Isolate* isolate = module_object->GetIsolate(); 244 245 // Find the function for this breakpoint. 246 int func_index = module_object->GetContainingFunction(*position); 247 if (func_index < 0) return false; 248 const WasmFunction& func = module_object->module()->functions[func_index]; 249 int offset_in_func = *position - func.code.offset(); 250 251 // According to the current design, we should only be called with valid 252 // breakable positions. 253 DCHECK(IsBreakablePosition(module_object->native_module(), func_index, 254 offset_in_func)); 255 256 // Insert new break point into break_positions of module object. 257 WasmModuleObject::AddBreakpoint(module_object, *position, break_point); 258 259 // Iterate over all instances of this module and tell them to set this new 260 // breakpoint. We do this using the weak list of all instances. 261 Handle<WeakArrayList> weak_instance_list(module_object->weak_instance_list(), 262 isolate); 263 for (int i = 0; i < weak_instance_list->length(); ++i) { 264 MaybeObject* maybe_instance = weak_instance_list->Get(i); 265 if (maybe_instance->IsWeakHeapObject()) { 266 Handle<WasmInstanceObject> instance( 267 WasmInstanceObject::cast(maybe_instance->ToWeakHeapObject()), 268 isolate); 269 Handle<WasmDebugInfo> debug_info = 270 WasmInstanceObject::GetOrCreateDebugInfo(instance); 271 WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func); 272 } 273 } 274 275 return true; 276 } 277 278 namespace { 279 280 int GetBreakpointPos(Isolate* isolate, Object* break_point_info_or_undef) { 281 if (break_point_info_or_undef->IsUndefined(isolate)) return kMaxInt; 282 return BreakPointInfo::cast(break_point_info_or_undef)->source_position(); 283 } 284 285 int FindBreakpointInfoInsertPos(Isolate* isolate, 286 Handle<FixedArray> breakpoint_infos, 287 int position) { 288 // Find insert location via binary search, taking care of undefined values on 289 // the right. Position is always greater than zero. 290 DCHECK_LT(0, position); 291 292 int left = 0; // inclusive 293 int right = breakpoint_infos->length(); // exclusive 294 while (right - left > 1) { 295 int mid = left + (right - left) / 2; 296 Object* mid_obj = breakpoint_infos->get(mid); 297 if (GetBreakpointPos(isolate, mid_obj) <= position) { 298 left = mid; 299 } else { 300 right = mid; 301 } 302 } 303 304 int left_pos = GetBreakpointPos(isolate, breakpoint_infos->get(left)); 305 return left_pos < position ? left + 1 : left; 306 } 307 308 } // namespace 309 310 void WasmModuleObject::AddBreakpoint(Handle<WasmModuleObject> module_object, 311 int position, 312 Handle<BreakPoint> break_point) { 313 Isolate* isolate = module_object->GetIsolate(); 314 Handle<FixedArray> breakpoint_infos; 315 if (module_object->has_breakpoint_infos()) { 316 breakpoint_infos = handle(module_object->breakpoint_infos(), isolate); 317 } else { 318 breakpoint_infos = isolate->factory()->NewFixedArray(4, TENURED); 319 module_object->set_breakpoint_infos(*breakpoint_infos); 320 } 321 322 int insert_pos = 323 FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position); 324 325 // If a BreakPointInfo object already exists for this position, add the new 326 // breakpoint object and return. 327 if (insert_pos < breakpoint_infos->length() && 328 GetBreakpointPos(isolate, breakpoint_infos->get(insert_pos)) == 329 position) { 330 Handle<BreakPointInfo> old_info( 331 BreakPointInfo::cast(breakpoint_infos->get(insert_pos)), isolate); 332 BreakPointInfo::SetBreakPoint(isolate, old_info, break_point); 333 return; 334 } 335 336 // Enlarge break positions array if necessary. 337 bool need_realloc = !breakpoint_infos->get(breakpoint_infos->length() - 1) 338 ->IsUndefined(isolate); 339 Handle<FixedArray> new_breakpoint_infos = breakpoint_infos; 340 if (need_realloc) { 341 new_breakpoint_infos = isolate->factory()->NewFixedArray( 342 2 * breakpoint_infos->length(), TENURED); 343 module_object->set_breakpoint_infos(*new_breakpoint_infos); 344 // Copy over the entries [0, insert_pos). 345 for (int i = 0; i < insert_pos; ++i) 346 new_breakpoint_infos->set(i, breakpoint_infos->get(i)); 347 } 348 349 // Move elements [insert_pos, ...] up by one. 350 for (int i = breakpoint_infos->length() - 1; i >= insert_pos; --i) { 351 Object* entry = breakpoint_infos->get(i); 352 if (entry->IsUndefined(isolate)) continue; 353 new_breakpoint_infos->set(i + 1, entry); 354 } 355 356 // Generate new BreakpointInfo. 357 Handle<BreakPointInfo> breakpoint_info = 358 isolate->factory()->NewBreakPointInfo(position); 359 BreakPointInfo::SetBreakPoint(isolate, breakpoint_info, break_point); 360 361 // Now insert new position at insert_pos. 362 new_breakpoint_infos->set(insert_pos, *breakpoint_info); 363 } 364 365 void WasmModuleObject::SetBreakpointsOnNewInstance( 366 Handle<WasmModuleObject> module_object, 367 Handle<WasmInstanceObject> instance) { 368 if (!module_object->has_breakpoint_infos()) return; 369 Isolate* isolate = module_object->GetIsolate(); 370 Handle<WasmDebugInfo> debug_info = 371 WasmInstanceObject::GetOrCreateDebugInfo(instance); 372 373 Handle<FixedArray> breakpoint_infos(module_object->breakpoint_infos(), 374 isolate); 375 // If the array exists, it should not be empty. 376 DCHECK_LT(0, breakpoint_infos->length()); 377 378 for (int i = 0, e = breakpoint_infos->length(); i < e; ++i) { 379 Handle<Object> obj(breakpoint_infos->get(i), isolate); 380 if (obj->IsUndefined(isolate)) { 381 for (; i < e; ++i) { 382 DCHECK(breakpoint_infos->get(i)->IsUndefined(isolate)); 383 } 384 break; 385 } 386 Handle<BreakPointInfo> breakpoint_info = Handle<BreakPointInfo>::cast(obj); 387 int position = breakpoint_info->source_position(); 388 389 // Find the function for this breakpoint, and set the breakpoint. 390 int func_index = module_object->GetContainingFunction(position); 391 DCHECK_LE(0, func_index); 392 const WasmFunction& func = module_object->module()->functions[func_index]; 393 int offset_in_func = position - func.code.offset(); 394 WasmDebugInfo::SetBreakpoint(debug_info, func_index, offset_in_func); 395 } 396 } 397 398 namespace { 399 400 enum AsmJsOffsetTableEntryLayout { 401 kOTEByteOffset, 402 kOTECallPosition, 403 kOTENumberConvPosition, 404 kOTESize 405 }; 406 407 Handle<ByteArray> GetDecodedAsmJsOffsetTable( 408 Handle<WasmModuleObject> module_object, Isolate* isolate) { 409 DCHECK(module_object->is_asm_js()); 410 Handle<ByteArray> offset_table(module_object->asm_js_offset_table(), isolate); 411 412 // The last byte in the asm_js_offset_tables ByteArray tells whether it is 413 // still encoded (0) or decoded (1). 414 enum AsmJsTableType : int { Encoded = 0, Decoded = 1 }; 415 int table_type = offset_table->get(offset_table->length() - 1); 416 DCHECK(table_type == Encoded || table_type == Decoded); 417 if (table_type == Decoded) return offset_table; 418 419 wasm::AsmJsOffsetsResult asm_offsets; 420 { 421 DisallowHeapAllocation no_gc; 422 byte* bytes_start = offset_table->GetDataStartAddress(); 423 byte* bytes_end = reinterpret_cast<byte*>( 424 reinterpret_cast<Address>(bytes_start) + offset_table->length() - 1); 425 asm_offsets = wasm::DecodeAsmJsOffsets(bytes_start, bytes_end); 426 } 427 // Wasm bytes must be valid and must contain asm.js offset table. 428 DCHECK(asm_offsets.ok()); 429 DCHECK_GE(kMaxInt, asm_offsets.val.size()); 430 int num_functions = static_cast<int>(asm_offsets.val.size()); 431 int num_imported_functions = 432 static_cast<int>(module_object->module()->num_imported_functions); 433 DCHECK_EQ(module_object->module()->functions.size(), 434 static_cast<size_t>(num_functions) + num_imported_functions); 435 int num_entries = 0; 436 for (int func = 0; func < num_functions; ++func) { 437 size_t new_size = asm_offsets.val[func].size(); 438 DCHECK_LE(new_size, static_cast<size_t>(kMaxInt) - num_entries); 439 num_entries += static_cast<int>(new_size); 440 } 441 // One byte to encode that this is a decoded table. 442 DCHECK_GE(kMaxInt, 443 1 + static_cast<uint64_t>(num_entries) * kOTESize * kIntSize); 444 int total_size = 1 + num_entries * kOTESize * kIntSize; 445 Handle<ByteArray> decoded_table = 446 isolate->factory()->NewByteArray(total_size, TENURED); 447 decoded_table->set(total_size - 1, AsmJsTableType::Decoded); 448 module_object->set_asm_js_offset_table(*decoded_table); 449 450 int idx = 0; 451 const std::vector<WasmFunction>& wasm_funs = 452 module_object->module()->functions; 453 for (int func = 0; func < num_functions; ++func) { 454 std::vector<wasm::AsmJsOffsetEntry>& func_asm_offsets = 455 asm_offsets.val[func]; 456 if (func_asm_offsets.empty()) continue; 457 int func_offset = wasm_funs[num_imported_functions + func].code.offset(); 458 for (wasm::AsmJsOffsetEntry& e : func_asm_offsets) { 459 // Byte offsets must be strictly monotonously increasing: 460 DCHECK_IMPLIES(idx > 0, func_offset + e.byte_offset > 461 decoded_table->get_int(idx - kOTESize)); 462 decoded_table->set_int(idx + kOTEByteOffset, func_offset + e.byte_offset); 463 decoded_table->set_int(idx + kOTECallPosition, e.source_position_call); 464 decoded_table->set_int(idx + kOTENumberConvPosition, 465 e.source_position_number_conversion); 466 idx += kOTESize; 467 } 468 } 469 DCHECK_EQ(total_size, idx * kIntSize + 1); 470 return decoded_table; 471 } 472 473 } // namespace 474 475 int WasmModuleObject::GetSourcePosition(Handle<WasmModuleObject> module_object, 476 uint32_t func_index, 477 uint32_t byte_offset, 478 bool is_at_number_conversion) { 479 Isolate* isolate = module_object->GetIsolate(); 480 const WasmModule* module = module_object->module(); 481 482 if (module->origin != wasm::kAsmJsOrigin) { 483 // for non-asm.js modules, we just add the function's start offset 484 // to make a module-relative position. 485 return byte_offset + module_object->GetFunctionOffset(func_index); 486 } 487 488 // asm.js modules have an additional offset table that must be searched. 489 Handle<ByteArray> offset_table = 490 GetDecodedAsmJsOffsetTable(module_object, isolate); 491 492 DCHECK_LT(func_index, module->functions.size()); 493 uint32_t func_code_offset = module->functions[func_index].code.offset(); 494 uint32_t total_offset = func_code_offset + byte_offset; 495 496 // Binary search for the total byte offset. 497 int left = 0; // inclusive 498 int right = offset_table->length() / kIntSize / kOTESize; // exclusive 499 DCHECK_LT(left, right); 500 while (right - left > 1) { 501 int mid = left + (right - left) / 2; 502 int mid_entry = offset_table->get_int(kOTESize * mid); 503 DCHECK_GE(kMaxInt, mid_entry); 504 if (static_cast<uint32_t>(mid_entry) <= total_offset) { 505 left = mid; 506 } else { 507 right = mid; 508 } 509 } 510 // There should be an entry for each position that could show up on the stack 511 // trace: 512 DCHECK_EQ(total_offset, offset_table->get_int(kOTESize * left)); 513 int idx = is_at_number_conversion ? kOTENumberConvPosition : kOTECallPosition; 514 return offset_table->get_int(kOTESize * left + idx); 515 } 516 517 v8::debug::WasmDisassembly WasmModuleObject::DisassembleFunction( 518 int func_index) { 519 DisallowHeapAllocation no_gc; 520 521 if (func_index < 0 || 522 static_cast<uint32_t>(func_index) >= module()->functions.size()) 523 return {}; 524 525 Vector<const byte> wire_bytes = native_module()->wire_bytes(); 526 527 std::ostringstream disassembly_os; 528 v8::debug::WasmDisassembly::OffsetTable offset_table; 529 530 PrintWasmText(module(), wire_bytes, static_cast<uint32_t>(func_index), 531 disassembly_os, &offset_table); 532 533 return {disassembly_os.str(), std::move(offset_table)}; 534 } 535 536 bool WasmModuleObject::GetPossibleBreakpoints( 537 const v8::debug::Location& start, const v8::debug::Location& end, 538 std::vector<v8::debug::BreakLocation>* locations) { 539 DisallowHeapAllocation no_gc; 540 541 const std::vector<WasmFunction>& functions = module()->functions; 542 if (start.GetLineNumber() < 0 || start.GetColumnNumber() < 0 || 543 (!end.IsEmpty() && 544 (end.GetLineNumber() < 0 || end.GetColumnNumber() < 0))) 545 return false; 546 547 // start_func_index, start_offset and end_func_index is inclusive. 548 // end_offset is exclusive. 549 // start_offset and end_offset are module-relative byte offsets. 550 uint32_t start_func_index = start.GetLineNumber(); 551 if (start_func_index >= functions.size()) return false; 552 int start_func_len = functions[start_func_index].code.length(); 553 if (start.GetColumnNumber() > start_func_len) return false; 554 uint32_t start_offset = 555 functions[start_func_index].code.offset() + start.GetColumnNumber(); 556 uint32_t end_func_index; 557 uint32_t end_offset; 558 if (end.IsEmpty()) { 559 // Default: everything till the end of the Script. 560 end_func_index = static_cast<uint32_t>(functions.size() - 1); 561 end_offset = functions[end_func_index].code.end_offset(); 562 } else { 563 // If end is specified: Use it and check for valid input. 564 end_func_index = static_cast<uint32_t>(end.GetLineNumber()); 565 566 // Special case: Stop before the start of the next function. Change to: Stop 567 // at the end of the function before, such that we don't disassemble the 568 // next function also. 569 if (end.GetColumnNumber() == 0 && end_func_index > 0) { 570 --end_func_index; 571 end_offset = functions[end_func_index].code.end_offset(); 572 } else { 573 if (end_func_index >= functions.size()) return false; 574 end_offset = 575 functions[end_func_index].code.offset() + end.GetColumnNumber(); 576 if (end_offset > functions[end_func_index].code.end_offset()) 577 return false; 578 } 579 } 580 581 AccountingAllocator alloc; 582 Zone tmp(&alloc, ZONE_NAME); 583 const byte* module_start = native_module()->wire_bytes().start(); 584 585 for (uint32_t func_idx = start_func_index; func_idx <= end_func_index; 586 ++func_idx) { 587 const WasmFunction& func = functions[func_idx]; 588 if (func.code.length() == 0) continue; 589 590 wasm::BodyLocalDecls locals(&tmp); 591 wasm::BytecodeIterator iterator(module_start + func.code.offset(), 592 module_start + func.code.end_offset(), 593 &locals); 594 DCHECK_LT(0u, locals.encoded_size); 595 for (uint32_t offset : iterator.offsets()) { 596 uint32_t total_offset = func.code.offset() + offset; 597 if (total_offset >= end_offset) { 598 DCHECK_EQ(end_func_index, func_idx); 599 break; 600 } 601 if (total_offset < start_offset) continue; 602 locations->emplace_back(func_idx, offset, debug::kCommonBreakLocation); 603 } 604 } 605 return true; 606 } 607 608 MaybeHandle<FixedArray> WasmModuleObject::CheckBreakPoints( 609 Isolate* isolate, Handle<WasmModuleObject> module_object, int position) { 610 if (!module_object->has_breakpoint_infos()) return {}; 611 612 Handle<FixedArray> breakpoint_infos(module_object->breakpoint_infos(), 613 isolate); 614 int insert_pos = 615 FindBreakpointInfoInsertPos(isolate, breakpoint_infos, position); 616 if (insert_pos >= breakpoint_infos->length()) return {}; 617 618 Handle<Object> maybe_breakpoint_info(breakpoint_infos->get(insert_pos), 619 isolate); 620 if (maybe_breakpoint_info->IsUndefined(isolate)) return {}; 621 Handle<BreakPointInfo> breakpoint_info = 622 Handle<BreakPointInfo>::cast(maybe_breakpoint_info); 623 if (breakpoint_info->source_position() != position) return {}; 624 625 // There is no support for conditional break points. Just assume that every 626 // break point always hits. 627 Handle<Object> break_points(breakpoint_info->break_points(), isolate); 628 if (break_points->IsFixedArray()) { 629 return Handle<FixedArray>::cast(break_points); 630 } 631 Handle<FixedArray> break_points_hit = isolate->factory()->NewFixedArray(1); 632 break_points_hit->set(0, *break_points); 633 return break_points_hit; 634 } 635 636 MaybeHandle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes( 637 Isolate* isolate, Handle<WasmModuleObject> module_object, 638 wasm::WireBytesRef ref) { 639 // TODO(wasm): cache strings from modules if it's a performance win. 640 Vector<const uint8_t> wire_bytes = 641 module_object->native_module()->wire_bytes(); 642 return ExtractUtf8StringFromModuleBytes(isolate, wire_bytes, ref); 643 } 644 645 MaybeHandle<String> WasmModuleObject::ExtractUtf8StringFromModuleBytes( 646 Isolate* isolate, Vector<const uint8_t> wire_bytes, 647 wasm::WireBytesRef ref) { 648 Vector<const uint8_t> name_vec = wire_bytes + ref.offset(); 649 name_vec.Truncate(ref.length()); 650 // UTF8 validation happens at decode time. 651 DCHECK(unibrow::Utf8::ValidateEncoding(name_vec.start(), name_vec.length())); 652 return isolate->factory()->NewStringFromUtf8( 653 Vector<const char>::cast(name_vec)); 654 } 655 656 MaybeHandle<String> WasmModuleObject::GetModuleNameOrNull( 657 Isolate* isolate, Handle<WasmModuleObject> module_object) { 658 const WasmModule* module = module_object->module(); 659 if (!module->name.is_set()) return {}; 660 return ExtractUtf8StringFromModuleBytes(isolate, module_object, module->name); 661 } 662 663 MaybeHandle<String> WasmModuleObject::GetFunctionNameOrNull( 664 Isolate* isolate, Handle<WasmModuleObject> module_object, 665 uint32_t func_index) { 666 DCHECK_LT(func_index, module_object->module()->functions.size()); 667 wasm::WireBytesRef name = module_object->module()->LookupFunctionName( 668 wasm::ModuleWireBytes(module_object->native_module()->wire_bytes()), 669 func_index); 670 if (!name.is_set()) return {}; 671 return ExtractUtf8StringFromModuleBytes(isolate, module_object, name); 672 } 673 674 Handle<String> WasmModuleObject::GetFunctionName( 675 Isolate* isolate, Handle<WasmModuleObject> module_object, 676 uint32_t func_index) { 677 MaybeHandle<String> name = 678 GetFunctionNameOrNull(isolate, module_object, func_index); 679 if (!name.is_null()) return name.ToHandleChecked(); 680 EmbeddedVector<char, 32> buffer; 681 int length = SNPrintF(buffer, "wasm-function[%u]", func_index); 682 return isolate->factory() 683 ->NewStringFromOneByte(Vector<uint8_t>::cast(buffer.SubVector(0, length))) 684 .ToHandleChecked(); 685 } 686 687 Vector<const uint8_t> WasmModuleObject::GetRawFunctionName( 688 uint32_t func_index) { 689 DCHECK_GT(module()->functions.size(), func_index); 690 wasm::ModuleWireBytes wire_bytes(native_module()->wire_bytes()); 691 wasm::WireBytesRef name_ref = 692 module()->LookupFunctionName(wire_bytes, func_index); 693 wasm::WasmName name = wire_bytes.GetName(name_ref); 694 return Vector<const uint8_t>::cast(name); 695 } 696 697 int WasmModuleObject::GetFunctionOffset(uint32_t func_index) { 698 const std::vector<WasmFunction>& functions = module()->functions; 699 if (static_cast<uint32_t>(func_index) >= functions.size()) return -1; 700 DCHECK_GE(kMaxInt, functions[func_index].code.offset()); 701 return static_cast<int>(functions[func_index].code.offset()); 702 } 703 704 int WasmModuleObject::GetContainingFunction(uint32_t byte_offset) { 705 const std::vector<WasmFunction>& functions = module()->functions; 706 707 // Binary search for a function containing the given position. 708 int left = 0; // inclusive 709 int right = static_cast<int>(functions.size()); // exclusive 710 if (right == 0) return false; 711 while (right - left > 1) { 712 int mid = left + (right - left) / 2; 713 if (functions[mid].code.offset() <= byte_offset) { 714 left = mid; 715 } else { 716 right = mid; 717 } 718 } 719 // If the found function does not contains the given position, return -1. 720 const WasmFunction& func = functions[left]; 721 if (byte_offset < func.code.offset() || 722 byte_offset >= func.code.end_offset()) { 723 return -1; 724 } 725 726 return left; 727 } 728 729 bool WasmModuleObject::GetPositionInfo(uint32_t position, 730 Script::PositionInfo* info) { 731 int func_index = GetContainingFunction(position); 732 if (func_index < 0) return false; 733 734 const WasmFunction& function = module()->functions[func_index]; 735 736 info->line = func_index; 737 info->column = position - function.code.offset(); 738 info->line_start = function.code.offset(); 739 info->line_end = function.code.end_offset(); 740 return true; 741 } 742 743 Handle<WasmTableObject> WasmTableObject::New(Isolate* isolate, uint32_t initial, 744 int64_t maximum, 745 Handle<FixedArray>* js_functions) { 746 Handle<JSFunction> table_ctor( 747 isolate->native_context()->wasm_table_constructor(), isolate); 748 auto table_obj = Handle<WasmTableObject>::cast( 749 isolate->factory()->NewJSObject(table_ctor)); 750 751 *js_functions = isolate->factory()->NewFixedArray(initial); 752 Object* null = ReadOnlyRoots(isolate).null_value(); 753 for (int i = 0; i < static_cast<int>(initial); ++i) { 754 (*js_functions)->set(i, null); 755 } 756 table_obj->set_functions(**js_functions); 757 DCHECK_EQ(maximum, static_cast<int>(maximum)); 758 Handle<Object> max = isolate->factory()->NewNumber(maximum); 759 table_obj->set_maximum_length(*max); 760 761 table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array()); 762 return Handle<WasmTableObject>::cast(table_obj); 763 } 764 765 void WasmTableObject::AddDispatchTable(Isolate* isolate, 766 Handle<WasmTableObject> table_obj, 767 Handle<WasmInstanceObject> instance, 768 int table_index) { 769 Handle<FixedArray> dispatch_tables(table_obj->dispatch_tables(), isolate); 770 int old_length = dispatch_tables->length(); 771 DCHECK_EQ(0, old_length % kDispatchTableNumElements); 772 773 if (instance.is_null()) return; 774 // TODO(titzer): use weak cells here to avoid leaking instances. 775 776 // Grow the dispatch table and add a new entry at the end. 777 Handle<FixedArray> new_dispatch_tables = 778 isolate->factory()->CopyFixedArrayAndGrow(dispatch_tables, 779 kDispatchTableNumElements); 780 781 new_dispatch_tables->set(old_length + kDispatchTableInstanceOffset, 782 *instance); 783 new_dispatch_tables->set(old_length + kDispatchTableIndexOffset, 784 Smi::FromInt(table_index)); 785 786 table_obj->set_dispatch_tables(*new_dispatch_tables); 787 } 788 789 void WasmTableObject::Grow(Isolate* isolate, uint32_t count) { 790 if (count == 0) return; // Degenerate case: nothing to do. 791 792 Handle<FixedArray> dispatch_tables(this->dispatch_tables(), isolate); 793 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); 794 uint32_t old_size = functions()->length(); 795 796 // Tables are stored in the instance object, no code patching is 797 // necessary. We simply have to grow the raw tables in each instance 798 // that has imported this table. 799 800 // TODO(titzer): replace the dispatch table with a weak list of all 801 // the instances that import a given table. 802 for (int i = 0; i < dispatch_tables->length(); 803 i += kDispatchTableNumElements) { 804 Handle<WasmInstanceObject> instance( 805 WasmInstanceObject::cast(dispatch_tables->get(i)), isolate); 806 DCHECK_EQ(old_size, instance->indirect_function_table_size()); 807 uint32_t new_size = old_size + count; 808 WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(instance, 809 new_size); 810 } 811 } 812 813 void WasmTableObject::Set(Isolate* isolate, Handle<WasmTableObject> table, 814 int32_t table_index, Handle<JSFunction> function) { 815 Handle<FixedArray> array(table->functions(), isolate); 816 if (function.is_null()) { 817 ClearDispatchTables(isolate, table, table_index); // Degenerate case. 818 array->set(table_index, ReadOnlyRoots(isolate).null_value()); 819 return; 820 } 821 822 // TODO(titzer): Change this to MaybeHandle<WasmExportedFunction> 823 DCHECK(WasmExportedFunction::IsWasmExportedFunction(*function)); 824 auto exported_function = Handle<WasmExportedFunction>::cast(function); 825 Handle<WasmInstanceObject> other_instance(exported_function->instance(), 826 isolate); 827 int func_index = exported_function->function_index(); 828 auto* wasm_function = &other_instance->module()->functions[func_index]; 829 DCHECK_NOT_NULL(wasm_function); 830 DCHECK_NOT_NULL(wasm_function->sig); 831 Address call_target = exported_function->GetWasmCallTarget(); 832 UpdateDispatchTables(isolate, table, table_index, wasm_function->sig, 833 handle(exported_function->instance(), isolate), 834 call_target); 835 array->set(table_index, *function); 836 } 837 838 void WasmTableObject::UpdateDispatchTables( 839 Isolate* isolate, Handle<WasmTableObject> table, int table_index, 840 wasm::FunctionSig* sig, Handle<WasmInstanceObject> from_instance, 841 Address call_target) { 842 // We simply need to update the IFTs for each instance that imports 843 // this table. 844 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate); 845 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); 846 847 for (int i = 0; i < dispatch_tables->length(); 848 i += kDispatchTableNumElements) { 849 Handle<WasmInstanceObject> to_instance( 850 WasmInstanceObject::cast( 851 dispatch_tables->get(i + kDispatchTableInstanceOffset)), 852 isolate); 853 // Note that {SignatureMap::Find} may return {-1} if the signature is 854 // not found; it will simply never match any check. 855 auto sig_id = to_instance->module()->signature_map.Find(*sig); 856 IndirectFunctionTableEntry(to_instance, table_index) 857 .set(sig_id, *from_instance, call_target); 858 } 859 } 860 861 void WasmTableObject::ClearDispatchTables(Isolate* isolate, 862 Handle<WasmTableObject> table, 863 int index) { 864 Handle<FixedArray> dispatch_tables(table->dispatch_tables(), isolate); 865 DCHECK_EQ(0, dispatch_tables->length() % kDispatchTableNumElements); 866 for (int i = 0; i < dispatch_tables->length(); 867 i += kDispatchTableNumElements) { 868 Handle<WasmInstanceObject> target_instance( 869 WasmInstanceObject::cast( 870 dispatch_tables->get(i + kDispatchTableInstanceOffset)), 871 isolate); 872 DCHECK_LT(index, target_instance->indirect_function_table_size()); 873 IndirectFunctionTableEntry(target_instance, index).clear(); 874 } 875 } 876 877 namespace { 878 MaybeHandle<JSArrayBuffer> GrowMemoryBuffer(Isolate* isolate, 879 Handle<JSArrayBuffer> old_buffer, 880 uint32_t pages, 881 uint32_t maximum_pages) { 882 if (!old_buffer->is_growable()) return {}; 883 void* old_mem_start = old_buffer->backing_store(); 884 size_t old_size = old_buffer->byte_length()->Number(); 885 CHECK_GE(wasm::kV8MaxWasmMemoryBytes, old_size); 886 CHECK_EQ(0, old_size % wasm::kWasmPageSize); 887 size_t old_pages = old_size / wasm::kWasmPageSize; 888 if (old_pages > maximum_pages || // already reached maximum 889 (pages > maximum_pages - old_pages) || // exceeds remaining 890 (pages > FLAG_wasm_max_mem_pages - old_pages)) { // exceeds limit 891 return {}; 892 } 893 size_t new_size = 894 static_cast<size_t>(old_pages + pages) * wasm::kWasmPageSize; 895 CHECK_GE(wasm::kV8MaxWasmMemoryBytes, new_size); 896 897 // Reusing the backing store from externalized buffers causes problems with 898 // Blink's array buffers. The connection between the two is lost, which can 899 // lead to Blink not knowing about the other reference to the buffer and 900 // freeing it too early. 901 if (!old_buffer->is_external() && 902 ((new_size < old_buffer->allocation_length()) || old_size == new_size)) { 903 if (old_size != new_size) { 904 DCHECK_NOT_NULL(old_buffer->backing_store()); 905 // If adjusting permissions fails, propagate error back to return 906 // failure to grow. 907 if (!i::SetPermissions(old_mem_start, new_size, 908 PageAllocator::kReadWrite)) { 909 return {}; 910 } 911 reinterpret_cast<v8::Isolate*>(isolate) 912 ->AdjustAmountOfExternalAllocatedMemory(pages * wasm::kWasmPageSize); 913 } 914 // NOTE: We must allocate a new array buffer here because the spec 915 // assumes that ArrayBuffers do not change size. 916 void* backing_store = old_buffer->backing_store(); 917 bool is_external = old_buffer->is_external(); 918 // Disconnect buffer early so GC won't free it. 919 i::wasm::DetachMemoryBuffer(isolate, old_buffer, false); 920 Handle<JSArrayBuffer> new_buffer = 921 wasm::SetupArrayBuffer(isolate, backing_store, new_size, is_external); 922 return new_buffer; 923 } else { 924 // We couldn't reuse the old backing store, so create a new one and copy the 925 // old contents in. 926 Handle<JSArrayBuffer> new_buffer; 927 if (!wasm::NewArrayBuffer(isolate, new_size).ToHandle(&new_buffer)) { 928 return {}; 929 } 930 wasm::WasmMemoryTracker* const memory_tracker = 931 isolate->wasm_engine()->memory_tracker(); 932 // If the old buffer had full guard regions, we can only safely use the new 933 // buffer if it also has full guard regions. Otherwise, we'd have to 934 // recompile all the instances using this memory to insert bounds checks. 935 if (memory_tracker->HasFullGuardRegions(old_mem_start) && 936 !memory_tracker->HasFullGuardRegions(new_buffer->backing_store())) { 937 return {}; 938 } 939 if (old_size == 0) return new_buffer; 940 memcpy(new_buffer->backing_store(), old_mem_start, old_size); 941 DCHECK(old_buffer.is_null() || !old_buffer->is_shared()); 942 constexpr bool free_memory = true; 943 i::wasm::DetachMemoryBuffer(isolate, old_buffer, free_memory); 944 return new_buffer; 945 } 946 } 947 948 // May GC, because SetSpecializationMemInfoFrom may GC 949 void SetInstanceMemory(Handle<WasmInstanceObject> instance, 950 Handle<JSArrayBuffer> buffer) { 951 instance->SetRawMemory(reinterpret_cast<byte*>(buffer->backing_store()), 952 buffer->byte_length()->Number()); 953 #if DEBUG 954 if (!FLAG_mock_arraybuffer_allocator) { 955 // To flush out bugs earlier, in DEBUG mode, check that all pages of the 956 // memory are accessible by reading and writing one byte on each page. 957 // Don't do this if the mock ArrayBuffer allocator is enabled. 958 byte* mem_start = instance->memory_start(); 959 size_t mem_size = instance->memory_size(); 960 for (size_t offset = 0; offset < mem_size; offset += wasm::kWasmPageSize) { 961 byte val = mem_start[offset]; 962 USE(val); 963 mem_start[offset] = val; 964 } 965 } 966 #endif 967 } 968 969 } // namespace 970 971 Handle<WasmMemoryObject> WasmMemoryObject::New( 972 Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer, 973 int32_t maximum) { 974 // TODO(kschimpf): Do we need to add an argument that defines the 975 // style of memory the user prefers (with/without trap handling), so 976 // that the memory will match the style of the compiled wasm module. 977 // See issue v8:7143 978 Handle<JSFunction> memory_ctor( 979 isolate->native_context()->wasm_memory_constructor(), isolate); 980 auto memory_obj = Handle<WasmMemoryObject>::cast( 981 isolate->factory()->NewJSObject(memory_ctor, TENURED)); 982 983 Handle<JSArrayBuffer> buffer; 984 if (!maybe_buffer.ToHandle(&buffer)) { 985 // If no buffer was provided, create a 0-length one. 986 buffer = wasm::SetupArrayBuffer(isolate, nullptr, 0, false); 987 } 988 memory_obj->set_array_buffer(*buffer); 989 memory_obj->set_maximum_pages(maximum); 990 991 return memory_obj; 992 } 993 994 uint32_t WasmMemoryObject::current_pages() { 995 uint32_t byte_length; 996 CHECK(array_buffer()->byte_length()->ToUint32(&byte_length)); 997 return byte_length / wasm::kWasmPageSize; 998 } 999 1000 bool WasmMemoryObject::has_full_guard_region(Isolate* isolate) { 1001 const wasm::WasmMemoryTracker::AllocationData* allocation = 1002 isolate->wasm_engine()->memory_tracker()->FindAllocationData( 1003 array_buffer()->backing_store()); 1004 CHECK_NOT_NULL(allocation); 1005 1006 Address allocation_base = 1007 reinterpret_cast<Address>(allocation->allocation_base); 1008 Address buffer_start = reinterpret_cast<Address>(allocation->buffer_start); 1009 1010 // Return whether the allocation covers every possible Wasm heap index. 1011 // 1012 // We always have the following relationship: 1013 // allocation_base <= buffer_start <= buffer_start + memory_size <= 1014 // allocation_base + allocation_length 1015 // (in other words, the buffer fits within the allocation) 1016 // 1017 // The space between buffer_start + memory_size and allocation_base + 1018 // allocation_length is the guard region. Here we make sure the guard region 1019 // is large enough for any Wasm heap offset. 1020 return buffer_start + wasm::kWasmMaxHeapOffset <= 1021 allocation_base + allocation->allocation_length; 1022 } 1023 1024 void WasmMemoryObject::AddInstance(Isolate* isolate, 1025 Handle<WasmMemoryObject> memory, 1026 Handle<WasmInstanceObject> instance) { 1027 Handle<WeakArrayList> old_instances = 1028 memory->has_instances() 1029 ? Handle<WeakArrayList>(memory->instances(), isolate) 1030 : handle(ReadOnlyRoots(isolate->heap()).empty_weak_array_list(), 1031 isolate); 1032 Handle<WeakArrayList> new_instances = WeakArrayList::AddToEnd( 1033 isolate, old_instances, MaybeObjectHandle::Weak(instance)); 1034 memory->set_instances(*new_instances); 1035 Handle<JSArrayBuffer> buffer(memory->array_buffer(), isolate); 1036 SetInstanceMemory(instance, buffer); 1037 } 1038 1039 void WasmMemoryObject::RemoveInstance(Handle<WasmMemoryObject> memory, 1040 Handle<WasmInstanceObject> instance) { 1041 if (memory->has_instances()) { 1042 memory->instances()->RemoveOne(MaybeObjectHandle::Weak(instance)); 1043 } 1044 } 1045 1046 // static 1047 int32_t WasmMemoryObject::Grow(Isolate* isolate, 1048 Handle<WasmMemoryObject> memory_object, 1049 uint32_t pages) { 1050 Handle<JSArrayBuffer> old_buffer(memory_object->array_buffer(), isolate); 1051 if (!old_buffer->is_growable()) return -1; 1052 uint32_t old_size = 0; 1053 CHECK(old_buffer->byte_length()->ToUint32(&old_size)); 1054 DCHECK_EQ(0, old_size % wasm::kWasmPageSize); 1055 Handle<JSArrayBuffer> new_buffer; 1056 1057 uint32_t maximum_pages = FLAG_wasm_max_mem_pages; 1058 if (memory_object->has_maximum_pages()) { 1059 maximum_pages = Min(FLAG_wasm_max_mem_pages, 1060 static_cast<uint32_t>(memory_object->maximum_pages())); 1061 } 1062 if (!GrowMemoryBuffer(isolate, old_buffer, pages, maximum_pages) 1063 .ToHandle(&new_buffer)) { 1064 return -1; 1065 } 1066 1067 if (memory_object->has_instances()) { 1068 Handle<WeakArrayList> instances(memory_object->instances(), isolate); 1069 for (int i = 0; i < instances->length(); i++) { 1070 MaybeObject* elem = instances->Get(i); 1071 HeapObject* heap_object; 1072 if (elem->ToWeakHeapObject(&heap_object)) { 1073 Handle<WasmInstanceObject> instance( 1074 WasmInstanceObject::cast(heap_object), isolate); 1075 SetInstanceMemory(instance, new_buffer); 1076 } else { 1077 DCHECK(elem->IsClearedWeakHeapObject()); 1078 } 1079 } 1080 } 1081 memory_object->set_array_buffer(*new_buffer); 1082 return old_size / wasm::kWasmPageSize; 1083 } 1084 1085 // static 1086 MaybeHandle<WasmGlobalObject> WasmGlobalObject::New( 1087 Isolate* isolate, MaybeHandle<JSArrayBuffer> maybe_buffer, 1088 wasm::ValueType type, int32_t offset, bool is_mutable) { 1089 Handle<JSFunction> global_ctor( 1090 isolate->native_context()->wasm_global_constructor(), isolate); 1091 auto global_obj = Handle<WasmGlobalObject>::cast( 1092 isolate->factory()->NewJSObject(global_ctor)); 1093 1094 uint32_t type_size = wasm::ValueTypes::ElementSizeInBytes(type); 1095 1096 Handle<JSArrayBuffer> buffer; 1097 if (!maybe_buffer.ToHandle(&buffer)) { 1098 // If no buffer was provided, create one long enough for the given type. 1099 buffer = 1100 isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared, TENURED); 1101 1102 const bool initialize = true; 1103 if (!JSArrayBuffer::SetupAllocatingData(buffer, isolate, type_size, 1104 initialize)) { 1105 return {}; 1106 } 1107 } 1108 1109 // Check that the offset is in bounds. 1110 uint32_t buffer_size = 0; 1111 CHECK(buffer->byte_length()->ToUint32(&buffer_size)); 1112 CHECK(offset + type_size <= buffer_size); 1113 1114 global_obj->set_array_buffer(*buffer); 1115 global_obj->set_flags(0); 1116 global_obj->set_type(type); 1117 global_obj->set_offset(offset); 1118 global_obj->set_is_mutable(is_mutable); 1119 1120 return global_obj; 1121 } 1122 1123 void IndirectFunctionTableEntry::clear() { 1124 instance_->indirect_function_table_sig_ids()[index_] = -1; 1125 instance_->indirect_function_table_targets()[index_] = 0; 1126 instance_->indirect_function_table_instances()->set( 1127 index_, ReadOnlyRoots(instance_->GetIsolate()).undefined_value()); 1128 } 1129 1130 void IndirectFunctionTableEntry::set(int sig_id, WasmInstanceObject* instance, 1131 Address call_target) { 1132 TRACE_IFT("IFT entry %p[%d] = {sig_id=%d, instance=%p, target=%" PRIuPTR 1133 "}\n", 1134 *instance_, index_, sig_id, instance, call_target); 1135 instance_->indirect_function_table_sig_ids()[index_] = sig_id; 1136 instance_->indirect_function_table_targets()[index_] = call_target; 1137 instance_->indirect_function_table_instances()->set(index_, instance); 1138 } 1139 1140 WasmInstanceObject* IndirectFunctionTableEntry::instance() { 1141 return WasmInstanceObject::cast( 1142 instance_->indirect_function_table_instances()->get(index_)); 1143 } 1144 1145 int IndirectFunctionTableEntry::sig_id() { 1146 return instance_->indirect_function_table_sig_ids()[index_]; 1147 } 1148 1149 Address IndirectFunctionTableEntry::target() { 1150 return instance_->indirect_function_table_targets()[index_]; 1151 } 1152 1153 void ImportedFunctionEntry::set_wasm_to_js( 1154 JSReceiver* callable, const wasm::WasmCode* wasm_to_js_wrapper) { 1155 TRACE_IFT("Import callable %p[%d] = {callable=%p, target=%p}\n", *instance_, 1156 index_, callable, wasm_to_js_wrapper->instructions().start()); 1157 DCHECK_EQ(wasm::WasmCode::kWasmToJsWrapper, wasm_to_js_wrapper->kind()); 1158 instance_->imported_function_instances()->set(index_, *instance_); 1159 instance_->imported_function_callables()->set(index_, callable); 1160 instance_->imported_function_targets()[index_] = 1161 wasm_to_js_wrapper->instruction_start(); 1162 } 1163 1164 void ImportedFunctionEntry::set_wasm_to_wasm(WasmInstanceObject* instance, 1165 Address call_target) { 1166 TRACE_IFT("Import WASM %p[%d] = {instance=%p, target=%" PRIuPTR "}\n", 1167 *instance_, index_, instance, call_target); 1168 instance_->imported_function_instances()->set(index_, instance); 1169 instance_->imported_function_callables()->set( 1170 index_, instance_->GetReadOnlyRoots().undefined_value()); 1171 instance_->imported_function_targets()[index_] = call_target; 1172 } 1173 1174 WasmInstanceObject* ImportedFunctionEntry::instance() { 1175 return WasmInstanceObject::cast( 1176 instance_->imported_function_instances()->get(index_)); 1177 } 1178 1179 JSReceiver* ImportedFunctionEntry::callable() { 1180 return JSReceiver::cast( 1181 instance_->imported_function_callables()->get(index_)); 1182 } 1183 1184 Address ImportedFunctionEntry::target() { 1185 return instance_->imported_function_targets()[index_]; 1186 } 1187 1188 bool ImportedFunctionEntry::is_js_receiver_entry() { 1189 return instance_->imported_function_callables()->get(index_)->IsJSReceiver(); 1190 } 1191 1192 bool WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( 1193 Handle<WasmInstanceObject> instance, uint32_t minimum_size) { 1194 uint32_t old_size = instance->indirect_function_table_size(); 1195 if (old_size >= minimum_size) return false; // Nothing to do. 1196 1197 Isolate* isolate = instance->GetIsolate(); 1198 HandleScope scope(isolate); 1199 auto native_allocations = GetNativeAllocations(*instance); 1200 native_allocations->resize_indirect_function_table(isolate, instance, 1201 minimum_size); 1202 return true; 1203 } 1204 1205 void WasmInstanceObject::SetRawMemory(byte* mem_start, size_t mem_size) { 1206 CHECK_LE(mem_size, wasm::kV8MaxWasmMemoryBytes); 1207 #if V8_HOST_ARCH_64_BIT 1208 uint64_t mem_mask64 = base::bits::RoundUpToPowerOfTwo64(mem_size) - 1; 1209 set_memory_start(mem_start); 1210 set_memory_size(mem_size); 1211 set_memory_mask(mem_mask64); 1212 #else 1213 // Must handle memory > 2GiB specially. 1214 CHECK_LE(mem_size, size_t{kMaxUInt32}); 1215 uint32_t mem_mask32 = 1216 (mem_size > 2 * size_t{GB}) 1217 ? 0xFFFFFFFFu 1218 : base::bits::RoundUpToPowerOfTwo32(static_cast<uint32_t>(mem_size)) - 1219 1; 1220 set_memory_start(mem_start); 1221 set_memory_size(mem_size); 1222 set_memory_mask(mem_mask32); 1223 #endif 1224 } 1225 1226 const WasmModule* WasmInstanceObject::module() { 1227 return module_object()->module(); 1228 } 1229 1230 Handle<WasmDebugInfo> WasmInstanceObject::GetOrCreateDebugInfo( 1231 Handle<WasmInstanceObject> instance) { 1232 if (instance->has_debug_info()) { 1233 return handle(instance->debug_info(), instance->GetIsolate()); 1234 } 1235 Handle<WasmDebugInfo> new_info = WasmDebugInfo::New(instance); 1236 DCHECK(instance->has_debug_info()); 1237 return new_info; 1238 } 1239 1240 Handle<WasmInstanceObject> WasmInstanceObject::New( 1241 Isolate* isolate, Handle<WasmModuleObject> module_object) { 1242 Handle<JSFunction> instance_cons( 1243 isolate->native_context()->wasm_instance_constructor(), isolate); 1244 Handle<JSObject> instance_object = 1245 isolate->factory()->NewJSObject(instance_cons, TENURED); 1246 1247 Handle<WasmInstanceObject> instance( 1248 reinterpret_cast<WasmInstanceObject*>(*instance_object), isolate); 1249 1250 // Initialize the imported function arrays. 1251 auto module = module_object->module(); 1252 auto num_imported_functions = module->num_imported_functions; 1253 auto num_imported_mutable_globals = module->num_imported_mutable_globals; 1254 size_t native_allocations_size = EstimateNativeAllocationsSize(module); 1255 auto native_allocations = Managed<WasmInstanceNativeAllocations>::Allocate( 1256 isolate, native_allocations_size, instance, num_imported_functions, 1257 num_imported_mutable_globals); 1258 instance->set_managed_native_allocations(*native_allocations); 1259 1260 Handle<FixedArray> imported_function_instances = 1261 isolate->factory()->NewFixedArray(num_imported_functions); 1262 instance->set_imported_function_instances(*imported_function_instances); 1263 1264 Handle<FixedArray> imported_function_callables = 1265 isolate->factory()->NewFixedArray(num_imported_functions); 1266 instance->set_imported_function_callables(*imported_function_callables); 1267 1268 Handle<Code> centry_stub = CodeFactory::CEntry(isolate); 1269 instance->set_centry_stub(*centry_stub); 1270 1271 instance->SetRawMemory(nullptr, 0); 1272 instance->set_roots_array_address( 1273 reinterpret_cast<Address>(isolate->heap()->roots_array_start())); 1274 instance->set_stack_limit_address( 1275 isolate->stack_guard()->address_of_jslimit()); 1276 instance->set_real_stack_limit_address( 1277 isolate->stack_guard()->address_of_real_jslimit()); 1278 instance->set_globals_start(nullptr); 1279 instance->set_indirect_function_table_size(0); 1280 instance->set_indirect_function_table_sig_ids(nullptr); 1281 instance->set_indirect_function_table_targets(nullptr); 1282 instance->set_native_context(*isolate->native_context()); 1283 instance->set_module_object(*module_object); 1284 instance->set_undefined_value(ReadOnlyRoots(isolate).undefined_value()); 1285 instance->set_null_value(ReadOnlyRoots(isolate).null_value()); 1286 instance->set_jump_table_start( 1287 module_object->native_module()->jump_table_start()); 1288 1289 // Insert the new instance into the modules weak list of instances. 1290 // TODO(mstarzinger): Allow to reuse holes in the {WeakArrayList} below. 1291 Handle<WeakArrayList> weak_instance_list(module_object->weak_instance_list(), 1292 isolate); 1293 weak_instance_list = WeakArrayList::AddToEnd( 1294 isolate, weak_instance_list, MaybeObjectHandle::Weak(instance)); 1295 module_object->set_weak_instance_list(*weak_instance_list); 1296 1297 return instance; 1298 } 1299 1300 namespace { 1301 void InstanceFinalizer(const v8::WeakCallbackInfo<void>& data) { 1302 DisallowHeapAllocation no_gc; 1303 JSObject** p = reinterpret_cast<JSObject**>(data.GetParameter()); 1304 WasmInstanceObject* instance = reinterpret_cast<WasmInstanceObject*>(*p); 1305 Isolate* isolate = reinterpret_cast<Isolate*>(data.GetIsolate()); 1306 // If a link to shared memory instances exists, update the list of memory 1307 // instances before the instance is destroyed. 1308 TRACE("Finalizing instance of %p {\n", 1309 instance->module_object()->native_module()); 1310 1311 // Since the order of finalizers is not guaranteed, it can be the case 1312 // that {instance->compiled_module()->module()}, which is a 1313 // {Managed<WasmModule>} has been collected earlier in this GC cycle. 1314 // Weak references to this instance won't be cleared until 1315 // the next GC cycle, so we need to manually break some links (such as 1316 // the weak references from {WasmMemoryObject::instances}. 1317 if (instance->has_memory_object()) { 1318 WasmMemoryObject::RemoveInstance(handle(instance->memory_object(), isolate), 1319 handle(instance, isolate)); 1320 } 1321 1322 // Free raw C++ memory associated with the instance. 1323 GetNativeAllocations(instance)->free(); 1324 1325 GlobalHandles::Destroy(reinterpret_cast<Object**>(p)); 1326 TRACE("}\n"); 1327 } 1328 1329 } // namespace 1330 1331 void WasmInstanceObject::InstallFinalizer(Isolate* isolate, 1332 Handle<WasmInstanceObject> instance) { 1333 Handle<Object> global_handle = isolate->global_handles()->Create(*instance); 1334 GlobalHandles::MakeWeak(global_handle.location(), global_handle.location(), 1335 InstanceFinalizer, v8::WeakCallbackType::kFinalizer); 1336 } 1337 1338 Address WasmInstanceObject::GetCallTarget(uint32_t func_index) { 1339 wasm::NativeModule* native_module = module_object()->native_module(); 1340 if (func_index < native_module->num_imported_functions()) { 1341 return imported_function_targets()[func_index]; 1342 } 1343 return native_module->GetCallTargetForFunction(func_index); 1344 } 1345 1346 bool WasmExportedFunction::IsWasmExportedFunction(Object* object) { 1347 if (!object->IsJSFunction()) return false; 1348 JSFunction* js_function = JSFunction::cast(object); 1349 if (Code::JS_TO_WASM_FUNCTION != js_function->code()->kind()) return false; 1350 DCHECK(js_function->shared()->HasWasmExportedFunctionData()); 1351 return true; 1352 } 1353 1354 WasmExportedFunction* WasmExportedFunction::cast(Object* object) { 1355 DCHECK(IsWasmExportedFunction(object)); 1356 return reinterpret_cast<WasmExportedFunction*>(object); 1357 } 1358 1359 WasmInstanceObject* WasmExportedFunction::instance() { 1360 return shared()->wasm_exported_function_data()->instance(); 1361 } 1362 1363 int WasmExportedFunction::function_index() { 1364 return shared()->wasm_exported_function_data()->function_index(); 1365 } 1366 1367 Handle<WasmExportedFunction> WasmExportedFunction::New( 1368 Isolate* isolate, Handle<WasmInstanceObject> instance, 1369 MaybeHandle<String> maybe_name, int func_index, int arity, 1370 Handle<Code> export_wrapper) { 1371 DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind()); 1372 int num_imported_functions = instance->module()->num_imported_functions; 1373 int jump_table_offset = -1; 1374 if (func_index >= num_imported_functions) { 1375 ptrdiff_t jump_table_diff = 1376 instance->module_object()->native_module()->jump_table_offset( 1377 func_index); 1378 DCHECK(jump_table_diff >= 0 && jump_table_diff <= INT_MAX); 1379 jump_table_offset = static_cast<int>(jump_table_diff); 1380 } 1381 Handle<WasmExportedFunctionData> function_data = 1382 Handle<WasmExportedFunctionData>::cast(isolate->factory()->NewStruct( 1383 WASM_EXPORTED_FUNCTION_DATA_TYPE, TENURED)); 1384 function_data->set_wrapper_code(*export_wrapper); 1385 function_data->set_instance(*instance); 1386 function_data->set_jump_table_offset(jump_table_offset); 1387 function_data->set_function_index(func_index); 1388 Handle<String> name; 1389 if (!maybe_name.ToHandle(&name)) { 1390 EmbeddedVector<char, 16> buffer; 1391 int length = SNPrintF(buffer, "%d", func_index); 1392 name = isolate->factory() 1393 ->NewStringFromOneByte( 1394 Vector<uint8_t>::cast(buffer.SubVector(0, length))) 1395 .ToHandleChecked(); 1396 } 1397 NewFunctionArgs args = NewFunctionArgs::ForWasm( 1398 name, function_data, isolate->sloppy_function_without_prototype_map()); 1399 Handle<JSFunction> js_function = isolate->factory()->NewFunction(args); 1400 // According to the spec, exported functions should not have a [[Construct]] 1401 // method. 1402 DCHECK(!js_function->IsConstructor()); 1403 js_function->shared()->set_length(arity); 1404 js_function->shared()->set_internal_formal_parameter_count(arity); 1405 return Handle<WasmExportedFunction>::cast(js_function); 1406 } 1407 1408 Address WasmExportedFunction::GetWasmCallTarget() { 1409 return instance()->GetCallTarget(function_index()); 1410 } 1411 1412 #undef TRACE 1413 #undef TRACE_IFT 1414 } // namespace internal 1415 } // namespace v8 1416