1// Copyright 2016 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/snapshot/code-serializer.h" 6 7#include <memory> 8 9#include "src/base/logging.h" 10#include "src/base/platform/elapsed-timer.h" 11#include "src/base/platform/platform.h" 12#include "src/codegen/macro-assembler.h" 13#include "src/common/globals.h" 14#include "src/debug/debug.h" 15#include "src/handles/maybe-handles.h" 16#include "src/handles/persistent-handles.h" 17#include "src/heap/heap-inl.h" 18#include "src/heap/local-factory-inl.h" 19#include "src/heap/parked-scope.h" 20#include "src/logging/counters-scopes.h" 21#include "src/logging/log.h" 22#include "src/logging/runtime-call-stats-scope.h" 23#include "src/objects/objects-inl.h" 24#include "src/objects/shared-function-info.h" 25#include "src/objects/slots.h" 26#include "src/objects/visitors.h" 27#include "src/snapshot/object-deserializer.h" 28#include "src/snapshot/snapshot-utils.h" 29#include "src/snapshot/snapshot.h" 30#include "src/utils/version.h" 31 32namespace v8 { 33namespace internal { 34 35AlignedCachedData::AlignedCachedData(const byte* data, int length) 36 : owns_data_(false), rejected_(false), data_(data), length_(length) { 37 if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) { 38 byte* copy = NewArray<byte>(length); 39 DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment)); 40 CopyBytes(copy, data, length); 41 data_ = copy; 42 AcquireDataOwnership(); 43 } 44} 45 46CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash) 47 : Serializer(isolate, Snapshot::kDefaultSerializerFlags), 48 source_hash_(source_hash) {} 49 50// static 51ScriptCompiler::CachedData* CodeSerializer::Serialize( 52 Handle<SharedFunctionInfo> info) { 53 Isolate* isolate = info->GetIsolate(); 54 TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.Execute"); 55 NestedTimedHistogramScope histogram_timer( 56 isolate->counters()->compile_serialize()); 57 RCS_SCOPE(isolate, RuntimeCallCounterId::kCompileSerialize); 58 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileSerialize"); 59 60 base::ElapsedTimer timer; 61 if (FLAG_profile_deserialization) timer.Start(); 62 Handle<Script> script(Script::cast(info->script()), isolate); 63 if (FLAG_trace_serializer) { 64 PrintF("[Serializing from"); 65 script->name().ShortPrint(); 66 PrintF("]\n"); 67 } 68#if V8_ENABLE_WEBASSEMBLY 69 // TODO(7110): Enable serialization of Asm modules once the AsmWasmData is 70 // context independent. 71 if (script->ContainsAsmModule()) return nullptr; 72#endif // V8_ENABLE_WEBASSEMBLY 73 74 // Serialize code object. 75 Handle<String> source(String::cast(script->source()), isolate); 76 HandleScope scope(isolate); 77 CodeSerializer cs(isolate, SerializedCodeData::SourceHash( 78 source, script->origin_options())); 79 DisallowGarbageCollection no_gc; 80 cs.reference_map()->AddAttachedReference(*source); 81 AlignedCachedData* cached_data = cs.SerializeSharedFunctionInfo(info); 82 83 if (FLAG_profile_deserialization) { 84 double ms = timer.Elapsed().InMillisecondsF(); 85 int length = cached_data->length(); 86 PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms); 87 } 88 89 ScriptCompiler::CachedData* result = 90 new ScriptCompiler::CachedData(cached_data->data(), cached_data->length(), 91 ScriptCompiler::CachedData::BufferOwned); 92 cached_data->ReleaseDataOwnership(); 93 delete cached_data; 94 95 return result; 96} 97 98AlignedCachedData* CodeSerializer::SerializeSharedFunctionInfo( 99 Handle<SharedFunctionInfo> info) { 100 DisallowGarbageCollection no_gc; 101 102 VisitRootPointer(Root::kHandleScope, nullptr, 103 FullObjectSlot(info.location())); 104 SerializeDeferredObjects(); 105 Pad(); 106 107 SerializedCodeData data(sink_.data(), this); 108 109 return data.GetScriptData(); 110} 111 112bool CodeSerializer::SerializeReadOnlyObject( 113 HeapObject obj, const DisallowGarbageCollection& no_gc) { 114 if (!ReadOnlyHeap::Contains(obj)) return false; 115 116 // For objects on the read-only heap, never serialize the object, but instead 117 // create a back reference that encodes the page number as the chunk_index and 118 // the offset within the page as the chunk_offset. 119 Address address = obj.address(); 120 BasicMemoryChunk* chunk = BasicMemoryChunk::FromAddress(address); 121 uint32_t chunk_index = 0; 122 ReadOnlySpace* const read_only_space = isolate()->heap()->read_only_space(); 123 for (ReadOnlyPage* page : read_only_space->pages()) { 124 if (chunk == page) break; 125 ++chunk_index; 126 } 127 uint32_t chunk_offset = static_cast<uint32_t>(chunk->Offset(address)); 128 sink_.Put(kReadOnlyHeapRef, "ReadOnlyHeapRef"); 129 sink_.PutInt(chunk_index, "ReadOnlyHeapRefChunkIndex"); 130 sink_.PutInt(chunk_offset, "ReadOnlyHeapRefChunkOffset"); 131 return true; 132} 133 134void CodeSerializer::SerializeObjectImpl(Handle<HeapObject> obj) { 135 ReadOnlyRoots roots(isolate()); 136 InstanceType instance_type; 137 { 138 DisallowGarbageCollection no_gc; 139 HeapObject raw = *obj; 140 if (SerializeHotObject(raw)) return; 141 if (SerializeRoot(raw)) return; 142 if (SerializeBackReference(raw)) return; 143 if (SerializeReadOnlyObject(raw, no_gc)) return; 144 145 instance_type = raw.map().instance_type(); 146 CHECK(!InstanceTypeChecker::IsCode(instance_type)); 147 148 if (ElideObject(raw)) { 149 AllowGarbageCollection allow_gc; 150 return SerializeObject(roots.undefined_value_handle()); 151 } 152 } 153 154 if (InstanceTypeChecker::IsScript(instance_type)) { 155 Handle<FixedArray> host_options; 156 Handle<Object> context_data; 157 { 158 DisallowGarbageCollection no_gc; 159 Script script_obj = Script::cast(*obj); 160 DCHECK_NE(script_obj.compilation_type(), Script::COMPILATION_TYPE_EVAL); 161 // We want to differentiate between undefined and uninitialized_symbol for 162 // context_data for now. It is hack to allow debugging for scripts that 163 // are included as a part of custom snapshot. (see 164 // debug::Script::IsEmbedded()) 165 Object raw_context_data = script_obj.context_data(); 166 if (raw_context_data != roots.undefined_value() && 167 raw_context_data != roots.uninitialized_symbol()) { 168 script_obj.set_context_data(roots.undefined_value()); 169 } 170 context_data = handle(raw_context_data, isolate()); 171 // We don't want to serialize host options to avoid serializing 172 // unnecessary object graph. 173 host_options = handle(script_obj.host_defined_options(), isolate()); 174 script_obj.set_host_defined_options(roots.empty_fixed_array()); 175 } 176 SerializeGeneric(obj); 177 { 178 DisallowGarbageCollection no_gc; 179 Script script_obj = Script::cast(*obj); 180 script_obj.set_host_defined_options(*host_options); 181 script_obj.set_context_data(*context_data); 182 } 183 return; 184 } else if (InstanceTypeChecker::IsSharedFunctionInfo(instance_type)) { 185 Handle<DebugInfo> debug_info; 186 bool restore_bytecode = false; 187 { 188 DisallowGarbageCollection no_gc; 189 SharedFunctionInfo sfi = SharedFunctionInfo::cast(*obj); 190 DCHECK(!sfi.IsApiFunction()); 191#if V8_ENABLE_WEBASSEMBLY 192 // TODO(7110): Enable serializing of Asm modules once the AsmWasmData 193 // is context independent. 194 DCHECK(!sfi.HasAsmWasmData()); 195#endif // V8_ENABLE_WEBASSEMBLY 196 197 if (sfi.HasDebugInfo()) { 198 // Clear debug info. 199 DebugInfo raw_debug_info = sfi.GetDebugInfo(); 200 if (raw_debug_info.HasInstrumentedBytecodeArray()) { 201 restore_bytecode = true; 202 sfi.SetActiveBytecodeArray(raw_debug_info.OriginalBytecodeArray()); 203 } 204 sfi.set_script_or_debug_info(raw_debug_info.script(), kReleaseStore); 205 debug_info = handle(raw_debug_info, isolate()); 206 } 207 DCHECK(!sfi.HasDebugInfo()); 208 } 209 SerializeGeneric(obj); 210 // Restore debug info 211 if (!debug_info.is_null()) { 212 DisallowGarbageCollection no_gc; 213 SharedFunctionInfo sfi = SharedFunctionInfo::cast(*obj); 214 sfi.set_script_or_debug_info(*debug_info, kReleaseStore); 215 if (restore_bytecode) { 216 sfi.SetActiveBytecodeArray(debug_info->DebugBytecodeArray()); 217 } 218 } 219 return; 220 } else if (InstanceTypeChecker::IsUncompiledDataWithoutPreparseDataWithJob( 221 instance_type)) { 222 Handle<UncompiledDataWithoutPreparseDataWithJob> data = 223 Handle<UncompiledDataWithoutPreparseDataWithJob>::cast(obj); 224 Address job = data->job(); 225 data->set_job(kNullAddress); 226 SerializeGeneric(data); 227 data->set_job(job); 228 return; 229 } else if (InstanceTypeChecker::IsUncompiledDataWithPreparseDataAndJob( 230 instance_type)) { 231 Handle<UncompiledDataWithPreparseDataAndJob> data = 232 Handle<UncompiledDataWithPreparseDataAndJob>::cast(obj); 233 Address job = data->job(); 234 data->set_job(kNullAddress); 235 SerializeGeneric(data); 236 data->set_job(job); 237 return; 238 } 239 240 // NOTE(mmarchini): If we try to serialize an InterpreterData our process 241 // will crash since it stores a code object. Instead, we serialize the 242 // bytecode array stored within the InterpreterData, which is the important 243 // information. On deserialization we'll create our code objects again, if 244 // --interpreted-frames-native-stack is on. See v8:9122 for more context 245#ifndef V8_TARGET_ARCH_ARM 246 if (V8_UNLIKELY(FLAG_interpreted_frames_native_stack) && 247 obj->IsInterpreterData()) { 248 obj = handle(InterpreterData::cast(*obj).bytecode_array(), isolate()); 249 } 250#endif // V8_TARGET_ARCH_ARM 251 252 // Past this point we should not see any (context-specific) maps anymore. 253 CHECK(!InstanceTypeChecker::IsMap(instance_type)); 254 // There should be no references to the global object embedded. 255 CHECK(!InstanceTypeChecker::IsJSGlobalProxy(instance_type) && 256 !InstanceTypeChecker::IsJSGlobalObject(instance_type)); 257 // Embedded FixedArrays that need rehashing must support rehashing. 258 CHECK_IMPLIES(obj->NeedsRehashing(cage_base()), 259 obj->CanBeRehashed(cage_base())); 260 // We expect no instantiated function objects or contexts. 261 CHECK(!InstanceTypeChecker::IsJSFunction(instance_type) && 262 !InstanceTypeChecker::IsContext(instance_type)); 263 264 SerializeGeneric(obj); 265} 266 267void CodeSerializer::SerializeGeneric(Handle<HeapObject> heap_object) { 268 // Object has not yet been serialized. Serialize it here. 269 ObjectSerializer serializer(this, heap_object, &sink_); 270 serializer.Serialize(); 271} 272 273namespace { 274 275#ifndef V8_TARGET_ARCH_ARM 276// NOTE(mmarchini): when FLAG_interpreted_frames_native_stack is on, we want to 277// create duplicates of InterpreterEntryTrampoline for the deserialized 278// functions, otherwise we'll call the builtin IET for those functions (which 279// is not what a user of this flag wants). 280void CreateInterpreterDataForDeserializedCode(Isolate* isolate, 281 Handle<SharedFunctionInfo> sfi, 282 bool log_code_creation) { 283 Handle<Script> script(Script::cast(sfi->script()), isolate); 284 String name = ReadOnlyRoots(isolate).empty_string(); 285 if (script->name().IsString()) name = String::cast(script->name()); 286 Handle<String> name_handle(name, isolate); 287 288 SharedFunctionInfo::ScriptIterator iter(isolate, *script); 289 for (SharedFunctionInfo shared_info = iter.Next(); !shared_info.is_null(); 290 shared_info = iter.Next()) { 291 IsCompiledScope is_compiled(shared_info, isolate); 292 if (!is_compiled.is_compiled()) continue; 293 DCHECK(shared_info.HasBytecodeArray()); 294 Handle<SharedFunctionInfo> info = handle(shared_info, isolate); 295 Handle<Code> code = isolate->factory()->CopyCode(Handle<Code>::cast( 296 isolate->factory()->interpreter_entry_trampoline_for_profiling())); 297 298 Handle<InterpreterData> interpreter_data = 299 Handle<InterpreterData>::cast(isolate->factory()->NewStruct( 300 INTERPRETER_DATA_TYPE, AllocationType::kOld)); 301 302 interpreter_data->set_bytecode_array(info->GetBytecodeArray(isolate)); 303 interpreter_data->set_interpreter_trampoline(ToCodeT(*code)); 304 if (info->HasBaselineCode()) { 305 FromCodeT(info->baseline_code(kAcquireLoad)) 306 .set_bytecode_or_interpreter_data(*interpreter_data); 307 } else { 308 info->set_interpreter_data(*interpreter_data); 309 } 310 311 if (!log_code_creation) continue; 312 Handle<AbstractCode> abstract_code = Handle<AbstractCode>::cast(code); 313 int line_num = script->GetLineNumber(info->StartPosition()) + 1; 314 int column_num = script->GetColumnNumber(info->StartPosition()) + 1; 315 PROFILE(isolate, 316 CodeCreateEvent(CodeEventListener::FUNCTION_TAG, abstract_code, 317 info, name_handle, line_num, column_num)); 318 } 319} 320#endif // V8_TARGET_ARCH_ARM 321 322class StressOffThreadDeserializeThread final : public base::Thread { 323 public: 324 explicit StressOffThreadDeserializeThread(Isolate* isolate, 325 AlignedCachedData* cached_data) 326 : Thread( 327 base::Thread::Options("StressOffThreadDeserializeThread", 2 * MB)), 328 isolate_(isolate), 329 cached_data_(cached_data) {} 330 331 void Run() final { 332 LocalIsolate local_isolate(isolate_, ThreadKind::kBackground); 333 UnparkedScope unparked_scope(&local_isolate); 334 LocalHandleScope handle_scope(&local_isolate); 335 off_thread_data_ = 336 CodeSerializer::StartDeserializeOffThread(&local_isolate, cached_data_); 337 } 338 339 MaybeHandle<SharedFunctionInfo> Finalize(Isolate* isolate, 340 Handle<String> source, 341 ScriptOriginOptions origin_options) { 342 return CodeSerializer::FinishOffThreadDeserialize( 343 isolate, std::move(off_thread_data_), cached_data_, source, 344 origin_options); 345 } 346 347 private: 348 Isolate* isolate_; 349 AlignedCachedData* cached_data_; 350 CodeSerializer::OffThreadDeserializeData off_thread_data_; 351}; 352 353void FinalizeDeserialization(Isolate* isolate, 354 Handle<SharedFunctionInfo> result, 355 const base::ElapsedTimer& timer) { 356 const bool log_code_creation = 357 isolate->logger()->is_listening_to_code_events() || 358 isolate->is_profiling() || 359 isolate->code_event_dispatcher()->IsListeningToCodeEvents(); 360 361#ifndef V8_TARGET_ARCH_ARM 362 if (V8_UNLIKELY(FLAG_interpreted_frames_native_stack)) 363 CreateInterpreterDataForDeserializedCode(isolate, result, 364 log_code_creation); 365#endif // V8_TARGET_ARCH_ARM 366 367 bool needs_source_positions = isolate->NeedsSourcePositionsForProfiling(); 368 369 if (log_code_creation || FLAG_log_function_events) { 370 Handle<Script> script(Script::cast(result->script()), isolate); 371 Handle<String> name(script->name().IsString() 372 ? String::cast(script->name()) 373 : ReadOnlyRoots(isolate).empty_string(), 374 isolate); 375 376 if (FLAG_log_function_events) { 377 LOG(isolate, 378 FunctionEvent("deserialize", script->id(), 379 timer.Elapsed().InMillisecondsF(), 380 result->StartPosition(), result->EndPosition(), *name)); 381 } 382 if (log_code_creation) { 383 Script::InitLineEnds(isolate, script); 384 385 SharedFunctionInfo::ScriptIterator iter(isolate, *script); 386 for (SharedFunctionInfo info = iter.Next(); !info.is_null(); 387 info = iter.Next()) { 388 if (info.is_compiled()) { 389 Handle<SharedFunctionInfo> shared_info(info, isolate); 390 if (needs_source_positions) { 391 SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, 392 shared_info); 393 } 394 DisallowGarbageCollection no_gc; 395 int line_num = 396 script->GetLineNumber(shared_info->StartPosition()) + 1; 397 int column_num = 398 script->GetColumnNumber(shared_info->StartPosition()) + 1; 399 PROFILE( 400 isolate, 401 CodeCreateEvent( 402 shared_info->is_toplevel() ? CodeEventListener::SCRIPT_TAG 403 : CodeEventListener::FUNCTION_TAG, 404 handle(shared_info->abstract_code(isolate), isolate), 405 shared_info, name, line_num, column_num)); 406 } 407 } 408 } 409 } 410 411 if (needs_source_positions) { 412 Handle<Script> script(Script::cast(result->script()), isolate); 413 Script::InitLineEnds(isolate, script); 414 } 415} 416 417} // namespace 418 419MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize( 420 Isolate* isolate, AlignedCachedData* cached_data, Handle<String> source, 421 ScriptOriginOptions origin_options) { 422 if (FLAG_stress_background_compile) { 423 StressOffThreadDeserializeThread thread(isolate, cached_data); 424 CHECK(thread.Start()); 425 thread.Join(); 426 return thread.Finalize(isolate, source, origin_options); 427 // TODO(leszeks): Compare off-thread deserialized data to on-thread. 428 } 429 430 base::ElapsedTimer timer; 431 if (FLAG_profile_deserialization || FLAG_log_function_events) timer.Start(); 432 433 HandleScope scope(isolate); 434 435 SerializedCodeSanityCheckResult sanity_check_result = 436 SerializedCodeSanityCheckResult::kSuccess; 437 const SerializedCodeData scd = SerializedCodeData::FromCachedData( 438 cached_data, SerializedCodeData::SourceHash(source, origin_options), 439 &sanity_check_result); 440 if (sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) { 441 if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n"); 442 DCHECK(cached_data->rejected()); 443 isolate->counters()->code_cache_reject_reason()->AddSample( 444 static_cast<int>(sanity_check_result)); 445 return MaybeHandle<SharedFunctionInfo>(); 446 } 447 448 // Deserialize. 449 MaybeHandle<SharedFunctionInfo> maybe_result = 450 ObjectDeserializer::DeserializeSharedFunctionInfo(isolate, &scd, source); 451 452 Handle<SharedFunctionInfo> result; 453 if (!maybe_result.ToHandle(&result)) { 454 // Deserializing may fail if the reservations cannot be fulfilled. 455 if (FLAG_profile_deserialization) PrintF("[Deserializing failed]\n"); 456 return MaybeHandle<SharedFunctionInfo>(); 457 } 458 459 if (FLAG_profile_deserialization) { 460 double ms = timer.Elapsed().InMillisecondsF(); 461 int length = cached_data->length(); 462 PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms); 463 } 464 465 FinalizeDeserialization(isolate, result, timer); 466 467 return scope.CloseAndEscape(result); 468} 469 470CodeSerializer::OffThreadDeserializeData 471CodeSerializer::StartDeserializeOffThread(LocalIsolate* local_isolate, 472 AlignedCachedData* cached_data) { 473 OffThreadDeserializeData result; 474 475 DCHECK(!local_isolate->heap()->HasPersistentHandles()); 476 477 const SerializedCodeData scd = 478 SerializedCodeData::FromCachedDataWithoutSource( 479 cached_data, &result.sanity_check_result); 480 if (result.sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) { 481 // Exit early but don't report yet, we'll re-check this when finishing on 482 // the main thread 483 DCHECK(cached_data->rejected()); 484 return result; 485 } 486 487 MaybeHandle<SharedFunctionInfo> local_maybe_result = 488 OffThreadObjectDeserializer::DeserializeSharedFunctionInfo( 489 local_isolate, &scd, &result.scripts); 490 491 result.maybe_result = 492 local_isolate->heap()->NewPersistentMaybeHandle(local_maybe_result); 493 result.persistent_handles = local_isolate->heap()->DetachPersistentHandles(); 494 495 return result; 496} 497 498MaybeHandle<SharedFunctionInfo> CodeSerializer::FinishOffThreadDeserialize( 499 Isolate* isolate, OffThreadDeserializeData&& data, 500 AlignedCachedData* cached_data, Handle<String> source, 501 ScriptOriginOptions origin_options) { 502 base::ElapsedTimer timer; 503 if (FLAG_profile_deserialization || FLAG_log_function_events) timer.Start(); 504 505 HandleScope scope(isolate); 506 507 // Do a source sanity check now that we have the source. It's important for 508 // FromPartiallySanityCheckedCachedData call that the sanity_check_result 509 // holds the result of the off-thread sanity check. 510 SerializedCodeSanityCheckResult sanity_check_result = 511 data.sanity_check_result; 512 const SerializedCodeData scd = 513 SerializedCodeData::FromPartiallySanityCheckedCachedData( 514 cached_data, SerializedCodeData::SourceHash(source, origin_options), 515 &sanity_check_result); 516 if (sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) { 517 // The only case where the deserialization result could exist despite a 518 // check failure is on a source mismatch, since we can't test for this 519 // off-thread. 520 DCHECK_IMPLIES(!data.maybe_result.is_null(), 521 sanity_check_result == 522 SerializedCodeSanityCheckResult::kSourceMismatch); 523 // The only kind of sanity check we can't test for off-thread is a source 524 // mismatch. 525 DCHECK_IMPLIES(sanity_check_result != data.sanity_check_result, 526 sanity_check_result == 527 SerializedCodeSanityCheckResult::kSourceMismatch); 528 if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n"); 529 DCHECK(cached_data->rejected()); 530 isolate->counters()->code_cache_reject_reason()->AddSample( 531 static_cast<int>(sanity_check_result)); 532 return MaybeHandle<SharedFunctionInfo>(); 533 } 534 535 Handle<SharedFunctionInfo> result; 536 if (!data.maybe_result.ToHandle(&result)) { 537 // Deserializing may fail if the reservations cannot be fulfilled. 538 if (FLAG_profile_deserialization) { 539 PrintF("[Off-thread deserializing failed]\n"); 540 } 541 return MaybeHandle<SharedFunctionInfo>(); 542 } 543 544 // Change the result persistent handle into a regular handle. 545 DCHECK(data.persistent_handles->Contains(result.location())); 546 result = handle(*result, isolate); 547 548 // Fix up the source on the script. This should be the only deserialized 549 // script, and the off-thread deserializer should have set its source to 550 // the empty string. 551 DCHECK_EQ(data.scripts.size(), 1); 552 DCHECK_EQ(result->script(), *data.scripts[0]); 553 DCHECK_EQ(Script::cast(result->script()).source(), 554 ReadOnlyRoots(isolate).empty_string()); 555 Script::cast(result->script()).set_source(*source); 556 557 // Fix up the script list to include the newly deserialized script. 558 Handle<WeakArrayList> list = isolate->factory()->script_list(); 559 for (Handle<Script> script : data.scripts) { 560 DCHECK(data.persistent_handles->Contains(script.location())); 561 list = 562 WeakArrayList::AddToEnd(isolate, list, MaybeObjectHandle::Weak(script)); 563 } 564 isolate->heap()->SetRootScriptList(*list); 565 566 if (FLAG_profile_deserialization) { 567 double ms = timer.Elapsed().InMillisecondsF(); 568 int length = cached_data->length(); 569 PrintF("[Finishing off-thread deserialize from %d bytes took %0.3f ms]\n", 570 length, ms); 571 } 572 573 FinalizeDeserialization(isolate, result, timer); 574 575 return scope.CloseAndEscape(result); 576} 577 578SerializedCodeData::SerializedCodeData(const std::vector<byte>* payload, 579 const CodeSerializer* cs) { 580 DisallowGarbageCollection no_gc; 581 582 // Calculate sizes. 583 uint32_t size = kHeaderSize + static_cast<uint32_t>(payload->size()); 584 DCHECK(IsAligned(size, kPointerAlignment)); 585 586 // Allocate backing store and create result data. 587 AllocateData(size); 588 589 // Zero out pre-payload data. Part of that is only used for padding. 590 memset(data_, 0, kHeaderSize); 591 592 // Set header values. 593 SetMagicNumber(); 594 SetHeaderValue(kVersionHashOffset, Version::Hash()); 595 SetHeaderValue(kSourceHashOffset, cs->source_hash()); 596 SetHeaderValue(kFlagHashOffset, FlagList::Hash()); 597 SetHeaderValue(kPayloadLengthOffset, static_cast<uint32_t>(payload->size())); 598 599 // Zero out any padding in the header. 600 memset(data_ + kUnalignedHeaderSize, 0, kHeaderSize - kUnalignedHeaderSize); 601 602 // Copy serialized data. 603 CopyBytes(data_ + kHeaderSize, payload->data(), 604 static_cast<size_t>(payload->size())); 605 uint32_t checksum = 606 FLAG_verify_snapshot_checksum ? Checksum(ChecksummedContent()) : 0; 607 SetHeaderValue(kChecksumOffset, checksum); 608} 609 610SerializedCodeSanityCheckResult SerializedCodeData::SanityCheck( 611 uint32_t expected_source_hash) const { 612 SerializedCodeSanityCheckResult result = SanityCheckWithoutSource(); 613 if (result != SerializedCodeSanityCheckResult::kSuccess) return result; 614 return SanityCheckJustSource(expected_source_hash); 615} 616 617SerializedCodeSanityCheckResult SerializedCodeData::SanityCheckJustSource( 618 uint32_t expected_source_hash) const { 619 uint32_t source_hash = GetHeaderValue(kSourceHashOffset); 620 if (source_hash != expected_source_hash) { 621 return SerializedCodeSanityCheckResult::kSourceMismatch; 622 } 623 return SerializedCodeSanityCheckResult::kSuccess; 624} 625 626SerializedCodeSanityCheckResult SerializedCodeData::SanityCheckWithoutSource() 627 const { 628 if (this->size_ < kHeaderSize) { 629 return SerializedCodeSanityCheckResult::kInvalidHeader; 630 } 631 uint32_t magic_number = GetMagicNumber(); 632 if (magic_number != kMagicNumber) { 633 return SerializedCodeSanityCheckResult::kMagicNumberMismatch; 634 } 635 uint32_t version_hash = GetHeaderValue(kVersionHashOffset); 636 if (version_hash != Version::Hash()) { 637 return SerializedCodeSanityCheckResult::kVersionMismatch; 638 } 639 uint32_t flags_hash = GetHeaderValue(kFlagHashOffset); 640 if (flags_hash != FlagList::Hash()) { 641 return SerializedCodeSanityCheckResult::kFlagsMismatch; 642 } 643 uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset); 644 uint32_t max_payload_length = this->size_ - kHeaderSize; 645 if (payload_length > max_payload_length) { 646 return SerializedCodeSanityCheckResult::kLengthMismatch; 647 } 648 if (FLAG_verify_snapshot_checksum) { 649 uint32_t checksum = GetHeaderValue(kChecksumOffset); 650 if (Checksum(ChecksummedContent()) != checksum) { 651 return SerializedCodeSanityCheckResult::kChecksumMismatch; 652 } 653 } 654 return SerializedCodeSanityCheckResult::kSuccess; 655} 656 657uint32_t SerializedCodeData::SourceHash(Handle<String> source, 658 ScriptOriginOptions origin_options) { 659 const uint32_t source_length = source->length(); 660 661 static constexpr uint32_t kModuleFlagMask = (1 << 31); 662 const uint32_t is_module = origin_options.IsModule() ? kModuleFlagMask : 0; 663 DCHECK_EQ(0, source_length & kModuleFlagMask); 664 665 return source_length | is_module; 666} 667 668// Return ScriptData object and relinquish ownership over it to the caller. 669AlignedCachedData* SerializedCodeData::GetScriptData() { 670 DCHECK(owns_data_); 671 AlignedCachedData* result = new AlignedCachedData(data_, size_); 672 result->AcquireDataOwnership(); 673 owns_data_ = false; 674 data_ = nullptr; 675 return result; 676} 677 678base::Vector<const byte> SerializedCodeData::Payload() const { 679 const byte* payload = data_ + kHeaderSize; 680 DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment)); 681 int length = GetHeaderValue(kPayloadLengthOffset); 682 DCHECK_EQ(data_ + size_, payload + length); 683 return base::Vector<const byte>(payload, length); 684} 685 686SerializedCodeData::SerializedCodeData(AlignedCachedData* data) 687 : SerializedData(const_cast<byte*>(data->data()), data->length()) {} 688 689SerializedCodeData SerializedCodeData::FromCachedData( 690 AlignedCachedData* cached_data, uint32_t expected_source_hash, 691 SerializedCodeSanityCheckResult* rejection_result) { 692 DisallowGarbageCollection no_gc; 693 SerializedCodeData scd(cached_data); 694 *rejection_result = scd.SanityCheck(expected_source_hash); 695 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) { 696 cached_data->Reject(); 697 return SerializedCodeData(nullptr, 0); 698 } 699 return scd; 700} 701 702SerializedCodeData SerializedCodeData::FromCachedDataWithoutSource( 703 AlignedCachedData* cached_data, 704 SerializedCodeSanityCheckResult* rejection_result) { 705 DisallowGarbageCollection no_gc; 706 SerializedCodeData scd(cached_data); 707 *rejection_result = scd.SanityCheckWithoutSource(); 708 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) { 709 cached_data->Reject(); 710 return SerializedCodeData(nullptr, 0); 711 } 712 return scd; 713} 714 715SerializedCodeData SerializedCodeData::FromPartiallySanityCheckedCachedData( 716 AlignedCachedData* cached_data, uint32_t expected_source_hash, 717 SerializedCodeSanityCheckResult* rejection_result) { 718 DisallowGarbageCollection no_gc; 719 // The previous call to FromCachedDataWithoutSource may have already rejected 720 // the cached data, so re-use the previous rejection result if it's not a 721 // success. 722 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) { 723 // FromCachedDataWithoutSource doesn't check the source, so there can't be 724 // a source mismatch. 725 DCHECK_NE(*rejection_result, 726 SerializedCodeSanityCheckResult::kSourceMismatch); 727 cached_data->Reject(); 728 return SerializedCodeData(nullptr, 0); 729 } 730 SerializedCodeData scd(cached_data); 731 *rejection_result = scd.SanityCheckJustSource(expected_source_hash); 732 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) { 733 // This check only checks the source, so the only possible failure is a 734 // source mismatch. 735 DCHECK_EQ(*rejection_result, 736 SerializedCodeSanityCheckResult::kSourceMismatch); 737 cached_data->Reject(); 738 return SerializedCodeData(nullptr, 0); 739 } 740 return scd; 741} 742 743} // namespace internal 744} // namespace v8 745