• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 namespace v8 {
33 namespace internal {
34 
AlignedCachedData(const byte * data,int length)35 AlignedCachedData::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 
CodeSerializer(Isolate * isolate,uint32_t source_hash)46 CodeSerializer::CodeSerializer(Isolate* isolate, uint32_t source_hash)
47     : Serializer(isolate, Snapshot::kDefaultSerializerFlags),
48       source_hash_(source_hash) {}
49 
50 // static
Serialize(Handle<SharedFunctionInfo> info)51 ScriptCompiler::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 
SerializeSharedFunctionInfo(Handle<SharedFunctionInfo> info)98 AlignedCachedData* 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 
SerializeReadOnlyObject(HeapObject obj,const DisallowGarbageCollection & no_gc)112 bool 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 
SerializeObjectImpl(Handle<HeapObject> obj)134 void 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 
SerializeGeneric(Handle<HeapObject> heap_object)267 void 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 
273 namespace {
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).
CreateInterpreterDataForDeserializedCode(Isolate * isolate,Handle<SharedFunctionInfo> sfi,bool log_code_creation)280 void 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 
322 class StressOffThreadDeserializeThread final : public base::Thread {
323  public:
StressOffThreadDeserializeThread(Isolate * isolate,AlignedCachedData * cached_data)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 
Run()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 
Finalize(Isolate * isolate,Handle<String> source,ScriptOriginOptions origin_options)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 
FinalizeDeserialization(Isolate * isolate,Handle<SharedFunctionInfo> result,const base::ElapsedTimer & timer)353 void 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 
Deserialize(Isolate * isolate,AlignedCachedData * cached_data,Handle<String> source,ScriptOriginOptions origin_options)419 MaybeHandle<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 
470 CodeSerializer::OffThreadDeserializeData
StartDeserializeOffThread(LocalIsolate * local_isolate,AlignedCachedData * cached_data)471 CodeSerializer::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 
FinishOffThreadDeserialize(Isolate * isolate,OffThreadDeserializeData && data,AlignedCachedData * cached_data,Handle<String> source,ScriptOriginOptions origin_options)498 MaybeHandle<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 
SerializedCodeData(const std::vector<byte> * payload,const CodeSerializer * cs)578 SerializedCodeData::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 
SanityCheck(uint32_t expected_source_hash) const610 SerializedCodeSanityCheckResult 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 
SanityCheckJustSource(uint32_t expected_source_hash) const617 SerializedCodeSanityCheckResult 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 
SanityCheckWithoutSource() const626 SerializedCodeSanityCheckResult 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 
SourceHash(Handle<String> source,ScriptOriginOptions origin_options)657 uint32_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.
GetScriptData()669 AlignedCachedData* 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 
Payload() const678 base::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 
SerializedCodeData(AlignedCachedData * data)686 SerializedCodeData::SerializedCodeData(AlignedCachedData* data)
687     : SerializedData(const_cast<byte*>(data->data()), data->length()) {}
688 
FromCachedData(AlignedCachedData * cached_data,uint32_t expected_source_hash,SerializedCodeSanityCheckResult * rejection_result)689 SerializedCodeData 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 
FromCachedDataWithoutSource(AlignedCachedData * cached_data,SerializedCodeSanityCheckResult * rejection_result)702 SerializedCodeData 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 
FromPartiallySanityCheckedCachedData(AlignedCachedData * cached_data,uint32_t expected_source_hash,SerializedCodeSanityCheckResult * rejection_result)715 SerializedCodeData 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