v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
code-serializer.cc
Go to the documentation of this file.
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
6
7#include <memory>
8
9#include "src/base/logging.h"
14#include "src/common/globals.h"
17#include "src/heap/heap-inl.h"
20#include "src/logging/log.h"
24#include "src/objects/slots.h"
29#include "src/utils/version.h"
30
31namespace v8 {
32namespace internal {
33
34AlignedCachedData::AlignedCachedData(const uint8_t* data, int length)
35 : owns_data_(false), rejected_(false), data_(data), length_(length) {
36 if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) {
37 uint8_t* copy = NewArray<uint8_t>(length);
38 DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment));
39 CopyBytes(copy, data, length);
40 data_ = copy;
42 }
43}
44
45CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash)
46 : Serializer(isolate, Snapshot::kDefaultSerializerFlags),
47 source_hash_(source_hash) {}
48
49// static
51 Isolate* isolate, Handle<SharedFunctionInfo> info) {
52 TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.SerializeCode");
53 NestedTimedHistogramScope histogram_timer(
54 isolate->counters()->compile_serialize());
55 RCS_SCOPE(isolate, RuntimeCallCounterId::kCompileSerialize);
56 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileSerialize");
57
59 if (v8_flags.profile_deserialization) timer.Start();
60 DirectHandle<Script> script(Cast<Script>(info->script()), isolate);
61 if (v8_flags.trace_serializer) {
62 PrintF("[Serializing from");
63 ShortPrint(script->name());
64 PrintF("]\n");
65 }
66#if V8_ENABLE_WEBASSEMBLY
67 // TODO(7110): Enable serialization of Asm modules once the AsmWasmData is
68 // context independent.
69 if (script->ContainsAsmModule()) return nullptr;
70#endif // V8_ENABLE_WEBASSEMBLY
71
72 // Serialize code object.
73 DirectHandle<String> source(Cast<String>(script->source()), isolate);
74 DirectHandle<FixedArray> wrapped_arguments;
75 if (script->is_wrapped()) {
76 wrapped_arguments =
77 DirectHandle<FixedArray>(script->wrapped_arguments(), isolate);
78 }
79
80 HandleScope scope(isolate);
81 CodeSerializer cs(isolate,
82 SerializedCodeData::SourceHash(source, wrapped_arguments,
83 script->origin_options()));
85
86#ifndef DEBUG
87 cs.reference_map()->AddAttachedReference(*source);
88#endif
89 AlignedCachedData* cached_data = cs.SerializeSharedFunctionInfo(info);
90
91 if (v8_flags.profile_deserialization) {
92 double ms = timer.Elapsed().InMillisecondsF();
93 int length = cached_data->length();
94 PrintF("[Serializing to %d bytes took %0.3f ms]\n", length, ms);
95 }
96
98 new ScriptCompiler::CachedData(cached_data->data(), cached_data->length(),
100 cached_data->ReleaseDataOwnership();
101 delete cached_data;
102
103 return result;
104}
105
109
110 VisitRootPointer(Root::kHandleScope, nullptr,
111 FullObjectSlot(info.location()));
113 Pad();
114
116
117 return data.GetScriptData();
118}
119
121 SlotType slot_type) {
122 ReadOnlyRoots roots(isolate());
123 InstanceType instance_type;
124 {
126 Tagged<HeapObject> raw = *obj;
127 if (SerializeHotObject(raw)) return;
128 if (SerializeRoot(raw)) return;
129 if (SerializeBackReference(raw)) return;
130 if (SerializeReadOnlyObjectReference(raw, &sink_)) return;
131
132 instance_type = raw->map()->instance_type();
133 CHECK(!InstanceTypeChecker::IsInstructionStream(instance_type));
134 }
135
136 if (InstanceTypeChecker::IsScript(instance_type)) {
137 DirectHandle<FixedArray> host_options;
139 {
141 Tagged<Script> script_obj = Cast<Script>(*obj);
142 DCHECK_NE(script_obj->compilation_type(), Script::CompilationType::kEval);
143 // We want to differentiate between undefined and uninitialized_symbol for
144 // context_data for now. It is hack to allow debugging for scripts that
145 // are included as a part of custom snapshot. (see
146 // debug::Script::IsEmbedded())
148 script_obj->context_data();
149 if (raw_context_data != roots.undefined_value() &&
150 raw_context_data != roots.uninitialized_symbol()) {
151 script_obj->set_context_data(roots.undefined_value());
152 }
153 context_data = direct_handle(raw_context_data, isolate());
154 // We don't want to serialize host options to avoid serializing
155 // unnecessary object graph.
156 host_options =
157 direct_handle(script_obj->host_defined_options(), isolate());
158 script_obj->set_host_defined_options(roots.empty_fixed_array());
159 }
160 SerializeGeneric(obj, slot_type);
161 {
163 Tagged<Script> script_obj = Cast<Script>(*obj);
164 script_obj->set_host_defined_options(*host_options);
165 script_obj->set_context_data(*context_data);
166 }
167 return;
168 } else if (InstanceTypeChecker::IsSharedFunctionInfo(instance_type)) {
169 DirectHandle<DebugInfo> debug_info;
170 CachedTieringDecision cached_tiering_decision;
171 bool restore_bytecode = false;
172 {
175 DCHECK(!sfi->IsApiFunction());
176#if V8_ENABLE_WEBASSEMBLY
177 // TODO(7110): Enable serializing of Asm modules once the AsmWasmData
178 // is context independent.
179 DCHECK(!sfi->HasAsmWasmData());
180#endif // V8_ENABLE_WEBASSEMBLY
181
182 if (auto maybe_debug_info = sfi->TryGetDebugInfo(isolate())) {
183 debug_info = direct_handle(maybe_debug_info.value(), isolate());
184 // Clear debug info.
185 if (debug_info->HasInstrumentedBytecodeArray()) {
186 restore_bytecode = true;
187 sfi->SetActiveBytecodeArray(
188 debug_info->OriginalBytecodeArray(isolate()), isolate());
189 }
190 }
191 if (v8_flags.profile_guided_optimization) {
192 cached_tiering_decision = sfi->cached_tiering_decision();
193 if (cached_tiering_decision > CachedTieringDecision::kEarlySparkplug) {
194 sfi->set_cached_tiering_decision(
196 }
197 }
198 }
199 SerializeGeneric(obj, slot_type);
202 if (restore_bytecode) {
203 sfi->SetActiveBytecodeArray(debug_info->DebugBytecodeArray(isolate()),
204 isolate());
205 }
206 if (v8_flags.profile_guided_optimization &&
207 cached_tiering_decision > CachedTieringDecision::kEarlySparkplug) {
208 sfi->set_cached_tiering_decision(cached_tiering_decision);
209 }
210 return;
211 } else if (InstanceTypeChecker::IsUncompiledDataWithoutPreparseDataWithJob(
212 instance_type)) {
215 Address job = data->job();
216 data->set_job(kNullAddress);
217 SerializeGeneric(data, slot_type);
218 data->set_job(job);
219 return;
220 } else if (InstanceTypeChecker::IsUncompiledDataWithPreparseDataAndJob(
221 instance_type)) {
224 Address job = data->job();
225 data->set_job(kNullAddress);
226 SerializeGeneric(data, slot_type);
227 data->set_job(job);
228 return;
229 } else if (InstanceTypeChecker::IsScopeInfo(instance_type)) {
230 // TODO(ishell): define a dedicated instance type for DependentCode and
231 // serialize DependentCode objects as an empty_dependent_code instead
232 // of customizing ScopeInfo serialization.
233 static_assert(DEPENDENT_CODE_TYPE == WEAK_ARRAY_LIST_TYPE);
234 Handle<ScopeInfo> scope_info = Cast<ScopeInfo>(obj);
236 bool restore_dependent_code = false;
237 if (scope_info->SloppyEvalCanExtendVars()) {
238 // If |scope_info| has a dependent code field, serialize it as an empty
239 // dependent code in order to avoid accidental serialization of optimized
240 // code.
241 Tagged<DependentCode> empty_dependent_code =
243 if (scope_info->dependent_code() != empty_dependent_code) {
244 dependent_code = direct_handle(scope_info->dependent_code(), isolate());
245 restore_dependent_code = true;
246 scope_info->set_dependent_code(empty_dependent_code);
247 }
248 }
249 SerializeGeneric(scope_info, slot_type);
250 if (restore_dependent_code) {
251 scope_info->set_dependent_code(*dependent_code);
252 }
253 return;
254 }
255
256 // NOTE(mmarchini): If we try to serialize an InterpreterData our process
257 // will crash since it stores a code object. Instead, we serialize the
258 // bytecode array stored within the InterpreterData, which is the important
259 // information. On deserialization we'll create our code objects again, if
260 // --interpreted-frames-native-stack is on. See v8:9122 for more context
261 if (V8_UNLIKELY(isolate()->interpreted_frames_native_stack()) &&
262 IsInterpreterData(*obj)) {
263 obj = handle(Cast<InterpreterData>(*obj)->bytecode_array(), isolate());
264 }
265
266 // Past this point we should not see any (context-specific) maps anymore.
267 CHECK(!InstanceTypeChecker::IsMap(instance_type));
268 // There should be no references to the global object embedded.
269 CHECK(!InstanceTypeChecker::IsJSGlobalProxy(instance_type) &&
270 !InstanceTypeChecker::IsJSGlobalObject(instance_type));
271 // Embedded FixedArrays that need rehashing must support rehashing.
272 CHECK_IMPLIES(obj->NeedsRehashing(cage_base()),
273 obj->CanBeRehashed(cage_base()));
274 // We expect no instantiated function objects or contexts.
275 CHECK(!InstanceTypeChecker::IsJSFunction(instance_type) &&
276 !InstanceTypeChecker::IsContext(instance_type));
277
278 SerializeGeneric(obj, slot_type);
279}
280
282 SlotType slot_type) {
283 // Object has not yet been serialized. Serialize it here.
284 ObjectSerializer serializer(this, heap_object, &sink_);
285 serializer.Serialize(slot_type);
286}
287
288namespace {
289
290// NOTE(mmarchini): when v8_flags.interpreted_frames_native_stack is on, we want
291// to create duplicates of InterpreterEntryTrampoline for the deserialized
292// functions, otherwise we'll call the builtin IET for those functions (which
293// is not what a user of this flag wants).
294void CreateInterpreterDataForDeserializedCode(
295 Isolate* isolate, DirectHandle<SharedFunctionInfo> result_sfi,
296 bool log_code_creation) {
297 DCHECK_IMPLIES(log_code_creation, isolate->NeedsSourcePositions());
298
299 DirectHandle<Script> script(Cast<Script>(result_sfi->script()), isolate);
300 if (log_code_creation) Script::InitLineEnds(isolate, script);
301
302 Tagged<String> name = ReadOnlyRoots(isolate).empty_string();
303 if (IsString(script->name())) name = Cast<String>(script->name());
304 DirectHandle<String> name_handle(name, isolate);
305
306 SharedFunctionInfo::ScriptIterator iter(isolate, *script);
307 for (Tagged<SharedFunctionInfo> shared_info = iter.Next();
308 !shared_info.is_null(); shared_info = iter.Next()) {
309 IsCompiledScope is_compiled(shared_info, isolate);
310 if (!is_compiled.is_compiled()) continue;
311 DCHECK(shared_info->HasBytecodeArray());
312 DirectHandle<SharedFunctionInfo> sfi(shared_info, isolate);
313
314 DirectHandle<BytecodeArray> bytecode(sfi->GetBytecodeArray(isolate),
315 isolate);
316 DirectHandle<Code> code =
318 DirectHandle<InterpreterData> interpreter_data =
319 isolate->factory()->NewInterpreterData(bytecode, code);
320
321 if (sfi->HasBaselineCode()) {
322 sfi->baseline_code(kAcquireLoad)
323 ->set_bytecode_or_interpreter_data(*interpreter_data);
324 } else {
325 sfi->set_interpreter_data(isolate, *interpreter_data);
326 }
327
328 if (!log_code_creation) continue;
329
330 DirectHandle<AbstractCode> abstract_code = Cast<AbstractCode>(code);
331 Script::PositionInfo info;
332 Script::GetPositionInfo(script, sfi->StartPosition(), &info);
333 int line_num = info.line_start + 1;
334 int column_num = info.line_end + 1;
335 PROFILE(isolate,
336 CodeCreateEvent(LogEventListener::CodeTag::kFunction, abstract_code,
337 sfi, name_handle, line_num, column_num));
338 }
339}
340
341class StressOffThreadDeserializeThread final : public base::Thread {
342 public:
343 explicit StressOffThreadDeserializeThread(Isolate* isolate,
344 AlignedCachedData* cached_data)
345 : Thread(
346 base::Thread::Options("StressOffThreadDeserializeThread", 2 * MB)),
347 isolate_(isolate),
348 cached_data_(cached_data) {}
349
350 void Run() final {
351 LocalIsolate local_isolate(isolate_, ThreadKind::kBackground);
352 UnparkedScope unparked_scope(&local_isolate);
353 LocalHandleScope handle_scope(&local_isolate);
355 CodeSerializer::StartDeserializeOffThread(&local_isolate, cached_data_);
356 }
357
358 MaybeDirectHandle<SharedFunctionInfo> Finalize(
359 Isolate* isolate, DirectHandle<String> source,
360 const ScriptDetails& script_details) {
362 isolate, std::move(off_thread_data_), cached_data_, source,
363 script_details);
364 }
365
366 private:
367 Isolate* isolate_;
368 AlignedCachedData* cached_data_;
369 CodeSerializer::OffThreadDeserializeData off_thread_data_;
370};
371
372void FinalizeDeserialization(Isolate* isolate,
373 DirectHandle<SharedFunctionInfo> result,
374 const base::ElapsedTimer& timer,
375 const ScriptDetails& script_details) {
376 // Devtools can report time in this function as profiler overhead, since none
377 // of the following tasks would need to happen normally.
379 "V8.FinalizeDeserialization");
380
381 const bool log_code_creation = isolate->IsLoggingCodeCreation();
382
383 if (V8_UNLIKELY(isolate->interpreted_frames_native_stack())) {
384 CreateInterpreterDataForDeserializedCode(isolate, result,
385 log_code_creation);
386 }
387
388 DirectHandle<Script> script(Cast<Script>(result->script()), isolate);
389 // Reset the script details, including host-defined options.
390 {
392 SetScriptFieldsFromDetails(isolate, *script, script_details, &no_gc);
393 }
394
395 bool needs_source_positions = isolate->NeedsSourcePositions();
396 if (!log_code_creation && !needs_source_positions) return;
397
398 if (needs_source_positions) {
399 Script::InitLineEnds(isolate, script);
400 }
401
402 DirectHandle<String> name(IsString(script->name())
403 ? Cast<String>(script->name())
404 : ReadOnlyRoots(isolate).empty_string(),
405 isolate);
406
407 if (V8_UNLIKELY(v8_flags.log_function_events)) {
408 LOG(isolate,
409 FunctionEvent("deserialize", script->id(),
410 timer.Elapsed().InMillisecondsF(),
411 result->StartPosition(), result->EndPosition(), *name));
412 }
413
414 SharedFunctionInfo::ScriptIterator iter(isolate, *script);
415 for (Tagged<SharedFunctionInfo> info = iter.Next(); !info.is_null();
416 info = iter.Next()) {
417 if (!info->is_compiled()) continue;
418 DirectHandle<SharedFunctionInfo> shared_info(info, isolate);
419 if (needs_source_positions) {
421 }
422 Script::PositionInfo pos_info;
423 Script::GetPositionInfo(script, shared_info->StartPosition(), &pos_info);
424 int line_num = pos_info.line + 1;
425 int column_num = pos_info.column + 1;
426 PROFILE(
427 isolate,
428 CodeCreateEvent(
429 shared_info->is_toplevel() ? LogEventListener::CodeTag::kScript
430 : LogEventListener::CodeTag::kFunction,
431 direct_handle(shared_info->abstract_code(isolate), isolate),
432 shared_info, name, line_num, column_num));
433 }
434}
435
436#ifdef V8_ENABLE_SPARKPLUG
437void BaselineBatchCompileIfSparkplugCompiled(Isolate* isolate,
438 Tagged<Script> script) {
439 // Here is main thread, we trigger early baseline compilation only in
440 // concurrent sparkplug and baseline batch compilation mode which consumes
441 // little main thread execution time.
442 if (v8_flags.concurrent_sparkplug && v8_flags.baseline_batch_compilation) {
443 SharedFunctionInfo::ScriptIterator iter(isolate, script);
444 for (Tagged<SharedFunctionInfo> info = iter.Next(); !info.is_null();
445 info = iter.Next()) {
446 if (info->cached_tiering_decision() != CachedTieringDecision::kPending &&
447 CanCompileWithBaseline(isolate, info)) {
448 isolate->baseline_batch_compiler()->EnqueueSFI(info);
449 }
450 }
451 }
452}
453#else
454void BaselineBatchCompileIfSparkplugCompiled(Isolate*, Tagged<Script>) {}
455#endif // V8_ENABLE_SPARKPLUG
456
458 switch (result) {
459 case SerializedCodeSanityCheckResult::kSuccess:
460 return "success";
461 case SerializedCodeSanityCheckResult::kMagicNumberMismatch:
462 return "magic number mismatch";
463 case SerializedCodeSanityCheckResult::kVersionMismatch:
464 return "version mismatch";
465 case SerializedCodeSanityCheckResult::kSourceMismatch:
466 return "source mismatch";
467 case SerializedCodeSanityCheckResult::kFlagsMismatch:
468 return "flags mismatch";
469 case SerializedCodeSanityCheckResult::kChecksumMismatch:
470 return "checksum mismatch";
471 case SerializedCodeSanityCheckResult::kInvalidHeader:
472 return "invalid header";
473 case SerializedCodeSanityCheckResult::kLengthMismatch:
474 return "length mismatch";
475 case SerializedCodeSanityCheckResult::kReadOnlySnapshotChecksumMismatch:
476 return "read-only snapshot checksum mismatch";
477 }
478}
479} // namespace
480
482 Isolate* isolate, AlignedCachedData* cached_data,
483 DirectHandle<String> source, const ScriptDetails& script_details,
484 MaybeDirectHandle<Script> maybe_cached_script) {
485 if (v8_flags.stress_background_compile) {
486 StressOffThreadDeserializeThread thread(isolate, cached_data);
487 CHECK(thread.Start());
488 thread.Join();
489 return thread.Finalize(isolate, source, script_details);
490 // TODO(leszeks): Compare off-thread deserialized data to on-thread.
491 }
492
493 base::ElapsedTimer timer;
494 if (v8_flags.profile_deserialization || v8_flags.log_function_events) {
495 timer.Start();
496 }
497
498 HandleScope scope(isolate);
499
500 DirectHandle<FixedArray> wrapped_arguments;
501 if (!script_details.wrapped_arguments.is_null()) {
502 wrapped_arguments = script_details.wrapped_arguments.ToHandleChecked();
503 }
504
505 SerializedCodeSanityCheckResult sanity_check_result =
506 SerializedCodeSanityCheckResult::kSuccess;
508 isolate, cached_data,
509 SerializedCodeData::SourceHash(source, wrapped_arguments,
510 script_details.origin_options),
511 &sanity_check_result);
512 if (sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) {
513 if (v8_flags.profile_deserialization) {
514 PrintF("[Cached code failed check: %s]\n", ToString(sanity_check_result));
515 }
516 DCHECK(cached_data->rejected());
517 isolate->counters()->code_cache_reject_reason()->AddSample(
518 static_cast<int>(sanity_check_result));
520 }
521
522 // Deserialize.
525
527 if (!maybe_result.ToHandle(&result)) {
528 // Deserializing may fail if the reservations cannot be fulfilled.
529 if (v8_flags.profile_deserialization) PrintF("[Deserializing failed]\n");
531 }
532
533 // Check whether the newly deserialized data should be merged into an
534 // existing Script from the Isolate compilation cache. If so, perform
535 // the merge in a single-threaded manner since this deserialization was
536 // single-threaded.
537 if (DirectHandle<Script> cached_script;
538 maybe_cached_script.ToHandle(&cached_script)) {
540 merge.SetUpOnMainThread(isolate, cached_script);
542 DirectHandle<Script> new_script(Cast<Script>(result->script()), isolate);
543 merge.BeginMergeInBackground(isolate->AsLocalIsolate(), new_script);
545 result = merge.CompleteMergeInForeground(isolate, new_script);
546 }
547
548 Tagged<Script> script = Cast<Script>(result->script());
549 script->set_deserialized(true);
550 BaselineBatchCompileIfSparkplugCompiled(isolate, script);
551 if (v8_flags.profile_deserialization) {
552 double ms = timer.Elapsed().InMillisecondsF();
553 int length = cached_data->length();
554 PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
555 }
556
557 FinalizeDeserialization(isolate, result, timer, script_details);
558
559 return scope.CloseAndEscape(result);
560}
561
563 LocalHeap* heap) {
564 std::unique_ptr<PersistentHandles> previous_persistent_handles =
565 heap->DetachPersistentHandles();
566 heap->AttachPersistentHandles(std::move(persistent_handles));
567
568 DCHECK_EQ(scripts.size(), 1);
569 // Make a non-persistent handle to return.
571 DCHECK_EQ(*script, maybe_result.ToHandleChecked()->script());
572
573 persistent_handles = heap->DetachPersistentHandles();
574 if (previous_persistent_handles) {
575 heap->AttachPersistentHandles(std::move(previous_persistent_handles));
576 }
577
578 return script;
579}
580
583 AlignedCachedData* cached_data) {
585
586 DCHECK(!local_isolate->heap()->HasPersistentHandles());
587
588 const SerializedCodeData scd =
590 local_isolate, cached_data, &result.sanity_check_result);
591 if (result.sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) {
592 // Exit early but don't report yet, we'll re-check this when finishing on
593 // the main thread
594 DCHECK(cached_data->rejected());
595 return result;
596 }
597
598 MaybeDirectHandle<SharedFunctionInfo> local_maybe_result =
600 local_isolate, &scd, &result.scripts);
601
602 result.maybe_result =
603 local_isolate->heap()->NewPersistentMaybeHandle(local_maybe_result);
604 result.persistent_handles = local_isolate->heap()->DetachPersistentHandles();
605
606 return result;
607}
608
611 Isolate* isolate, OffThreadDeserializeData&& data,
612 AlignedCachedData* cached_data, DirectHandle<String> source,
613 const ScriptDetails& script_details,
614 BackgroundMergeTask* background_merge_task) {
615 base::ElapsedTimer timer;
616 if (v8_flags.profile_deserialization || v8_flags.log_function_events) {
617 timer.Start();
618 }
619
620 HandleScope scope(isolate);
621
622 DirectHandle<FixedArray> wrapped_arguments;
623 if (!script_details.wrapped_arguments.is_null()) {
624 wrapped_arguments = script_details.wrapped_arguments.ToHandleChecked();
625 }
626
627 // Do a source sanity check now that we have the source. It's important for
628 // FromPartiallySanityCheckedCachedData call that the sanity_check_result
629 // holds the result of the off-thread sanity check.
630 SerializedCodeSanityCheckResult sanity_check_result =
631 data.sanity_check_result;
632 const SerializedCodeData scd =
634 cached_data,
635 SerializedCodeData::SourceHash(source, wrapped_arguments,
636 script_details.origin_options),
637 &sanity_check_result);
638 if (sanity_check_result != SerializedCodeSanityCheckResult::kSuccess) {
639 // The only case where the deserialization result could exist despite a
640 // check failure is on a source mismatch, since we can't test for this
641 // off-thread.
642 DCHECK_IMPLIES(!data.maybe_result.is_null(),
643 sanity_check_result ==
644 SerializedCodeSanityCheckResult::kSourceMismatch);
645 // The only kind of sanity check we can't test for off-thread is a source
646 // mismatch.
647 DCHECK_IMPLIES(sanity_check_result != data.sanity_check_result,
648 sanity_check_result ==
649 SerializedCodeSanityCheckResult::kSourceMismatch);
650 if (v8_flags.profile_deserialization) {
651 PrintF("[Cached code failed check: %s]\n", ToString(sanity_check_result));
652 }
653 DCHECK(cached_data->rejected());
654 isolate->counters()->code_cache_reject_reason()->AddSample(
655 static_cast<int>(sanity_check_result));
657 }
658
660 if (!data.maybe_result.ToHandle(&result)) {
661 // Deserializing may fail if the reservations cannot be fulfilled.
662 if (v8_flags.profile_deserialization) {
663 PrintF("[Off-thread deserializing failed]\n");
664 }
666 }
667
668 // Change the result persistent handle into a regular handle.
669 DCHECK(data.persistent_handles->Contains(result.location()));
670 result = handle(*result, isolate);
671
672 if (background_merge_task &&
673 background_merge_task->HasPendingForegroundWork()) {
674 DCHECK_EQ(data.scripts.size(), 1);
675 DirectHandle<Script> new_script = data.scripts[0];
676 result =
677 background_merge_task->CompleteMergeInForeground(isolate, new_script);
678 DCHECK(Object::StrictEquals(Cast<Script>(result->script())->source(),
679 *source));
680 DCHECK(isolate->factory()->script_list()->Contains(
681 MakeWeak(result->script())));
682 } else {
683 DirectHandle<Script> result_script(Cast<Script>(result->script()), isolate);
684 // Fix up the source on the script. This should be the only deserialized
685 // script, and the off-thread deserializer should have set its source to the
686 // empty string. In debug mode the code cache does contain the original
687 // source.
688 DCHECK_EQ(data.scripts.size(), 1);
689 DCHECK_EQ(*result_script, *data.scripts[0]);
690#ifdef DEBUG
691 if (!Cast<String>(result_script->source())->Equals(*source)) {
692 isolate->PushStackTraceAndDie(
693 reinterpret_cast<void*>(result_script->source().ptr()),
694 reinterpret_cast<void*>(source->ptr()));
695 }
696#else
697 CHECK_EQ(result_script->source(), ReadOnlyRoots(isolate).empty_string());
698#endif
699 Script::SetSource(isolate, result_script, source);
700
701 // Fix up the script list to include the newly deserialized script.
702 Handle<WeakArrayList> list = isolate->factory()->script_list();
703 for (Handle<Script> script : data.scripts) {
704 script->set_deserialized(true);
705 BaselineBatchCompileIfSparkplugCompiled(isolate, *script);
706 DCHECK(data.persistent_handles->Contains(script.location()));
707 list = WeakArrayList::AddToEnd(isolate, list,
709 }
710 isolate->heap()->SetRootScriptList(*list);
711 }
712
713 if (v8_flags.profile_deserialization) {
714 double ms = timer.Elapsed().InMillisecondsF();
715 int length = cached_data->length();
716 PrintF("[Finishing off-thread deserialize from %d bytes took %0.3f ms]\n",
717 length, ms);
718 }
719
720 FinalizeDeserialization(isolate, result, timer, script_details);
721
722 DCHECK(!background_merge_task ||
723 !background_merge_task->HasPendingForegroundWork());
724
725 return scope.CloseAndEscape(result);
726}
727
728SerializedCodeData::SerializedCodeData(const std::vector<uint8_t>* payload,
729 const CodeSerializer* cs) {
731
732 // Calculate sizes.
733 uint32_t size = kHeaderSize + static_cast<uint32_t>(payload->size());
735
736 // Allocate backing store and create result data.
737 AllocateData(size);
738
739 // Zero out pre-payload data. Part of that is only used for padding.
740 memset(data_, 0, kHeaderSize);
741
742 // Set header values.
743 SetMagicNumber();
744 SetHeaderValue(kVersionHashOffset, Version::Hash());
745 SetHeaderValue(kSourceHashOffset, cs->source_hash());
746 SetHeaderValue(kFlagHashOffset, FlagList::Hash());
747 SetHeaderValue(kReadOnlySnapshotChecksumOffset,
749 cs->isolate()->snapshot_blob()));
750 SetHeaderValue(kPayloadLengthOffset, static_cast<uint32_t>(payload->size()));
751
752 // Zero out any padding in the header.
753 memset(data_ + kUnalignedHeaderSize, 0, kHeaderSize - kUnalignedHeaderSize);
754
755 // Copy serialized data.
756 CopyBytes(data_ + kHeaderSize, payload->data(),
757 static_cast<size_t>(payload->size()));
758 uint32_t checksum =
759 v8_flags.verify_snapshot_checksum ? Checksum(ChecksummedContent()) : 0;
760 SetHeaderValue(kChecksumOffset, checksum);
761}
762
764 uint32_t expected_ro_snapshot_checksum,
765 uint32_t expected_source_hash) const {
767 SanityCheckWithoutSource(expected_ro_snapshot_checksum);
768 if (result != SerializedCodeSanityCheckResult::kSuccess) return result;
769 return SanityCheckJustSource(expected_source_hash);
770}
771
773 uint32_t expected_source_hash) const {
774 uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
775 if (source_hash != expected_source_hash) {
776 return SerializedCodeSanityCheckResult::kSourceMismatch;
777 }
778 return SerializedCodeSanityCheckResult::kSuccess;
779}
780
782 uint32_t expected_ro_snapshot_checksum) const {
783 if (size_ < kHeaderSize) {
784 return SerializedCodeSanityCheckResult::kInvalidHeader;
785 }
786 uint32_t magic_number = GetMagicNumber();
787 if (magic_number != kMagicNumber) {
788 return SerializedCodeSanityCheckResult::kMagicNumberMismatch;
789 }
790 uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
791 if (version_hash != Version::Hash()) {
792 return SerializedCodeSanityCheckResult::kVersionMismatch;
793 }
794 uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
795 if (flags_hash != FlagList::Hash()) {
796 return SerializedCodeSanityCheckResult::kFlagsMismatch;
797 }
798 uint32_t ro_snapshot_checksum =
799 GetHeaderValue(kReadOnlySnapshotChecksumOffset);
800 if (ro_snapshot_checksum != expected_ro_snapshot_checksum) {
801 return SerializedCodeSanityCheckResult::kReadOnlySnapshotChecksumMismatch;
802 }
803 uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
804 uint32_t max_payload_length = size_ - kHeaderSize;
805 if (payload_length > max_payload_length) {
806 return SerializedCodeSanityCheckResult::kLengthMismatch;
807 }
808 if (v8_flags.verify_snapshot_checksum) {
809 uint32_t checksum = GetHeaderValue(kChecksumOffset);
810 if (Checksum(ChecksummedContent()) != checksum) {
811 return SerializedCodeSanityCheckResult::kChecksumMismatch;
812 }
813 }
814 return SerializedCodeSanityCheckResult::kSuccess;
815}
816
818 DirectHandle<String> source, DirectHandle<FixedArray> wrapped_arguments,
819 ScriptOriginOptions origin_options) {
820 using LengthField = base::BitField<uint32_t, 0, 29>;
821 static_assert(String::kMaxLength <= LengthField::kMax,
822 "String length must fit into a LengthField");
823 using HasWrappedArgumentsField = LengthField::Next<bool, 1>;
824 using IsModuleField = HasWrappedArgumentsField::Next<bool, 1>;
825
826 uint32_t hash = 0;
827 hash = LengthField::update(hash, source->length());
828 hash = HasWrappedArgumentsField::update(hash, !wrapped_arguments.is_null());
829 hash = IsModuleField::update(hash, origin_options.IsModule());
830 return hash;
831}
832
833// Return ScriptData object and relinquish ownership over it to the caller.
835 DCHECK(owns_data_);
837 result->AcquireDataOwnership();
838 owns_data_ = false;
839 data_ = nullptr;
840 return result;
841}
842
844 const uint8_t* payload = data_ + kHeaderSize;
845 DCHECK(IsAligned(reinterpret_cast<intptr_t>(payload), kPointerAlignment));
846 int length = GetHeaderValue(kPayloadLengthOffset);
847 DCHECK_EQ(data_ + size_, payload + length);
848 return base::Vector<const uint8_t>(payload, length);
849}
850
852 : SerializedData(const_cast<uint8_t*>(data->data()), data->length()) {}
853
855 Isolate* isolate, AlignedCachedData* cached_data,
856 uint32_t expected_source_hash,
857 SerializedCodeSanityCheckResult* rejection_result) {
859 SerializedCodeData scd(cached_data);
860 *rejection_result = scd.SanityCheck(
861 Snapshot::ExtractReadOnlySnapshotChecksum(isolate->snapshot_blob()),
862 expected_source_hash);
863 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
864 cached_data->Reject();
865 return SerializedCodeData(nullptr, 0);
866 }
867 return scd;
868}
869
871 LocalIsolate* local_isolate, AlignedCachedData* cached_data,
872 SerializedCodeSanityCheckResult* rejection_result) {
874 SerializedCodeData scd(cached_data);
875 *rejection_result =
877 local_isolate->snapshot_blob()));
878 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
879 cached_data->Reject();
880 return SerializedCodeData(nullptr, 0);
881 }
882 return scd;
883}
884
886 AlignedCachedData* cached_data, uint32_t expected_source_hash,
887 SerializedCodeSanityCheckResult* rejection_result) {
889 // The previous call to FromCachedDataWithoutSource may have already rejected
890 // the cached data, so reuse the previous rejection result if it's not a
891 // success.
892 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
893 // FromCachedDataWithoutSource doesn't check the source, so there can't be
894 // a source mismatch.
895 DCHECK_NE(*rejection_result,
896 SerializedCodeSanityCheckResult::kSourceMismatch);
897 cached_data->Reject();
898 return SerializedCodeData(nullptr, 0);
899 }
900 SerializedCodeData scd(cached_data);
901 *rejection_result = scd.SanityCheckJustSource(expected_source_hash);
902 if (*rejection_result != SerializedCodeSanityCheckResult::kSuccess) {
903 // This check only checks the source, so the only possible failure is a
904 // source mismatch.
905 DCHECK_EQ(*rejection_result,
906 SerializedCodeSanityCheckResult::kSourceMismatch);
907 cached_data->Reject();
908 return SerializedCodeData(nullptr, 0);
909 }
910 return scd;
911}
912
913} // namespace internal
914} // namespace v8
Isolate * isolate_
uint8_t data_[MAX_STACK_LENGTH]
interpreter::Bytecode bytecode
Definition builtins.cc:43
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
bool IsModule() const
Definition v8-message.h:46
const uint8_t * data() const
AlignedCachedData(const uint8_t *data, int length)
void SetUpOnMainThread(Isolate *isolate, Handle< String > source_text, const ScriptDetails &script_details, LanguageMode language_mode)
Definition compiler.cc:2278
void BeginMergeInBackground(LocalIsolate *isolate, DirectHandle< Script > new_script)
Definition compiler.cc:2395
Handle< SharedFunctionInfo > CompleteMergeInForeground(Isolate *isolate, DirectHandle< Script > new_script)
Definition compiler.cc:2511
static DirectHandle< Code > CreateInterpreterEntryTrampolineForProfiling(Isolate *isolate)
Definition builtins.cc:430
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< SharedFunctionInfo > FinishOffThreadDeserialize(Isolate *isolate, OffThreadDeserializeData &&data, AlignedCachedData *cached_data, DirectHandle< String > source, const ScriptDetails &script_details, BackgroundMergeTask *background_merge_task=nullptr)
void SerializeGeneric(Handle< HeapObject > heap_object, SlotType slot_type)
void SerializeObjectImpl(Handle< HeapObject > o, SlotType slot_type) override
static V8_EXPORT_PRIVATE ScriptCompiler::CachedData * Serialize(Isolate *isolate, Handle< SharedFunctionInfo > info)
CodeSerializer(const CodeSerializer &)=delete
AlignedCachedData * SerializeSharedFunctionInfo(Handle< SharedFunctionInfo > info)
static V8_WARN_UNUSED_RESULT OffThreadDeserializeData StartDeserializeOffThread(LocalIsolate *isolate, AlignedCachedData *cached_data)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< SharedFunctionInfo > Deserialize(Isolate *isolate, AlignedCachedData *cached_data, DirectHandle< String > source, const ScriptDetails &script_details, MaybeDirectHandle< Script > maybe_cached_script={})
static V8_EXPORT_PRIVATE Tagged< DependentCode > empty_dependent_code(const ReadOnlyRoots &roots)
V8_INLINE bool is_null() const
Definition handles.h:693
static uint32_t Hash()
Definition flags.cc:1196
HandleType< T > CloseAndEscape(HandleType< T > handle_value)
std::unique_ptr< PersistentHandles > DetachPersistentHandles()
MaybeIndirectHandle< T > NewPersistentMaybeHandle(MaybeHandleType< T > maybe_handle)
Definition local-heap.h:94
const v8::StartupData * snapshot_blob() const
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(DirectHandle< S > *out) const
static MaybeObjectDirectHandle Weak(Tagged< Object > object, Isolate *isolate)
static MaybeDirectHandle< SharedFunctionInfo > DeserializeSharedFunctionInfo(Isolate *isolate, const SerializedCodeData *data, DirectHandle< String > source)
static V8_EXPORT_PRIVATE bool StrictEquals(Tagged< Object > obj, Tagged< Object > that)
Definition objects.cc:986
static MaybeDirectHandle< SharedFunctionInfo > DeserializeSharedFunctionInfo(LocalIsolate *isolate, const SerializedCodeData *data, std::vector< IndirectHandle< Script > > *deserialized_scripts)
virtual void VisitRootPointer(Root root, const char *description, FullObjectSlot p)
Definition visitors.h:75
static void InitLineEnds(Isolate *isolate, DirectHandle< Script > script)
Definition script-inl.h:201
static void SetSource(Isolate *isolate, DirectHandle< Script > script, DirectHandle< String > source)
Definition objects.cc:4349
static bool GetPositionInfo(DirectHandle< Script > script, int position, PositionInfo *info, OffsetFlag offset_flag=OffsetFlag::kWithOffset)
Definition objects.cc:4367
static SerializedCodeData FromPartiallySanityCheckedCachedData(AlignedCachedData *cached_data, uint32_t expected_source_hash, SerializedCodeSanityCheckResult *rejection_result)
AlignedCachedData * GetScriptData()
SerializedCodeSanityCheckResult SanityCheckJustSource(uint32_t expected_source_hash) const
SerializedCodeSanityCheckResult SanityCheckWithoutSource(uint32_t expected_ro_snapshot_checksum) const
base::Vector< const uint8_t > Payload() const
static SerializedCodeData FromCachedData(Isolate *isolate, AlignedCachedData *cached_data, uint32_t expected_source_hash, SerializedCodeSanityCheckResult *rejection_result)
static SerializedCodeData FromCachedDataWithoutSource(LocalIsolate *local_isolate, AlignedCachedData *cached_data, SerializedCodeSanityCheckResult *rejection_result)
static uint32_t SourceHash(DirectHandle< String > source, DirectHandle< FixedArray > wrapped_arguments, ScriptOriginOptions origin_options)
SerializedCodeSanityCheckResult SanityCheck(uint32_t expected_ro_snapshot_checksum, uint32_t expected_source_hash) const
SerializedCodeData(const std::vector< uint8_t > *payload, const CodeSerializer *cs)
PtrComprCageBase cage_base() const
Definition serializer.h:199
Isolate * isolate() const
Definition serializer.h:195
SnapshotByteSink sink_
Definition serializer.h:323
bool SerializeRoot(Tagged< HeapObject > obj)
bool SerializeReadOnlyObjectReference(Tagged< HeapObject > obj, SnapshotByteSink *sink)
void Pad(int padding_offset=0)
bool SerializeHotObject(Tagged< HeapObject > obj)
bool SerializeBackReference(Tagged< HeapObject > obj)
static void EnsureSourcePositionsAvailable(Isolate *isolate, DirectHandle< SharedFunctionInfo > shared_info)
const std::vector< uint8_t > * data() const
static V8_EXPORT_PRIVATE uint32_t ExtractReadOnlySnapshotChecksum(const v8::StartupData *data)
Definition snapshot.cc:677
static const uint32_t kMaxLength
Definition string.h:511
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
static uint32_t Hash()
Definition version.h:30
NEVER_READ_ONLY_SPACE static V8_EXPORT_PRIVATE Handle< WeakArrayList > AddToEnd(Isolate *isolate, Handle< WeakArrayList > array, MaybeObjectDirectHandle value)
#define PROFILE(the_isolate, Call)
Definition code-events.h:59
AlignedCachedData * cached_data_
CodeSerializer::OffThreadDeserializeData off_thread_data_
const int size_
Definition assembler.cc:132
Handle< SharedFunctionInfo > info
ZoneVector< RpoNumber > & result
#define LOG(isolate, Call)
Definition log.h:78
InstructionOperand source
const int length_
Definition mul-fft.cc:473
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr intptr_t kPointerAlignment
Definition globals.h:945
void CopyBytes(T *dst, const T *src, size_t num_bytes)
Definition memcopy.h:261
constexpr const char * ToString(DeoptimizeKind kind)
Definition globals.h:880
v8::ScriptCompiler::CachedData::CompatibilityCheckResult SerializedCodeSanityCheckResult
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
void PrintF(const char *format,...)
Definition utils.cc:39
Tagged(T object) -> Tagged< T >
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
void SetScriptFieldsFromDetails(Isolate *isolate, Tagged< Script > script, const ScriptDetails &script_details, DisallowGarbageCollection *no_gc)
Definition compiler.cc:1763
void ShortPrint(Tagged< Object > obj, FILE *out)
Definition objects.cc:1865
Tagged< MaybeWeak< T > > MakeWeak(Tagged< T > value)
Definition tagged.h:893
V8_EXPORT_PRIVATE FlagValues v8_flags
static constexpr Address kNullAddress
Definition v8-internal.h:53
T * NewArray(size_t size)
Definition allocation.h:43
bool CanCompileWithBaseline(Isolate *isolate, Tagged< SharedFunctionInfo > shared)
Definition baseline.cc:82
uint32_t Checksum(base::Vector< const uint8_t > payload)
kInterpreterTrampolineOffset script
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
static constexpr AcquireLoadTag kAcquireLoad
Definition globals.h:2908
#define RCS_SCOPE(...)
static const uint32_t kReadOnlySnapshotChecksumOffset
Definition snapshot.cc:96
static const uint32_t kChecksumOffset
Definition snapshot.cc:95
#define CHECK_IMPLIES(lhs, rhs)
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define CHECK_EQ(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr bool IsAligned(T value, U alignment)
Definition macros.h:403
std::vector< IndirectHandle< Script > > scripts
MaybeIndirectHandle< SharedFunctionInfo > maybe_result
std::unique_ptr< PersistentHandles > persistent_handles
DirectHandle< Script > GetOnlyScript(LocalHeap *heap)
const ScriptOriginOptions origin_options
MaybeHandle< FixedArray > wrapped_arguments
#define TRACE_EVENT0(category_group, name)
#define TRACE_DISABLED_BY_DEFAULT(name)
#define TRACE_EVENT_CALL_STATS_SCOPED(isolate, category_group, name)
#define V8_UNLIKELY(condition)
Definition v8config.h:660