1 // Copyright 2017 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-serialization.h" 6 7 #include "src/assembler-inl.h" 8 #include "src/external-reference-table.h" 9 #include "src/objects-inl.h" 10 #include "src/objects.h" 11 #include "src/snapshot/code-serializer.h" 12 #include "src/snapshot/serializer-common.h" 13 #include "src/utils.h" 14 #include "src/version.h" 15 #include "src/wasm/function-compiler.h" 16 #include "src/wasm/module-compiler.h" 17 #include "src/wasm/module-decoder.h" 18 #include "src/wasm/wasm-code-manager.h" 19 #include "src/wasm/wasm-module.h" 20 #include "src/wasm/wasm-objects-inl.h" 21 #include "src/wasm/wasm-objects.h" 22 #include "src/wasm/wasm-result.h" 23 24 namespace v8 { 25 namespace internal { 26 namespace wasm { 27 28 namespace { 29 30 // TODO(bbudge) Try to unify the various implementations of readers and writers 31 // in WASM, e.g. StreamProcessor and ZoneBuffer, with these. 32 class Writer { 33 public: 34 explicit Writer(Vector<byte> buffer) 35 : start_(buffer.start()), end_(buffer.end()), pos_(buffer.start()) {} 36 37 size_t bytes_written() const { return pos_ - start_; } 38 byte* current_location() const { return pos_; } 39 size_t current_size() const { return end_ - pos_; } 40 Vector<byte> current_buffer() const { 41 return {current_location(), current_size()}; 42 } 43 44 template <typename T> 45 void Write(const T& value) { 46 DCHECK_GE(current_size(), sizeof(T)); 47 WriteUnalignedValue(reinterpret_cast<Address>(current_location()), value); 48 pos_ += sizeof(T); 49 if (FLAG_wasm_trace_serialization) { 50 StdoutStream{} << "wrote: " << (size_t)value << " sized: " << sizeof(T) 51 << std::endl; 52 } 53 } 54 55 void WriteVector(const Vector<const byte> v) { 56 DCHECK_GE(current_size(), v.size()); 57 if (v.size() > 0) { 58 memcpy(current_location(), v.start(), v.size()); 59 pos_ += v.size(); 60 } 61 if (FLAG_wasm_trace_serialization) { 62 StdoutStream{} << "wrote vector of " << v.size() << " elements" 63 << std::endl; 64 } 65 } 66 67 void Skip(size_t size) { pos_ += size; } 68 69 private: 70 byte* const start_; 71 byte* const end_; 72 byte* pos_; 73 }; 74 75 class Reader { 76 public: 77 explicit Reader(Vector<const byte> buffer) 78 : start_(buffer.start()), end_(buffer.end()), pos_(buffer.start()) {} 79 80 size_t bytes_read() const { return pos_ - start_; } 81 const byte* current_location() const { return pos_; } 82 size_t current_size() const { return end_ - pos_; } 83 Vector<const byte> current_buffer() const { 84 return {current_location(), current_size()}; 85 } 86 87 template <typename T> 88 T Read() { 89 DCHECK_GE(current_size(), sizeof(T)); 90 T value = 91 ReadUnalignedValue<T>(reinterpret_cast<Address>(current_location())); 92 pos_ += sizeof(T); 93 if (FLAG_wasm_trace_serialization) { 94 StdoutStream{} << "read: " << (size_t)value << " sized: " << sizeof(T) 95 << std::endl; 96 } 97 return value; 98 } 99 100 void ReadVector(Vector<byte> v) { 101 if (v.size() > 0) { 102 DCHECK_GE(current_size(), v.size()); 103 memcpy(v.start(), current_location(), v.size()); 104 pos_ += v.size(); 105 } 106 if (FLAG_wasm_trace_serialization) { 107 StdoutStream{} << "read vector of " << v.size() << " elements" 108 << std::endl; 109 } 110 } 111 112 void Skip(size_t size) { pos_ += size; } 113 114 private: 115 const byte* const start_; 116 const byte* const end_; 117 const byte* pos_; 118 }; 119 120 constexpr size_t kVersionSize = 4 * sizeof(uint32_t); 121 122 void WriteVersion(Isolate* isolate, Writer* writer) { 123 writer->Write(SerializedData::ComputeMagicNumber( 124 isolate->heap()->external_reference_table())); 125 writer->Write(Version::Hash()); 126 writer->Write(static_cast<uint32_t>(CpuFeatures::SupportedFeatures())); 127 writer->Write(FlagList::Hash()); 128 } 129 130 bool IsSupportedVersion(Isolate* isolate, const Vector<const byte> version) { 131 if (version.size() < kVersionSize) return false; 132 byte current_version[kVersionSize]; 133 Writer writer({current_version, kVersionSize}); 134 WriteVersion(isolate, &writer); 135 return memcmp(version.start(), current_version, kVersionSize) == 0; 136 } 137 138 // On Intel, call sites are encoded as a displacement. For linking and for 139 // serialization/deserialization, we want to store/retrieve a tag (the function 140 // index). On Intel, that means accessing the raw displacement. 141 // On ARM64, call sites are encoded as either a literal load or a direct branch. 142 // Other platforms simply require accessing the target address. 143 void SetWasmCalleeTag(RelocInfo* rinfo, uint32_t tag) { 144 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 145 *(reinterpret_cast<uint32_t*>(rinfo->target_address_address())) = tag; 146 #elif V8_TARGET_ARCH_ARM64 147 Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc()); 148 if (instr->IsLdrLiteralX()) { 149 Memory<Address>(rinfo->constant_pool_entry_address()) = 150 static_cast<Address>(tag); 151 } else { 152 DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch()); 153 instr->SetBranchImmTarget( 154 reinterpret_cast<Instruction*>(rinfo->pc() + tag * kInstrSize)); 155 } 156 #else 157 Address addr = static_cast<Address>(tag); 158 if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) { 159 rinfo->set_target_external_reference(addr, SKIP_ICACHE_FLUSH); 160 } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) { 161 rinfo->set_wasm_stub_call_address(addr, SKIP_ICACHE_FLUSH); 162 } else { 163 rinfo->set_target_address(addr, SKIP_WRITE_BARRIER, SKIP_ICACHE_FLUSH); 164 } 165 #endif 166 } 167 168 uint32_t GetWasmCalleeTag(RelocInfo* rinfo) { 169 #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32 170 return *(reinterpret_cast<uint32_t*>(rinfo->target_address_address())); 171 #elif V8_TARGET_ARCH_ARM64 172 Instruction* instr = reinterpret_cast<Instruction*>(rinfo->pc()); 173 if (instr->IsLdrLiteralX()) { 174 return static_cast<uint32_t>( 175 Memory<Address>(rinfo->constant_pool_entry_address())); 176 } else { 177 DCHECK(instr->IsBranchAndLink() || instr->IsUnconditionalBranch()); 178 return static_cast<uint32_t>(instr->ImmPCOffset() / kInstrSize); 179 } 180 #else 181 Address addr; 182 if (rinfo->rmode() == RelocInfo::EXTERNAL_REFERENCE) { 183 addr = rinfo->target_external_reference(); 184 } else if (rinfo->rmode() == RelocInfo::WASM_STUB_CALL) { 185 addr = rinfo->wasm_stub_call_address(); 186 } else { 187 addr = rinfo->target_address(); 188 } 189 return static_cast<uint32_t>(addr); 190 #endif 191 } 192 193 constexpr size_t kHeaderSize = 194 sizeof(uint32_t) + // total wasm function count 195 sizeof(uint32_t); // imported functions (index of first wasm function) 196 197 constexpr size_t kCodeHeaderSize = 198 sizeof(size_t) + // size of code section 199 sizeof(size_t) + // offset of constant pool 200 sizeof(size_t) + // offset of safepoint table 201 sizeof(size_t) + // offset of handler table 202 sizeof(uint32_t) + // stack slots 203 sizeof(size_t) + // code size 204 sizeof(size_t) + // reloc size 205 sizeof(size_t) + // source positions size 206 sizeof(size_t) + // protected instructions size 207 sizeof(WasmCode::Tier); // tier 208 209 } // namespace 210 211 class V8_EXPORT_PRIVATE NativeModuleSerializer { 212 public: 213 NativeModuleSerializer() = delete; 214 NativeModuleSerializer(Isolate*, const NativeModule*, 215 Vector<WasmCode* const>); 216 217 size_t Measure() const; 218 bool Write(Writer* writer); 219 220 private: 221 size_t MeasureCode(const WasmCode*) const; 222 void WriteHeader(Writer* writer); 223 void WriteCode(const WasmCode*, Writer* writer); 224 225 Isolate* const isolate_; 226 const NativeModule* const native_module_; 227 Vector<WasmCode* const> code_table_; 228 bool write_called_; 229 230 // Reverse lookup tables for embedded addresses. 231 std::map<Address, uint32_t> wasm_stub_targets_lookup_; 232 std::map<Address, uint32_t> reference_table_lookup_; 233 234 DISALLOW_COPY_AND_ASSIGN(NativeModuleSerializer); 235 }; 236 237 NativeModuleSerializer::NativeModuleSerializer( 238 Isolate* isolate, const NativeModule* module, 239 Vector<WasmCode* const> code_table) 240 : isolate_(isolate), 241 native_module_(module), 242 code_table_(code_table), 243 write_called_(false) { 244 DCHECK_NOT_NULL(isolate_); 245 DCHECK_NOT_NULL(native_module_); 246 // TODO(mtrofin): persist the export wrappers. Ideally, we'd only persist 247 // the unique ones, i.e. the cache. 248 for (uint32_t i = 0; i < WasmCode::kRuntimeStubCount; ++i) { 249 Address addr = 250 native_module_->runtime_stub(static_cast<WasmCode::RuntimeStubId>(i)) 251 ->instruction_start(); 252 wasm_stub_targets_lookup_.insert(std::make_pair(addr, i)); 253 } 254 ExternalReferenceTable* table = isolate_->heap()->external_reference_table(); 255 for (uint32_t i = 0; i < table->size(); ++i) { 256 Address addr = table->address(i); 257 reference_table_lookup_.insert(std::make_pair(addr, i)); 258 } 259 } 260 261 size_t NativeModuleSerializer::MeasureCode(const WasmCode* code) const { 262 if (code == nullptr) return sizeof(size_t); 263 DCHECK_EQ(WasmCode::kFunction, code->kind()); 264 return kCodeHeaderSize + code->instructions().size() + 265 code->reloc_info().size() + code->source_positions().size() + 266 code->protected_instructions().size() * 267 sizeof(trap_handler::ProtectedInstructionData); 268 } 269 270 size_t NativeModuleSerializer::Measure() const { 271 size_t size = kHeaderSize; 272 for (WasmCode* code : code_table_) { 273 size += MeasureCode(code); 274 } 275 return size; 276 } 277 278 void NativeModuleSerializer::WriteHeader(Writer* writer) { 279 writer->Write(native_module_->num_functions()); 280 writer->Write(native_module_->num_imported_functions()); 281 } 282 283 void NativeModuleSerializer::WriteCode(const WasmCode* code, Writer* writer) { 284 if (code == nullptr) { 285 writer->Write(size_t{0}); 286 return; 287 } 288 DCHECK_EQ(WasmCode::kFunction, code->kind()); 289 // Write the size of the entire code section, followed by the code header. 290 writer->Write(MeasureCode(code)); 291 writer->Write(code->constant_pool_offset()); 292 writer->Write(code->safepoint_table_offset()); 293 writer->Write(code->handler_table_offset()); 294 writer->Write(code->stack_slots()); 295 writer->Write(code->instructions().size()); 296 writer->Write(code->reloc_info().size()); 297 writer->Write(code->source_positions().size()); 298 writer->Write(code->protected_instructions().size()); 299 writer->Write(code->tier()); 300 301 // Get a pointer to the destination buffer, to hold relocated code. 302 byte* serialized_code_start = writer->current_buffer().start(); 303 byte* code_start = serialized_code_start; 304 size_t code_size = code->instructions().size(); 305 writer->Skip(code_size); 306 // Write the reloc info, source positions, and protected code. 307 writer->WriteVector(code->reloc_info()); 308 writer->WriteVector(code->source_positions()); 309 writer->WriteVector(Vector<byte>::cast(code->protected_instructions())); 310 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_ARM 311 // On platforms that don't support misaligned word stores, copy to an aligned 312 // buffer if necessary so we can relocate the serialized code. 313 std::unique_ptr<byte[]> aligned_buffer; 314 if (!IsAligned(reinterpret_cast<Address>(serialized_code_start), 315 kInt32Size)) { 316 aligned_buffer.reset(new byte[code_size]); 317 code_start = aligned_buffer.get(); 318 } 319 #endif 320 memcpy(code_start, code->instructions().start(), code_size); 321 // Relocate the code. 322 int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) | 323 RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) | 324 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | 325 RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) | 326 RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED); 327 RelocIterator orig_iter(code->instructions(), code->reloc_info(), 328 code->constant_pool(), mask); 329 for (RelocIterator iter( 330 {code_start, code->instructions().size()}, code->reloc_info(), 331 reinterpret_cast<Address>(code_start) + code->constant_pool_offset(), 332 mask); 333 !iter.done(); iter.next(), orig_iter.next()) { 334 RelocInfo::Mode mode = orig_iter.rinfo()->rmode(); 335 switch (mode) { 336 case RelocInfo::WASM_CALL: { 337 Address orig_target = orig_iter.rinfo()->wasm_call_address(); 338 uint32_t tag = 339 native_module_->GetFunctionIndexFromJumpTableSlot(orig_target); 340 SetWasmCalleeTag(iter.rinfo(), tag); 341 } break; 342 case RelocInfo::WASM_STUB_CALL: { 343 Address orig_target = orig_iter.rinfo()->wasm_stub_call_address(); 344 auto stub_iter = wasm_stub_targets_lookup_.find(orig_target); 345 DCHECK(stub_iter != wasm_stub_targets_lookup_.end()); 346 uint32_t tag = stub_iter->second; 347 SetWasmCalleeTag(iter.rinfo(), tag); 348 } break; 349 case RelocInfo::EXTERNAL_REFERENCE: { 350 Address orig_target = orig_iter.rinfo()->target_external_reference(); 351 auto ref_iter = reference_table_lookup_.find(orig_target); 352 DCHECK(ref_iter != reference_table_lookup_.end()); 353 uint32_t tag = ref_iter->second; 354 SetWasmCalleeTag(iter.rinfo(), tag); 355 } break; 356 case RelocInfo::INTERNAL_REFERENCE: 357 case RelocInfo::INTERNAL_REFERENCE_ENCODED: { 358 Address orig_target = orig_iter.rinfo()->target_internal_reference(); 359 Address offset = orig_target - code->instruction_start(); 360 Assembler::deserialization_set_target_internal_reference_at( 361 iter.rinfo()->pc(), offset, mode); 362 } break; 363 default: 364 UNREACHABLE(); 365 } 366 } 367 // If we copied to an aligned buffer, copy code into serialized buffer. 368 if (code_start != serialized_code_start) { 369 memcpy(serialized_code_start, code_start, code_size); 370 } 371 } 372 373 bool NativeModuleSerializer::Write(Writer* writer) { 374 DCHECK(!write_called_); 375 write_called_ = true; 376 377 WriteHeader(writer); 378 379 for (WasmCode* code : code_table_) { 380 WriteCode(code, writer); 381 } 382 return true; 383 } 384 385 WasmSerializer::WasmSerializer(Isolate* isolate, NativeModule* native_module) 386 : isolate_(isolate), 387 native_module_(native_module), 388 code_table_(native_module->SnapshotCodeTable()) {} 389 390 size_t WasmSerializer::GetSerializedNativeModuleSize() const { 391 Vector<WasmCode* const> code_table(code_table_.data(), code_table_.size()); 392 NativeModuleSerializer serializer(isolate_, native_module_, code_table); 393 return kVersionSize + serializer.Measure(); 394 } 395 396 bool WasmSerializer::SerializeNativeModule(Vector<byte> buffer) const { 397 Vector<WasmCode* const> code_table(code_table_.data(), code_table_.size()); 398 NativeModuleSerializer serializer(isolate_, native_module_, code_table); 399 size_t measured_size = kVersionSize + serializer.Measure(); 400 if (buffer.size() < measured_size) return false; 401 402 Writer writer(buffer); 403 WriteVersion(isolate_, &writer); 404 405 if (!serializer.Write(&writer)) return false; 406 DCHECK_EQ(measured_size, writer.bytes_written()); 407 return true; 408 } 409 410 class V8_EXPORT_PRIVATE NativeModuleDeserializer { 411 public: 412 NativeModuleDeserializer() = delete; 413 NativeModuleDeserializer(Isolate*, NativeModule*); 414 415 bool Read(Reader* reader); 416 417 private: 418 bool ReadHeader(Reader* reader); 419 bool ReadCode(uint32_t fn_index, Reader* reader); 420 421 Isolate* const isolate_; 422 NativeModule* const native_module_; 423 bool read_called_; 424 425 DISALLOW_COPY_AND_ASSIGN(NativeModuleDeserializer); 426 }; 427 428 NativeModuleDeserializer::NativeModuleDeserializer(Isolate* isolate, 429 NativeModule* native_module) 430 : isolate_(isolate), native_module_(native_module), read_called_(false) {} 431 432 bool NativeModuleDeserializer::Read(Reader* reader) { 433 DCHECK(!read_called_); 434 read_called_ = true; 435 436 if (!ReadHeader(reader)) return false; 437 uint32_t total_fns = native_module_->num_functions(); 438 uint32_t first_wasm_fn = native_module_->num_imported_functions(); 439 for (uint32_t i = first_wasm_fn; i < total_fns; ++i) { 440 if (!ReadCode(i, reader)) return false; 441 } 442 return reader->current_size() == 0; 443 } 444 445 bool NativeModuleDeserializer::ReadHeader(Reader* reader) { 446 size_t functions = reader->Read<uint32_t>(); 447 size_t imports = reader->Read<uint32_t>(); 448 return functions == native_module_->num_functions() && 449 imports == native_module_->num_imported_functions(); 450 } 451 452 bool NativeModuleDeserializer::ReadCode(uint32_t fn_index, Reader* reader) { 453 size_t code_section_size = reader->Read<size_t>(); 454 if (code_section_size == 0) return true; 455 size_t constant_pool_offset = reader->Read<size_t>(); 456 size_t safepoint_table_offset = reader->Read<size_t>(); 457 size_t handler_table_offset = reader->Read<size_t>(); 458 uint32_t stack_slot_count = reader->Read<uint32_t>(); 459 size_t code_size = reader->Read<size_t>(); 460 size_t reloc_size = reader->Read<size_t>(); 461 size_t source_position_size = reader->Read<size_t>(); 462 size_t protected_instructions_size = reader->Read<size_t>(); 463 WasmCode::Tier tier = reader->Read<WasmCode::Tier>(); 464 465 Vector<const byte> code_buffer = {reader->current_location(), code_size}; 466 reader->Skip(code_size); 467 468 OwnedVector<byte> reloc_info = OwnedVector<byte>::New(reloc_size); 469 reader->ReadVector(reloc_info.as_vector()); 470 OwnedVector<byte> source_pos = OwnedVector<byte>::New(source_position_size); 471 reader->ReadVector(source_pos.as_vector()); 472 auto protected_instructions = 473 OwnedVector<trap_handler::ProtectedInstructionData>::New( 474 protected_instructions_size); 475 reader->ReadVector(Vector<byte>::cast(protected_instructions.as_vector())); 476 477 WasmCode* code = native_module_->AddDeserializedCode( 478 fn_index, code_buffer, stack_slot_count, safepoint_table_offset, 479 handler_table_offset, constant_pool_offset, 480 std::move(protected_instructions), std::move(reloc_info), 481 std::move(source_pos), tier); 482 483 // Relocate the code. 484 int mask = RelocInfo::ModeMask(RelocInfo::WASM_CALL) | 485 RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL) | 486 RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) | 487 RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) | 488 RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED); 489 for (RelocIterator iter(code->instructions(), code->reloc_info(), 490 code->constant_pool(), mask); 491 !iter.done(); iter.next()) { 492 RelocInfo::Mode mode = iter.rinfo()->rmode(); 493 switch (mode) { 494 case RelocInfo::WASM_CALL: { 495 uint32_t tag = GetWasmCalleeTag(iter.rinfo()); 496 Address target = native_module_->GetCallTargetForFunction(tag); 497 iter.rinfo()->set_wasm_call_address(target, SKIP_ICACHE_FLUSH); 498 break; 499 } 500 case RelocInfo::WASM_STUB_CALL: { 501 uint32_t tag = GetWasmCalleeTag(iter.rinfo()); 502 DCHECK_LT(tag, WasmCode::kRuntimeStubCount); 503 Address target = 504 native_module_ 505 ->runtime_stub(static_cast<WasmCode::RuntimeStubId>(tag)) 506 ->instruction_start(); 507 iter.rinfo()->set_wasm_stub_call_address(target, SKIP_ICACHE_FLUSH); 508 break; 509 } 510 case RelocInfo::EXTERNAL_REFERENCE: { 511 uint32_t tag = GetWasmCalleeTag(iter.rinfo()); 512 Address address = 513 isolate_->heap()->external_reference_table()->address(tag); 514 iter.rinfo()->set_target_external_reference(address, SKIP_ICACHE_FLUSH); 515 break; 516 } 517 case RelocInfo::INTERNAL_REFERENCE: 518 case RelocInfo::INTERNAL_REFERENCE_ENCODED: { 519 Address offset = iter.rinfo()->target_internal_reference(); 520 Address target = code->instruction_start() + offset; 521 Assembler::deserialization_set_target_internal_reference_at( 522 iter.rinfo()->pc(), target, mode); 523 break; 524 } 525 default: 526 UNREACHABLE(); 527 } 528 } 529 530 if (FLAG_print_code || FLAG_print_wasm_code) code->Print(); 531 code->Validate(); 532 533 // Finally, flush the icache for that code. 534 Assembler::FlushICache(code->instructions().start(), 535 code->instructions().size()); 536 537 return true; 538 } 539 540 MaybeHandle<WasmModuleObject> DeserializeNativeModule( 541 Isolate* isolate, Vector<const byte> data, Vector<const byte> wire_bytes) { 542 if (!IsWasmCodegenAllowed(isolate, isolate->native_context())) { 543 return {}; 544 } 545 if (!IsSupportedVersion(isolate, data)) { 546 return {}; 547 } 548 // TODO(titzer): module features should be part of the serialization format. 549 WasmFeatures enabled_features = WasmFeaturesFromIsolate(isolate); 550 ModuleResult decode_result = DecodeWasmModule( 551 enabled_features, wire_bytes.start(), wire_bytes.end(), false, 552 i::wasm::kWasmOrigin, isolate->counters(), isolate->allocator()); 553 if (!decode_result.ok()) return {}; 554 CHECK_NOT_NULL(decode_result.val); 555 WasmModule* module = decode_result.val.get(); 556 Handle<Script> script = CreateWasmScript(isolate, wire_bytes); 557 558 // TODO(eholk): We need to properly preserve the flag whether the trap 559 // handler was used or not when serializing. 560 UseTrapHandler use_trap_handler = 561 trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler; 562 ModuleEnv env(module, use_trap_handler, 563 RuntimeExceptionSupport::kRuntimeExceptionSupport); 564 565 OwnedVector<uint8_t> wire_bytes_copy = OwnedVector<uint8_t>::Of(wire_bytes); 566 567 Handle<WasmModuleObject> module_object = WasmModuleObject::New( 568 isolate, enabled_features, std::move(decode_result.val), env, 569 std::move(wire_bytes_copy), script, Handle<ByteArray>::null()); 570 NativeModule* native_module = module_object->native_module(); 571 572 if (FLAG_wasm_lazy_compilation) { 573 native_module->SetLazyBuiltin(BUILTIN_CODE(isolate, WasmCompileLazy)); 574 } 575 NativeModuleDeserializer deserializer(isolate, native_module); 576 577 Reader reader(data + kVersionSize); 578 if (!deserializer.Read(&reader)) return {}; 579 580 // TODO(6792): Wrappers below might be cloned using {Factory::CopyCode}. This 581 // requires unlocking the code space here. This should eventually be moved 582 // into the allocator. 583 CodeSpaceMemoryModificationScope modification_scope(isolate->heap()); 584 CompileJsToWasmWrappers(isolate, module_object); 585 586 // Log the code within the generated module for profiling. 587 native_module->LogWasmCodes(isolate); 588 589 return module_object; 590 } 591 592 } // namespace wasm 593 } // namespace internal 594 } // namespace v8 595