File: | out/../deps/v8/src/web-snapshot/web-snapshot.cc |
Warning: | line 1225, column 3 Undefined or garbage value returned to caller |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | // Copyright 2021 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/web-snapshot/web-snapshot.h" | |||
6 | ||||
7 | #include <limits> | |||
8 | ||||
9 | #include "include/v8-isolate.h" | |||
10 | #include "include/v8-local-handle.h" | |||
11 | #include "include/v8-object.h" | |||
12 | #include "include/v8-primitive.h" | |||
13 | #include "include/v8-script.h" | |||
14 | #include "src/api/api-inl.h" | |||
15 | #include "src/base/platform/wrappers.h" | |||
16 | #include "src/handles/handles.h" | |||
17 | #include "src/logging/runtime-call-stats-scope.h" | |||
18 | #include "src/objects/contexts.h" | |||
19 | #include "src/objects/js-regexp-inl.h" | |||
20 | #include "src/objects/script.h" | |||
21 | ||||
22 | namespace v8 { | |||
23 | namespace internal { | |||
24 | ||||
25 | constexpr uint8_t WebSnapshotSerializerDeserializer::kMagicNumber[4]; | |||
26 | ||||
27 | // When encountering an error during deserializing, we note down the error but | |||
28 | // don't bail out from processing the snapshot further. This is to speed up | |||
29 | // deserialization; the error case is now slower since we don't bail out, but | |||
30 | // the non-error case is faster, since we don't repeatedly check for errors. | |||
31 | // (Invariant: we might fill our internal data structures with arbitrary data, | |||
32 | // but it shouldn't have an observable effect.) | |||
33 | ||||
34 | // This doesn't increase the complexity of processing the data in a robust and | |||
35 | // secure way. We cannot trust the data anyway, so every upcoming byte can have | |||
36 | // an arbitrary value, not depending on whether or not we've encountered an | |||
37 | // error before. | |||
38 | void WebSnapshotSerializerDeserializer::Throw(const char* message) { | |||
39 | if (error_message_ != nullptr) { | |||
40 | return; | |||
41 | } | |||
42 | error_message_ = message; | |||
43 | if (!isolate_->has_pending_exception()) { | |||
44 | isolate_->Throw(*factory()->NewError( | |||
45 | MessageTemplate::kWebSnapshotError, | |||
46 | factory()->NewStringFromAsciiChecked(error_message_))); | |||
47 | } | |||
48 | } | |||
49 | ||||
50 | uint32_t WebSnapshotSerializerDeserializer::FunctionKindToFunctionFlags( | |||
51 | FunctionKind kind) { | |||
52 | // TODO(v8:11525): Support more function kinds. | |||
53 | switch (kind) { | |||
54 | case FunctionKind::kNormalFunction: | |||
55 | case FunctionKind::kArrowFunction: | |||
56 | case FunctionKind::kGeneratorFunction: | |||
57 | case FunctionKind::kAsyncFunction: | |||
58 | case FunctionKind::kAsyncArrowFunction: | |||
59 | case FunctionKind::kAsyncGeneratorFunction: | |||
60 | case FunctionKind::kBaseConstructor: | |||
61 | case FunctionKind::kDefaultBaseConstructor: | |||
62 | case FunctionKind::kConciseMethod: | |||
63 | case FunctionKind::kAsyncConciseMethod: | |||
64 | break; | |||
65 | default: | |||
66 | Throw("Unsupported function kind"); | |||
67 | } | |||
68 | auto flags = AsyncFunctionBitField::encode(IsAsyncFunction(kind)) | | |||
69 | GeneratorFunctionBitField::encode(IsGeneratorFunction(kind)) | | |||
70 | ArrowFunctionBitField::encode(IsArrowFunction(kind)) | | |||
71 | MethodBitField::encode(IsConciseMethod(kind)) | | |||
72 | StaticBitField::encode(IsStatic(kind)) | | |||
73 | ClassConstructorBitField::encode(IsClassConstructor(kind)) | | |||
74 | DefaultConstructorBitField::encode(IsDefaultConstructor(kind)) | | |||
75 | DerivedConstructorBitField::encode(IsDerivedConstructor(kind)); | |||
76 | return flags; | |||
77 | } | |||
78 | ||||
79 | // TODO(v8:11525): Optionally, use an enum instead. | |||
80 | FunctionKind WebSnapshotSerializerDeserializer::FunctionFlagsToFunctionKind( | |||
81 | uint32_t flags) { | |||
82 | FunctionKind kind; | |||
83 | if (IsFunctionOrMethod(flags)) { | |||
84 | if (ArrowFunctionBitField::decode(flags) && MethodBitField::decode(flags)) { | |||
85 | kind = FunctionKind::kInvalid; | |||
86 | } else { | |||
87 | uint32_t index = AsyncFunctionBitField::decode(flags) << 0 | | |||
88 | GeneratorFunctionBitField::decode(flags) << 1 | | |||
89 | (ArrowFunctionBitField::decode(flags) || | |||
90 | StaticBitField::decode(flags)) | |||
91 | << 2 | | |||
92 | MethodBitField::decode(flags) << 3; | |||
93 | static const FunctionKind kFunctionKinds[] = { | |||
94 | // kNormalFunction | |||
95 | // is_generator = false | |||
96 | FunctionKind::kNormalFunction, // is_async = false | |||
97 | FunctionKind::kAsyncFunction, // is_async = true | |||
98 | // is_generator = true | |||
99 | FunctionKind::kGeneratorFunction, // is_async = false | |||
100 | FunctionKind::kAsyncGeneratorFunction, // is_async = true | |||
101 | ||||
102 | // kArrowFunction | |||
103 | // is_generator = false | |||
104 | FunctionKind::kArrowFunction, // is_async = false | |||
105 | FunctionKind::kAsyncArrowFunction, // is_async = true | |||
106 | // is_generator = true | |||
107 | FunctionKind::kInvalid, // is_async = false | |||
108 | FunctionKind::kInvalid, // is_async = true | |||
109 | ||||
110 | // kNonStaticMethod | |||
111 | // is_generator = false | |||
112 | FunctionKind::kConciseMethod, // is_async = false | |||
113 | FunctionKind::kAsyncConciseMethod, // is_async = true | |||
114 | // is_generator = true | |||
115 | // TODO(v8::11525) Support FunctionKind::kConciseGeneratorMethod. | |||
116 | FunctionKind::kInvalid, // is_async = false | |||
117 | // TODO(v8::11525) Support FunctionKind::kAsyncConciseGeneratorMethod. | |||
118 | FunctionKind::kInvalid, // is_async = true | |||
119 | ||||
120 | // kStaticMethod | |||
121 | // is_generator = false | |||
122 | // TODO(v8::11525) Support FunctionKind::kStaticConciseMethod. | |||
123 | FunctionKind::kInvalid, // is_async = false | |||
124 | // TODO(v8::11525) Support FunctionKind::kStaticAsyncConciseMethod. | |||
125 | FunctionKind::kInvalid, // is_async = true | |||
126 | // is_generator = true | |||
127 | // TODO(v8::11525) Support | |||
128 | // FunctionKind::kStaticConciseGeneratorMethod. | |||
129 | FunctionKind::kInvalid, // is_async = false | |||
130 | // TODO(v8::11525) Support | |||
131 | // FunctionKind::kStaticAsyncConciseGeneratorMethod. | |||
132 | FunctionKind::kInvalid // is_async = true | |||
133 | }; | |||
134 | kind = kFunctionKinds[index]; | |||
135 | } | |||
136 | } else if (IsConstructor(flags)) { | |||
137 | static const FunctionKind kFunctionKinds[] = { | |||
138 | // is_derived = false | |||
139 | FunctionKind::kBaseConstructor, // is_default = false | |||
140 | FunctionKind::kDefaultBaseConstructor, // is_default = true | |||
141 | // is_derived = true | |||
142 | FunctionKind::kDerivedConstructor, // is_default = false | |||
143 | FunctionKind::kDefaultDerivedConstructor // is_default = true | |||
144 | }; | |||
145 | kind = kFunctionKinds[flags >> DefaultConstructorBitField::kShift]; | |||
146 | } else { | |||
147 | kind = FunctionKind::kInvalid; | |||
148 | } | |||
149 | if (kind == FunctionKind::kInvalid) { | |||
150 | Throw("Invalid function flags\n"); | |||
151 | } | |||
152 | return kind; | |||
153 | } | |||
154 | ||||
155 | bool WebSnapshotSerializerDeserializer::IsFunctionOrMethod(uint32_t flags) { | |||
156 | uint32_t mask = AsyncFunctionBitField::kMask | | |||
157 | GeneratorFunctionBitField::kMask | | |||
158 | ArrowFunctionBitField::kMask | MethodBitField::kMask | | |||
159 | StaticBitField::kMask; | |||
160 | return (flags & mask) == flags; | |||
161 | } | |||
162 | ||||
163 | bool WebSnapshotSerializerDeserializer::IsConstructor(uint32_t flags) { | |||
164 | uint32_t mask = ClassConstructorBitField::kMask | | |||
165 | DefaultConstructorBitField::kMask | | |||
166 | DerivedConstructorBitField::kMask; | |||
167 | return ClassConstructorBitField::decode(flags) && (flags & mask) == flags; | |||
168 | } | |||
169 | ||||
170 | uint32_t WebSnapshotSerializerDeserializer::GetDefaultAttributeFlags() { | |||
171 | auto flags = ReadOnlyBitField::encode(false) | | |||
172 | ConfigurableBitField::encode(true) | | |||
173 | EnumerableBitField::encode(true); | |||
174 | return flags; | |||
175 | } | |||
176 | ||||
177 | uint32_t WebSnapshotSerializerDeserializer::AttributesToFlags( | |||
178 | PropertyDetails details) { | |||
179 | auto flags = ReadOnlyBitField::encode(details.IsReadOnly()) | | |||
180 | ConfigurableBitField::encode(details.IsConfigurable()) | | |||
181 | EnumerableBitField::encode(details.IsEnumerable()); | |||
182 | return flags; | |||
183 | } | |||
184 | ||||
185 | PropertyAttributes WebSnapshotSerializerDeserializer::FlagsToAttributes( | |||
186 | uint32_t flags) { | |||
187 | int attributes = ReadOnlyBitField::decode(flags) * READ_ONLY + | |||
188 | !ConfigurableBitField::decode(flags) * DONT_DELETE + | |||
189 | !EnumerableBitField::decode(flags) * DONT_ENUM; | |||
190 | return PropertyAttributesFromInt(attributes); | |||
191 | } | |||
192 | ||||
193 | WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate) | |||
194 | : WebSnapshotSerializer(reinterpret_cast<v8::internal::Isolate*>(isolate)) { | |||
195 | } | |||
196 | ||||
197 | WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate) | |||
198 | : WebSnapshotSerializerDeserializer(isolate), | |||
199 | string_serializer_(isolate_, nullptr), | |||
200 | map_serializer_(isolate_, nullptr), | |||
201 | context_serializer_(isolate_, nullptr), | |||
202 | function_serializer_(isolate_, nullptr), | |||
203 | class_serializer_(isolate_, nullptr), | |||
204 | array_serializer_(isolate_, nullptr), | |||
205 | object_serializer_(isolate_, nullptr), | |||
206 | export_serializer_(isolate_, nullptr), | |||
207 | external_objects_ids_(isolate_->heap()), | |||
208 | string_ids_(isolate_->heap()), | |||
209 | map_ids_(isolate_->heap()), | |||
210 | context_ids_(isolate_->heap()), | |||
211 | function_ids_(isolate_->heap()), | |||
212 | class_ids_(isolate_->heap()), | |||
213 | array_ids_(isolate_->heap()), | |||
214 | object_ids_(isolate_->heap()), | |||
215 | all_strings_(isolate_->heap()) { | |||
216 | auto empty_array_list = factory()->empty_array_list(); | |||
217 | contexts_ = empty_array_list; | |||
218 | functions_ = empty_array_list; | |||
219 | classes_ = empty_array_list; | |||
220 | arrays_ = empty_array_list; | |||
221 | objects_ = empty_array_list; | |||
222 | strings_ = empty_array_list; | |||
223 | maps_ = empty_array_list; | |||
224 | } | |||
225 | ||||
226 | WebSnapshotSerializer::~WebSnapshotSerializer() {} | |||
227 | ||||
228 | bool WebSnapshotSerializer::TakeSnapshot( | |||
229 | Handle<Object> object, MaybeHandle<FixedArray> maybe_externals, | |||
230 | WebSnapshotData& data_out) { | |||
231 | if (string_ids_.size() > 0) { | |||
232 | Throw("Can't reuse WebSnapshotSerializer"); | |||
233 | return false; | |||
234 | } | |||
235 | if (!maybe_externals.is_null()) { | |||
236 | ShallowDiscoverExternals(*maybe_externals.ToHandleChecked()); | |||
237 | } | |||
238 | ||||
239 | if (object->IsHeapObject()) Discover(Handle<HeapObject>::cast(object)); | |||
240 | ||||
241 | ConstructSource(); | |||
242 | // The export is serialized with the empty string as name; we need to | |||
243 | // "discover" the name here. | |||
244 | DiscoverString(factory()->empty_string()); | |||
245 | SerializeExport(object, factory()->empty_string()); | |||
246 | ||||
247 | WriteSnapshot(data_out.buffer, data_out.buffer_size); | |||
248 | ||||
249 | if (has_error()) { | |||
250 | isolate_->ReportPendingMessages(); | |||
251 | return false; | |||
252 | } | |||
253 | return true; | |||
254 | } | |||
255 | ||||
256 | bool WebSnapshotSerializer::TakeSnapshot(v8::Local<v8::Context> context, | |||
257 | v8::Local<v8::PrimitiveArray> exports, | |||
258 | WebSnapshotData& data_out) { | |||
259 | if (string_ids_.size() > 0) { | |||
260 | Throw("Can't reuse WebSnapshotSerializer"); | |||
261 | return false; | |||
262 | } | |||
263 | v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | |||
264 | ||||
265 | std::unique_ptr<Handle<JSObject>[]> export_objects( | |||
266 | new Handle<JSObject>[exports->Length()]); | |||
267 | for (int i = 0, length = exports->Length(); i < length; ++i) { | |||
268 | v8::Local<v8::String> str = | |||
269 | exports->Get(v8_isolate, i)->ToString(context).ToLocalChecked(); | |||
270 | if (str->Length() == 0) { | |||
271 | continue; | |||
272 | } | |||
273 | // Discover the export name. | |||
274 | DiscoverString(Handle<String>::cast(Utils::OpenHandle(*str))); | |||
275 | v8::ScriptCompiler::Source source(str); | |||
276 | auto script = ScriptCompiler::Compile(context, &source).ToLocalChecked(); | |||
277 | v8::MaybeLocal<v8::Value> script_result = script->Run(context); | |||
278 | v8::Local<v8::Object> v8_object; | |||
279 | if (script_result.IsEmpty() || | |||
280 | !script_result.ToLocalChecked()->ToObject(context).ToLocal( | |||
281 | &v8_object)) { | |||
282 | Throw("Exported object not found"); | |||
283 | return false; | |||
284 | } | |||
285 | export_objects[i] = Handle<JSObject>::cast(Utils::OpenHandle(*v8_object)); | |||
286 | Discover(export_objects[i]); | |||
287 | } | |||
288 | ||||
289 | ConstructSource(); | |||
290 | ||||
291 | for (int i = 0, length = exports->Length(); i < length; ++i) { | |||
292 | v8::Local<v8::String> str = | |||
293 | exports->Get(v8_isolate, i)->ToString(context).ToLocalChecked(); | |||
294 | if (str->Length() == 0) { | |||
295 | continue; | |||
296 | } | |||
297 | SerializeExport(export_objects[i], | |||
298 | Handle<String>::cast(Utils::OpenHandle(*str))); | |||
299 | } | |||
300 | ||||
301 | WriteSnapshot(data_out.buffer, data_out.buffer_size); | |||
302 | ||||
303 | if (has_error()) { | |||
304 | isolate_->ReportPendingMessages(); | |||
305 | return false; | |||
306 | } | |||
307 | return true; | |||
308 | } | |||
309 | ||||
310 | void WebSnapshotSerializer::SerializePendingItems() { | |||
311 | // The information about string reference counts is now complete. The strings | |||
312 | // in strings_ are not in place and can be serialized now. The in-place | |||
313 | // strings will be serialized as part of their respective objects. | |||
314 | for (int i = 0; i < strings_->Length(); ++i) { | |||
315 | Handle<String> string = handle(String::cast(strings_->Get(i)), isolate_); | |||
316 | SerializeString(string, string_serializer_); | |||
317 | } | |||
318 | ||||
319 | for (int i = 0; i < maps_->Length(); ++i) { | |||
320 | Handle<Map> map = handle(Map::cast(maps_->Get(i)), isolate_); | |||
321 | SerializeMap(map); | |||
322 | } | |||
323 | ||||
324 | // Serialize the items in the reverse order. The items at the end of the | |||
325 | // contexts_ etc get lower IDs and vice versa. IDs which items use for | |||
326 | // referring to each other are reversed by Get<item>Id functions(). | |||
327 | for (int i = contexts_->Length() - 1; i >= 0; --i) { | |||
328 | Handle<Context> context = | |||
329 | handle(Context::cast(contexts_->Get(i)), isolate_); | |||
330 | SerializeContext(context); | |||
331 | } | |||
332 | for (int i = functions_->Length() - 1; i >= 0; --i) { | |||
333 | Handle<JSFunction> function = | |||
334 | handle(JSFunction::cast(functions_->Get(i)), isolate_); | |||
335 | SerializeFunction(function); | |||
336 | } | |||
337 | for (int i = classes_->Length() - 1; i >= 0; --i) { | |||
338 | Handle<JSFunction> function = | |||
339 | handle(JSFunction::cast(classes_->Get(i)), isolate_); | |||
340 | SerializeClass(function); | |||
341 | } | |||
342 | for (int i = arrays_->Length() - 1; i >= 0; --i) { | |||
343 | Handle<JSArray> array = handle(JSArray::cast(arrays_->Get(i)), isolate_); | |||
344 | SerializeArray(array); | |||
345 | } | |||
346 | for (int i = objects_->Length() - 1; i >= 0; --i) { | |||
347 | Handle<JSObject> object = | |||
348 | handle(JSObject::cast(objects_->Get(i)), isolate_); | |||
349 | SerializeObject(object); | |||
350 | } | |||
351 | } | |||
352 | ||||
353 | // Format (full snapshot): | |||
354 | // - Magic number (4 bytes) | |||
355 | // - String count | |||
356 | // - For each string: | |||
357 | // - Serialized string | |||
358 | // - Shape count | |||
359 | // - For each shape: | |||
360 | // - Serialized shape | |||
361 | // - Context count | |||
362 | // - For each context: | |||
363 | // - Serialized context | |||
364 | // - Function count | |||
365 | // - For each function: | |||
366 | // - Serialized function | |||
367 | // - Object count | |||
368 | // - For each object: | |||
369 | // - Serialized object | |||
370 | // - Export count | |||
371 | // - For each export: | |||
372 | // - Serialized export | |||
373 | void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer, | |||
374 | size_t& buffer_size) { | |||
375 | if (has_error()) { | |||
376 | return; | |||
377 | } | |||
378 | SerializePendingItems(); | |||
379 | ||||
380 | ValueSerializer total_serializer(isolate_, nullptr); | |||
381 | size_t needed_size = | |||
382 | sizeof(kMagicNumber) + string_serializer_.buffer_size_ + | |||
383 | map_serializer_.buffer_size_ + context_serializer_.buffer_size_ + | |||
384 | function_serializer_.buffer_size_ + class_serializer_.buffer_size_ + | |||
385 | array_serializer_.buffer_size_ + object_serializer_.buffer_size_ + | |||
386 | export_serializer_.buffer_size_ + 8 * sizeof(uint32_t); | |||
387 | if (total_serializer.ExpandBuffer(needed_size).IsNothing()) { | |||
388 | Throw("Out of memory"); | |||
389 | return; | |||
390 | } | |||
391 | ||||
392 | total_serializer.WriteRawBytes(kMagicNumber, 4); | |||
393 | WriteObjects(total_serializer, string_count(), string_serializer_, "strings"); | |||
394 | WriteObjects(total_serializer, map_count(), map_serializer_, "maps"); | |||
395 | WriteObjects(total_serializer, context_count(), context_serializer_, | |||
396 | "contexts"); | |||
397 | WriteObjects(total_serializer, function_count(), function_serializer_, | |||
398 | "functions"); | |||
399 | WriteObjects(total_serializer, array_count(), array_serializer_, "arrays"); | |||
400 | WriteObjects(total_serializer, object_count(), object_serializer_, "objects"); | |||
401 | WriteObjects(total_serializer, class_count(), class_serializer_, "classes"); | |||
402 | WriteObjects(total_serializer, export_count_, export_serializer_, "exports"); | |||
403 | ||||
404 | if (has_error()) { | |||
405 | return; | |||
406 | } | |||
407 | ||||
408 | auto result = total_serializer.Release(); | |||
409 | buffer = result.first; | |||
410 | buffer_size = result.second; | |||
411 | } | |||
412 | void WebSnapshotSerializer::WriteObjects(ValueSerializer& destination, | |||
413 | size_t count, ValueSerializer& source, | |||
414 | const char* name) { | |||
415 | if (count > std::numeric_limits<uint32_t>::max()) { | |||
416 | Throw("Too many objects"); | |||
417 | return; | |||
418 | } | |||
419 | destination.WriteUint32(static_cast<uint32_t>(count)); | |||
420 | destination.WriteRawBytes(source.buffer_, source.buffer_size_); | |||
421 | } | |||
422 | ||||
423 | bool WebSnapshotSerializer::InsertIntoIndexMap(ObjectCacheIndexMap& map, | |||
424 | HeapObject heap_object, | |||
425 | uint32_t& id) { | |||
426 | DisallowGarbageCollection no_gc; | |||
427 | int index_out; | |||
428 | if (external_objects_ids_.Lookup(heap_object, &index_out)) { | |||
429 | return true; | |||
430 | } | |||
431 | bool found = map.LookupOrInsert(heap_object, &index_out); | |||
432 | id = static_cast<uint32_t>(index_out); | |||
433 | return found; | |||
434 | } | |||
435 | ||||
436 | // Format: | |||
437 | // - Length | |||
438 | // - Raw bytes (data) | |||
439 | void WebSnapshotSerializer::SerializeString(Handle<String> string, | |||
440 | ValueSerializer& serializer) { | |||
441 | DisallowGarbageCollection no_gc; | |||
442 | String::FlatContent flat = string->GetFlatContent(no_gc); | |||
443 | DCHECK(flat.IsFlat())((void) 0); | |||
444 | if (flat.IsOneByte()) { | |||
445 | base::Vector<const uint8_t> chars = flat.ToOneByteVector(); | |||
446 | serializer.WriteUint32(chars.length()); | |||
447 | serializer.WriteRawBytes(chars.begin(), chars.length() * sizeof(uint8_t)); | |||
448 | } else if (flat.IsTwoByte()) { | |||
449 | v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | |||
450 | v8::Local<v8::String> api_string = Utils::ToLocal(string); | |||
451 | int length = api_string->Utf8Length(v8_isolate); | |||
452 | std::unique_ptr<char[]> buffer(new char[length]); | |||
453 | api_string->WriteUtf8(v8_isolate, buffer.get(), length); | |||
454 | serializer.WriteUint32(length); | |||
455 | serializer.WriteRawBytes(buffer.get(), length * sizeof(uint8_t)); | |||
456 | } else { | |||
457 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
458 | } | |||
459 | } | |||
460 | ||||
461 | // Format (serialized shape): | |||
462 | // - PropertyAttributesType | |||
463 | // - 0 if the __proto__ is Object.prototype, 1 + object id for the __proto__ | |||
464 | // otherwise | |||
465 | // - Property count | |||
466 | // - For each property | |||
467 | // - String id (name) | |||
468 | // - If the PropertyAttributesType is CUSTOM: attributes | |||
469 | void WebSnapshotSerializer::SerializeMap(Handle<Map> map) { | |||
470 | int first_custom_index = -1; | |||
471 | std::vector<Handle<String>> keys; | |||
472 | std::vector<uint32_t> attributes; | |||
473 | keys.reserve(map->NumberOfOwnDescriptors()); | |||
474 | attributes.reserve(map->NumberOfOwnDescriptors()); | |||
475 | for (InternalIndex i : map->IterateOwnDescriptors()) { | |||
476 | Handle<Name> key(map->instance_descriptors(kRelaxedLoad).GetKey(i), | |||
477 | isolate_); | |||
478 | keys.push_back(Handle<String>::cast(key)); | |||
479 | ||||
480 | PropertyDetails details = | |||
481 | map->instance_descriptors(kRelaxedLoad).GetDetails(i); | |||
482 | ||||
483 | if (details.location() != PropertyLocation::kField) { | |||
484 | Throw("Properties which are not fields not supported"); | |||
485 | return; | |||
486 | } | |||
487 | if (first_custom_index >= 0 || details.IsReadOnly() || | |||
488 | !details.IsConfigurable() || details.IsDontEnum()) { | |||
489 | if (first_custom_index == -1) first_custom_index = i.as_int(); | |||
490 | attributes.push_back(AttributesToFlags(details)); | |||
491 | } | |||
492 | } | |||
493 | ||||
494 | map_serializer_.WriteUint32(first_custom_index == -1 | |||
495 | ? PropertyAttributesType::DEFAULT | |||
496 | : PropertyAttributesType::CUSTOM); | |||
497 | ||||
498 | if (map->prototype() == | |||
499 | isolate_->native_context()->initial_object_prototype()) { | |||
500 | map_serializer_.WriteUint32(0); | |||
501 | } else { | |||
502 | // TODO(v8:11525): Support non-JSObject prototypes, at least null. Recognize | |||
503 | // well-known objects to that we don't end up encoding them in the snapshot. | |||
504 | if (!map->prototype().IsJSObject()) { | |||
505 | Throw("Non-JSObject __proto__s not supported"); | |||
506 | return; | |||
507 | } | |||
508 | uint32_t prototype_id = GetObjectId(JSObject::cast(map->prototype())); | |||
509 | map_serializer_.WriteUint32(prototype_id + 1); | |||
510 | } | |||
511 | ||||
512 | map_serializer_.WriteUint32(static_cast<uint32_t>(keys.size())); | |||
513 | ||||
514 | uint32_t default_flags = GetDefaultAttributeFlags(); | |||
515 | for (size_t i = 0; i < keys.size(); ++i) { | |||
516 | if (first_custom_index >= 0) { | |||
517 | if (static_cast<int>(i) < first_custom_index) { | |||
518 | map_serializer_.WriteUint32(default_flags); | |||
519 | } else { | |||
520 | map_serializer_.WriteUint32(attributes[i - first_custom_index]); | |||
521 | } | |||
522 | } | |||
523 | WriteStringId(keys[i], map_serializer_); | |||
524 | } | |||
525 | } | |||
526 | ||||
527 | // Construct the minimal source string to be included in the snapshot. Maintain | |||
528 | // the "inner function is textually inside its outer function" relationship. | |||
529 | // Example: | |||
530 | // Input: | |||
531 | // Full source: abcdefghijklmnopqrstuvwxyzåäö | |||
532 | // Functions: 11111111 22222222 3 | |||
533 | // Inner functions: 44 55 666 | |||
534 | // Output: | |||
535 | // Constructed source: defghijkstuvwxyzö | |||
536 | // Functions: 11111111222222223 | |||
537 | // Inner functions 44 55 666 | |||
538 | void WebSnapshotSerializer::ConstructSource() { | |||
539 | if (source_intervals_.empty()) { | |||
540 | return; | |||
541 | } | |||
542 | ||||
543 | Handle<String> source_string = factory()->empty_string(); | |||
544 | int current_interval_start = 0; | |||
545 | int current_interval_end = 0; | |||
546 | for (const auto& interval : source_intervals_) { | |||
547 | DCHECK_LE(current_interval_start, interval.first)((void) 0); // Iterated in order. | |||
548 | DCHECK_LE(interval.first, interval.second)((void) 0); | |||
549 | if (interval.second <= current_interval_end) { | |||
550 | // This interval is fully within the current interval. We don't need to | |||
551 | // include any new source code, just record the position conversion. | |||
552 | auto offset_within_parent = interval.first - current_interval_start; | |||
553 | source_offset_to_compacted_source_offset_[interval.first] = | |||
554 | source_offset_to_compacted_source_offset_[current_interval_start] + | |||
555 | offset_within_parent; | |||
556 | continue; | |||
557 | } | |||
558 | // Start a new interval. | |||
559 | current_interval_start = interval.first; | |||
560 | current_interval_end = interval.second; | |||
561 | source_offset_to_compacted_source_offset_[current_interval_start] = | |||
562 | source_string->length(); | |||
563 | MaybeHandle<String> new_source_string = factory()->NewConsString( | |||
564 | source_string, | |||
565 | factory()->NewSubString(full_source_, current_interval_start, | |||
566 | current_interval_end)); | |||
567 | if (!new_source_string.ToHandle(&source_string)) { | |||
568 | Throw("Cannot construct source string"); | |||
569 | return; | |||
570 | } | |||
571 | } | |||
572 | DiscoverString(source_string); | |||
573 | bool in_place = false; | |||
574 | source_id_ = GetStringId(source_string, in_place); | |||
575 | DCHECK(!in_place)((void) 0); | |||
576 | } | |||
577 | ||||
578 | void WebSnapshotSerializer::SerializeFunctionInfo(ValueSerializer* serializer, | |||
579 | Handle<JSFunction> function) { | |||
580 | if (!function->shared().HasSourceCode()) { | |||
581 | Throw("Function without source code"); | |||
582 | return; | |||
583 | } | |||
584 | ||||
585 | { | |||
586 | DisallowGarbageCollection no_gc; | |||
587 | Context context = function->context(); | |||
588 | if (context.IsNativeContext() || context.IsScriptContext()) { | |||
589 | serializer->WriteUint32(0); | |||
590 | } else { | |||
591 | DCHECK(context.IsFunctionContext() || context.IsBlockContext())((void) 0); | |||
592 | uint32_t context_id = GetContextId(context); | |||
593 | serializer->WriteUint32(context_id + 1); | |||
594 | } | |||
595 | } | |||
596 | ||||
597 | serializer->WriteUint32(source_id_); | |||
598 | int start = function->shared().StartPosition(); | |||
599 | int end = function->shared().EndPosition(); | |||
600 | serializer->WriteUint32(source_offset_to_compacted_source_offset_[start]); | |||
601 | serializer->WriteUint32(end - start); | |||
602 | ||||
603 | serializer->WriteUint32( | |||
604 | function->shared().internal_formal_parameter_count_without_receiver()); | |||
605 | serializer->WriteUint32( | |||
606 | FunctionKindToFunctionFlags(function->shared().kind())); | |||
607 | ||||
608 | if (function->has_prototype_slot() && function->has_instance_prototype()) { | |||
609 | DisallowGarbageCollection no_gc; | |||
610 | JSObject prototype = JSObject::cast(function->instance_prototype()); | |||
611 | uint32_t prototype_id = GetObjectId(prototype); | |||
612 | serializer->WriteUint32(prototype_id + 1); | |||
613 | } else { | |||
614 | serializer->WriteUint32(0); | |||
615 | } | |||
616 | } | |||
617 | ||||
618 | void WebSnapshotSerializer::ShallowDiscoverExternals(FixedArray externals) { | |||
619 | DisallowGarbageCollection no_gc; | |||
620 | for (int i = 0; i < externals.length(); i++) { | |||
621 | Object object = externals.get(i); | |||
622 | if (!object.IsHeapObject()) continue; | |||
623 | uint32_t unused_id = 0; | |||
624 | InsertIntoIndexMap(external_objects_ids_, HeapObject::cast(object), | |||
625 | unused_id); | |||
626 | } | |||
627 | } | |||
628 | ||||
629 | void WebSnapshotSerializer::Discover(Handle<HeapObject> start_object) { | |||
630 | // The object discovery phase assigns IDs for objects / functions / classes / | |||
631 | // arrays and discovers outgoing references from them. This is needed so that | |||
632 | // e.g., we know all functions upfront and can construct the source code that | |||
633 | // covers them before serializing the functions. | |||
634 | ||||
635 | discovery_queue_.push(start_object); | |||
636 | ||||
637 | while (!discovery_queue_.empty()) { | |||
638 | const Handle<HeapObject>& object = discovery_queue_.front(); | |||
639 | switch (object->map().instance_type()) { | |||
640 | case JS_FUNCTION_TYPE: | |||
641 | DiscoverFunction(Handle<JSFunction>::cast(object)); | |||
642 | break; | |||
643 | case JS_CLASS_CONSTRUCTOR_TYPE: | |||
644 | DiscoverClass(Handle<JSFunction>::cast(object)); | |||
645 | break; | |||
646 | case JS_OBJECT_TYPE: | |||
647 | DiscoverObject(Handle<JSObject>::cast(object)); | |||
648 | break; | |||
649 | case JS_ARRAY_TYPE: | |||
650 | DiscoverArray(Handle<JSArray>::cast(object)); | |||
651 | break; | |||
652 | case ODDBALL_TYPE: | |||
653 | case HEAP_NUMBER_TYPE: | |||
654 | // Can't contain references to other objects. | |||
655 | break; | |||
656 | case JS_PRIMITIVE_WRAPPER_TYPE: { | |||
657 | Handle<JSPrimitiveWrapper> wrapper = | |||
658 | Handle<JSPrimitiveWrapper>::cast(object); | |||
659 | Handle<Object> value = handle(wrapper->value(), isolate_); | |||
660 | if (value->IsHeapObject()) { | |||
661 | discovery_queue_.push(Handle<HeapObject>::cast(value)); | |||
662 | } | |||
663 | break; | |||
664 | } | |||
665 | case JS_REG_EXP_TYPE: { | |||
666 | Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object); | |||
667 | Handle<String> pattern = handle(regexp->source(), isolate_); | |||
668 | DiscoverString(pattern); | |||
669 | Handle<String> flags_string = | |||
670 | JSRegExp::StringFromFlags(isolate_, regexp->flags()); | |||
671 | DiscoverString(flags_string); | |||
672 | break; | |||
673 | } | |||
674 | default: | |||
675 | if (object->IsString()) { | |||
676 | // These are array elements / object properties -> allow in place | |||
677 | // strings. | |||
678 | DiscoverString(Handle<String>::cast(object), AllowInPlace::Yes); | |||
679 | break; | |||
680 | } else if (external_objects_ids_.size() > 0) { | |||
681 | int unused_id; | |||
682 | external_objects_ids_.LookupOrInsert(*object, &unused_id); | |||
683 | } else { | |||
684 | Throw("Unsupported object"); | |||
685 | } | |||
686 | } | |||
687 | discovery_queue_.pop(); | |||
688 | } | |||
689 | } | |||
690 | ||||
691 | void WebSnapshotSerializer::DiscoverMap(Handle<Map> map) { | |||
692 | uint32_t id; | |||
693 | if (InsertIntoIndexMap(map_ids_, *map, id)) { | |||
694 | return; | |||
695 | } | |||
696 | DCHECK_EQ(id, maps_->Length())((void) 0); | |||
697 | maps_ = ArrayList::Add(isolate_, maps_, map); | |||
698 | for (InternalIndex i : map->IterateOwnDescriptors()) { | |||
699 | Handle<Name> key(map->instance_descriptors(kRelaxedLoad).GetKey(i), | |||
700 | isolate_); | |||
701 | if (!key->IsString()) { | |||
702 | Throw("Key is not a string"); | |||
703 | return; | |||
704 | } | |||
705 | DiscoverString(Handle<String>::cast(key)); | |||
706 | } | |||
707 | } | |||
708 | ||||
709 | void WebSnapshotSerializer::DiscoverString(Handle<String> string, | |||
710 | AllowInPlace can_be_in_place) { | |||
711 | // Can't contain references to other objects. We only log the existence of the | |||
712 | // string itself. Internalize the strings so that we can properly track which | |||
713 | // String objects are the same string. | |||
714 | string = factory()->InternalizeString(string); | |||
715 | auto result = all_strings_.FindOrInsert(string); | |||
716 | if (can_be_in_place == AllowInPlace::Yes && !result.already_exists) { | |||
717 | // This is the only reference to the string so far. Don't generate and | |||
718 | // ID for it yet; only generate it when another reference to the string is | |||
719 | // found. | |||
720 | return; | |||
721 | } | |||
722 | // The string is referred to more than two places, or in-placing not allowed | |||
723 | // -> not a candidate for writing it in-place. Generate an ID for it. | |||
724 | ||||
725 | // TODO(v8:11525): Allow in-place strings in more places. Heuristics for | |||
726 | // when to make them in place? | |||
727 | uint32_t id; | |||
728 | if (InsertIntoIndexMap(string_ids_, *string, id)) { | |||
729 | return; | |||
730 | } | |||
731 | DCHECK_EQ(id, strings_->Length())((void) 0); | |||
732 | strings_ = ArrayList::Add(isolate_, strings_, string); | |||
733 | } | |||
734 | ||||
735 | void WebSnapshotSerializer::DiscoverFunction(Handle<JSFunction> function) { | |||
736 | uint32_t id; | |||
737 | if (InsertIntoIndexMap(function_ids_, *function, id)) { | |||
738 | return; | |||
739 | } | |||
740 | ||||
741 | DCHECK_EQ(id, functions_->Length())((void) 0); | |||
742 | functions_ = ArrayList::Add(isolate_, functions_, function); | |||
743 | DiscoverContextAndPrototype(function); | |||
744 | // TODO(v8:11525): Support properties in functions. | |||
745 | DiscoverSource(function); | |||
746 | } | |||
747 | ||||
748 | void WebSnapshotSerializer::DiscoverClass(Handle<JSFunction> function) { | |||
749 | uint32_t id; | |||
750 | if (InsertIntoIndexMap(class_ids_, *function, id)) { | |||
751 | return; | |||
752 | } | |||
753 | ||||
754 | DCHECK_EQ(id, classes_->Length())((void) 0); | |||
755 | classes_ = ArrayList::Add(isolate_, classes_, function); | |||
756 | ||||
757 | DiscoverContextAndPrototype(function); | |||
758 | // TODO(v8:11525): Support properties in classes. | |||
759 | // TODO(v8:11525): Support class members. | |||
760 | DiscoverSource(function); | |||
761 | } | |||
762 | ||||
763 | void WebSnapshotSerializer::DiscoverContextAndPrototype( | |||
764 | Handle<JSFunction> function) { | |||
765 | Handle<Context> context(function->context(), isolate_); | |||
766 | if (context->IsFunctionContext() || context->IsBlockContext()) { | |||
767 | DiscoverContext(context); | |||
768 | } | |||
769 | ||||
770 | if (function->has_prototype_slot() && | |||
771 | function->map().has_non_instance_prototype()) { | |||
772 | Throw("Functions with non-instance prototypes not supported"); | |||
773 | return; | |||
774 | } | |||
775 | ||||
776 | if (function->has_prototype_slot() && function->has_instance_prototype()) { | |||
777 | Handle<JSObject> prototype = Handle<JSObject>::cast( | |||
778 | handle(function->instance_prototype(), isolate_)); | |||
779 | discovery_queue_.push(prototype); | |||
780 | } | |||
781 | } | |||
782 | ||||
783 | void WebSnapshotSerializer::DiscoverContext(Handle<Context> context) { | |||
784 | uint32_t id; | |||
785 | if (InsertIntoIndexMap(context_ids_, *context, id)) return; | |||
786 | ||||
787 | DCHECK_EQ(id, contexts_->Length())((void) 0); | |||
788 | contexts_ = ArrayList::Add(isolate_, contexts_, context); | |||
789 | ||||
790 | Handle<ScopeInfo> scope_info = handle(context->scope_info(), isolate_); | |||
791 | int count = scope_info->ContextLocalCount(); | |||
792 | ||||
793 | for (int i = 0; i < count; ++i) { | |||
794 | // TODO(v8:11525): support parameters | |||
795 | // TODO(v8:11525): distinguish variable modes | |||
796 | Handle<String> name(scope_info->context_local_names(i), isolate_); | |||
797 | DiscoverString(name); | |||
798 | Object value = context->get(scope_info->ContextHeaderLength() + i); | |||
799 | if (!value.IsHeapObject()) continue; | |||
800 | discovery_queue_.push(handle(HeapObject::cast(value), isolate_)); | |||
801 | } | |||
802 | ||||
803 | if (!context->previous().IsNativeContext() && | |||
804 | !context->previous().IsScriptContext()) { | |||
805 | DiscoverContext(handle(context->previous(), isolate_)); | |||
806 | } | |||
807 | } | |||
808 | ||||
809 | void WebSnapshotSerializer::DiscoverSource(Handle<JSFunction> function) { | |||
810 | source_intervals_.emplace(function->shared().StartPosition(), | |||
811 | function->shared().EndPosition()); | |||
812 | Handle<String> function_script_source = | |||
813 | handle(String::cast(Script::cast(function->shared().script()).source()), | |||
814 | isolate_); | |||
815 | if (full_source_.is_null()) { | |||
816 | full_source_ = function_script_source; | |||
817 | } else if (!full_source_->Equals(*function_script_source)) { | |||
818 | Throw("Cannot include functions from multiple scripts"); | |||
819 | } | |||
820 | } | |||
821 | ||||
822 | void WebSnapshotSerializer::DiscoverArray(Handle<JSArray> array) { | |||
823 | uint32_t id; | |||
824 | if (InsertIntoIndexMap(array_ids_, *array, id)) { | |||
825 | return; | |||
826 | } | |||
827 | DCHECK_EQ(id, arrays_->Length())((void) 0); | |||
828 | arrays_ = ArrayList::Add(isolate_, arrays_, array); | |||
829 | ||||
830 | auto elements_kind = array->GetElementsKind(); | |||
831 | if (elements_kind != PACKED_SMI_ELEMENTS && | |||
832 | elements_kind != PACKED_ELEMENTS) { | |||
833 | Throw("Unsupported array"); | |||
834 | return; | |||
835 | } | |||
836 | // TODO(v8:11525): Support sparse arrays & arrays with holes. | |||
837 | DisallowGarbageCollection no_gc; | |||
838 | FixedArray elements = FixedArray::cast(array->elements()); | |||
839 | for (int i = 0; i < elements.length(); ++i) { | |||
840 | Object object = elements.get(i); | |||
841 | if (!object.IsHeapObject()) continue; | |||
842 | discovery_queue_.push(handle(HeapObject::cast(object), isolate_)); | |||
843 | } | |||
844 | } | |||
845 | ||||
846 | void WebSnapshotSerializer::DiscoverObject(Handle<JSObject> object) { | |||
847 | uint32_t id; | |||
848 | if (InsertIntoIndexMap(object_ids_, *object, id)) return; | |||
849 | ||||
850 | DCHECK_EQ(id, objects_->Length())((void) 0); | |||
851 | objects_ = ArrayList::Add(isolate_, objects_, object); | |||
852 | ||||
853 | // TODO(v8:11525): Support objects with so many properties that they can't be | |||
854 | // in fast mode. | |||
855 | JSObject::MigrateSlowToFast(object, 0, "Web snapshot"); | |||
856 | if (!object->HasFastProperties()) { | |||
857 | Throw("Dictionary mode objects not supported"); | |||
858 | } | |||
859 | ||||
860 | Handle<Map> map(object->map(), isolate_); | |||
861 | DiscoverMap(map); | |||
862 | ||||
863 | // Discover __proto__. | |||
864 | if (map->prototype() != | |||
865 | isolate_->native_context()->initial_object_prototype()) { | |||
866 | discovery_queue_.push(handle(map->prototype(), isolate_)); | |||
867 | } | |||
868 | ||||
869 | // Discover property values. | |||
870 | for (InternalIndex i : map->IterateOwnDescriptors()) { | |||
871 | PropertyDetails details = | |||
872 | map->instance_descriptors(kRelaxedLoad).GetDetails(i); | |||
873 | FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); | |||
874 | Handle<Object> value = JSObject::FastPropertyAt( | |||
875 | isolate_, object, details.representation(), field_index); | |||
876 | if (!value->IsHeapObject()) continue; | |||
877 | discovery_queue_.push(Handle<HeapObject>::cast(value)); | |||
878 | } | |||
879 | ||||
880 | // Discover elements. | |||
881 | Handle<FixedArray> elements = | |||
882 | handle(FixedArray::cast(object->elements()), isolate_); | |||
883 | for (int i = 0; i < elements->length(); ++i) { | |||
884 | Object object = elements->get(i); | |||
885 | if (!object.IsHeapObject()) continue; | |||
886 | discovery_queue_.push(handle(HeapObject::cast(object), isolate_)); | |||
887 | } | |||
888 | } | |||
889 | ||||
890 | // Format (serialized function): | |||
891 | // - 0 if there's no context, 1 + context id otherwise | |||
892 | // - String id (source snippet) | |||
893 | // - Start position in the source snippet | |||
894 | // - Length in the source snippet | |||
895 | // - Formal parameter count | |||
896 | // - Flags (see FunctionFlags) | |||
897 | // - 0 if there's no function prototype, 1 + object id for the function | |||
898 | // prototype otherwise | |||
899 | // TODO(v8:11525): Investigate whether the length is really needed. | |||
900 | void WebSnapshotSerializer::SerializeFunction(Handle<JSFunction> function) { | |||
901 | SerializeFunctionInfo(&function_serializer_, function); | |||
902 | // TODO(v8:11525): Support properties in functions. | |||
903 | } | |||
904 | ||||
905 | // Format (serialized class): | |||
906 | // - 1 + context id | |||
907 | // - String id (source snippet) | |||
908 | // - Start position in the source snippet | |||
909 | // - Length in the source snippet | |||
910 | // - Formal parameter count | |||
911 | // - Flags (see FunctionFlags) | |||
912 | // - 1 + object id for the function prototype | |||
913 | void WebSnapshotSerializer::SerializeClass(Handle<JSFunction> function) { | |||
914 | SerializeFunctionInfo(&class_serializer_, function); | |||
915 | // TODO(v8:11525): Support properties in classes. | |||
916 | // TODO(v8:11525): Support class members. | |||
917 | } | |||
918 | ||||
919 | // Format (serialized context): | |||
920 | // - 0 if there's no parent context, 1 + parent context id otherwise | |||
921 | // - Variable count | |||
922 | // - For each variable: | |||
923 | // - String id (name) | |||
924 | // - Serialized value | |||
925 | void WebSnapshotSerializer::SerializeContext(Handle<Context> context) { | |||
926 | uint32_t parent_context_id = 0; | |||
927 | if (!context->previous().IsNativeContext() && | |||
928 | !context->previous().IsScriptContext()) { | |||
929 | parent_context_id = GetContextId(context->previous()) + 1; | |||
930 | } | |||
931 | ||||
932 | // TODO(v8:11525): Use less space for encoding the context type. | |||
933 | if (context->IsFunctionContext()) { | |||
934 | context_serializer_.WriteUint32(ContextType::FUNCTION); | |||
935 | } else if (context->IsBlockContext()) { | |||
936 | context_serializer_.WriteUint32(ContextType::BLOCK); | |||
937 | } else { | |||
938 | Throw("Unsupported context type"); | |||
939 | return; | |||
940 | } | |||
941 | ||||
942 | context_serializer_.WriteUint32(parent_context_id); | |||
943 | ||||
944 | Handle<ScopeInfo> scope_info(context->scope_info(), isolate_); | |||
945 | int count = scope_info->ContextLocalCount(); | |||
946 | context_serializer_.WriteUint32(count); | |||
947 | ||||
948 | for (int i = 0; i < count; ++i) { | |||
949 | // TODO(v8:11525): support parameters | |||
950 | // TODO(v8:11525): distinguish variable modes | |||
951 | Handle<String> name(scope_info->context_local_names(i), isolate_); | |||
952 | WriteStringId(name, context_serializer_); | |||
953 | Handle<Object> value(context->get(scope_info->ContextHeaderLength() + i), | |||
954 | isolate_); | |||
955 | WriteValue(value, context_serializer_); | |||
956 | } | |||
957 | } | |||
958 | ||||
959 | // Format (serialized object): | |||
960 | // - Shape id | |||
961 | // - For each property: | |||
962 | // - Serialized value | |||
963 | // - Max element index + 1 (or 0 if there are no elements) | |||
964 | // - For each element: | |||
965 | // - Index | |||
966 | // - Serialized value | |||
967 | // TODO(v8:11525): Support packed elements with a denser format. | |||
968 | void WebSnapshotSerializer::SerializeObject(Handle<JSObject> object) { | |||
969 | Handle<Map> map(object->map(), isolate_); | |||
970 | uint32_t map_id = GetMapId(*map); | |||
971 | object_serializer_.WriteUint32(map_id); | |||
972 | ||||
973 | // Properties. | |||
974 | for (InternalIndex i : map->IterateOwnDescriptors()) { | |||
975 | PropertyDetails details = | |||
976 | map->instance_descriptors(kRelaxedLoad).GetDetails(i); | |||
977 | FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); | |||
978 | Handle<Object> value = JSObject::FastPropertyAt( | |||
979 | isolate_, object, details.representation(), field_index); | |||
980 | WriteValue(value, object_serializer_); | |||
981 | } | |||
982 | ||||
983 | // Elements. | |||
984 | ReadOnlyRoots roots(isolate_); | |||
985 | Handle<FixedArray> elements = | |||
986 | handle(FixedArray::cast(object->elements()), isolate_); | |||
987 | uint32_t max_element_index = 0; | |||
988 | for (int i = 0; i < elements->length(); ++i) { | |||
989 | DisallowGarbageCollection no_gc; | |||
990 | Object value = elements->get(i); | |||
991 | if (value != roots.the_hole_value()) { | |||
992 | if (i > static_cast<int>(max_element_index)) { | |||
993 | max_element_index = i; | |||
994 | } | |||
995 | } | |||
996 | } | |||
997 | if (max_element_index == 0) { | |||
998 | object_serializer_.WriteUint32(0); | |||
999 | } else { | |||
1000 | object_serializer_.WriteUint32(max_element_index + 1); | |||
1001 | } | |||
1002 | for (int i = 0; i < elements->length(); ++i) { | |||
1003 | Handle<Object> value = handle(elements->get(i), isolate_); | |||
1004 | if (*value != roots.the_hole_value()) { | |||
1005 | DCHECK_LE(i, max_element_index)((void) 0); | |||
1006 | object_serializer_.WriteUint32(i); | |||
1007 | WriteValue(value, object_serializer_); | |||
1008 | } | |||
1009 | } | |||
1010 | } | |||
1011 | ||||
1012 | // Format (serialized array): | |||
1013 | // - Length | |||
1014 | // - For each element: | |||
1015 | // - Serialized value | |||
1016 | void WebSnapshotSerializer::SerializeArray(Handle<JSArray> array) { | |||
1017 | auto elements_kind = array->GetElementsKind(); | |||
1018 | if (elements_kind != PACKED_SMI_ELEMENTS && | |||
1019 | elements_kind != PACKED_ELEMENTS) { | |||
1020 | Throw("Unsupported array"); | |||
1021 | return; | |||
1022 | } | |||
1023 | // TODO(v8:11525): Support sparse arrays & arrays with holes. | |||
1024 | uint32_t length = static_cast<uint32_t>(array->length().ToSmi().value()); | |||
1025 | array_serializer_.WriteUint32(length); | |||
1026 | Handle<FixedArray> elements = | |||
1027 | handle(FixedArray::cast(array->elements()), isolate_); | |||
1028 | for (uint32_t i = 0; i < length; ++i) { | |||
1029 | WriteValue(handle(elements->get(i), isolate_), array_serializer_); | |||
1030 | } | |||
1031 | } | |||
1032 | ||||
1033 | // Format (serialized export): | |||
1034 | // - String id (export name) | |||
1035 | // - Serialized value (export value) | |||
1036 | void WebSnapshotSerializer::SerializeExport(Handle<Object> object, | |||
1037 | Handle<String> export_name) { | |||
1038 | ++export_count_; | |||
1039 | WriteStringId(export_name, export_serializer_); | |||
1040 | if (object->IsJSPrimitiveWrapper()) { | |||
1041 | Handle<JSPrimitiveWrapper> wrapper = | |||
1042 | Handle<JSPrimitiveWrapper>::cast(object); | |||
1043 | Handle<Object> export_value = handle(wrapper->value(), isolate_); | |||
1044 | WriteValue(export_value, export_serializer_); | |||
1045 | } else { | |||
1046 | WriteValue(object, export_serializer_); | |||
1047 | } | |||
1048 | } | |||
1049 | ||||
1050 | // Format (serialized value): | |||
1051 | // - Type id (ValueType enum) | |||
1052 | // - Value or id (interpretation depends on the type) | |||
1053 | void WebSnapshotSerializer::WriteValue(Handle<Object> object, | |||
1054 | ValueSerializer& serializer) { | |||
1055 | if (object->IsSmi()) { | |||
1056 | serializer.WriteUint32(ValueType::INTEGER); | |||
1057 | serializer.WriteZigZag<int32_t>(Smi::cast(*object).value()); | |||
1058 | return; | |||
1059 | } | |||
1060 | ||||
1061 | int external_id; | |||
1062 | if (external_objects_ids_.Lookup(HeapObject::cast(*object), &external_id)) { | |||
1063 | serializer.WriteUint32(ValueType::EXTERNAL_ID); | |||
1064 | serializer.WriteUint32(static_cast<uint32_t>(external_id)); | |||
1065 | return; | |||
1066 | } | |||
1067 | ||||
1068 | DCHECK(object->IsHeapObject())((void) 0); | |||
1069 | Handle<HeapObject> heap_object = Handle<HeapObject>::cast(object); | |||
1070 | switch ((*heap_object).map().instance_type()) { | |||
1071 | case ODDBALL_TYPE: | |||
1072 | switch (Oddball::cast(*heap_object).kind()) { | |||
1073 | case Oddball::kFalse: | |||
1074 | serializer.WriteUint32(ValueType::FALSE_CONSTANT); | |||
1075 | return; | |||
1076 | case Oddball::kTrue: | |||
1077 | serializer.WriteUint32(ValueType::TRUE_CONSTANT); | |||
1078 | return; | |||
1079 | case Oddball::kNull: | |||
1080 | serializer.WriteUint32(ValueType::NULL_CONSTANT); | |||
1081 | return; | |||
1082 | case Oddball::kUndefined: | |||
1083 | serializer.WriteUint32(ValueType::UNDEFINED_CONSTANT); | |||
1084 | return; | |||
1085 | default: | |||
1086 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
1087 | } | |||
1088 | case HEAP_NUMBER_TYPE: | |||
1089 | // TODO(v8:11525): Handle possible endianness mismatch. | |||
1090 | serializer.WriteUint32(ValueType::DOUBLE); | |||
1091 | serializer.WriteDouble(HeapNumber::cast(*heap_object).value()); | |||
1092 | break; | |||
1093 | case JS_FUNCTION_TYPE: | |||
1094 | serializer.WriteUint32(ValueType::FUNCTION_ID); | |||
1095 | serializer.WriteUint32(GetFunctionId(JSFunction::cast(*heap_object))); | |||
1096 | break; | |||
1097 | case JS_CLASS_CONSTRUCTOR_TYPE: | |||
1098 | serializer.WriteUint32(ValueType::CLASS_ID); | |||
1099 | serializer.WriteUint32(GetClassId(JSFunction::cast(*heap_object))); | |||
1100 | break; | |||
1101 | case JS_OBJECT_TYPE: | |||
1102 | serializer.WriteUint32(ValueType::OBJECT_ID); | |||
1103 | serializer.WriteUint32(GetObjectId(JSObject::cast(*heap_object))); | |||
1104 | break; | |||
1105 | case JS_ARRAY_TYPE: | |||
1106 | serializer.WriteUint32(ValueType::ARRAY_ID); | |||
1107 | serializer.WriteUint32(GetArrayId(JSArray::cast(*heap_object))); | |||
1108 | break; | |||
1109 | case JS_REG_EXP_TYPE: { | |||
1110 | Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(heap_object); | |||
1111 | if (regexp->map() != isolate_->regexp_function()->initial_map()) { | |||
1112 | Throw("Unsupported RegExp map"); | |||
1113 | return; | |||
1114 | } | |||
1115 | serializer.WriteUint32(ValueType::REGEXP); | |||
1116 | Handle<String> pattern = handle(regexp->source(), isolate_); | |||
1117 | WriteStringId(pattern, serializer); | |||
1118 | Handle<String> flags_string = | |||
1119 | JSRegExp::StringFromFlags(isolate_, regexp->flags()); | |||
1120 | WriteStringId(flags_string, serializer); | |||
1121 | break; | |||
1122 | } | |||
1123 | default: | |||
1124 | if (heap_object->IsString()) { | |||
1125 | // Write strings which are referred to only once as in-place strings. | |||
1126 | WriteStringMaybeInPlace(Handle<String>::cast(heap_object), serializer); | |||
1127 | } else { | |||
1128 | Throw("Unsupported object"); | |||
1129 | } | |||
1130 | } | |||
1131 | // TODO(v8:11525): Support more types. | |||
1132 | } | |||
1133 | ||||
1134 | void WebSnapshotSerializer::WriteStringMaybeInPlace( | |||
1135 | Handle<String> string, ValueSerializer& serializer) { | |||
1136 | // If the string is only referred to by one location, write it in-place. | |||
1137 | bool in_place = false; | |||
1138 | uint32_t id = GetStringId(string, in_place); | |||
1139 | if (in_place) { | |||
1140 | serializer.WriteUint32(ValueType::IN_PLACE_STRING_ID); | |||
1141 | SerializeString(string, serializer); | |||
1142 | } else { | |||
1143 | serializer.WriteUint32(ValueType::STRING_ID); | |||
1144 | serializer.WriteUint32(id); | |||
1145 | } | |||
1146 | } | |||
1147 | ||||
1148 | void WebSnapshotSerializer::WriteStringId(Handle<String> string, | |||
1149 | ValueSerializer& serializer) { | |||
1150 | bool in_place = false; | |||
1151 | uint32_t id = GetStringId(string, in_place); | |||
1152 | CHECK(!in_place)do { if ((__builtin_expect(!!(!(!in_place)), 0))) { V8_Fatal( "Check failed: %s.", "!in_place"); } } while (false); // The string must have an ID. | |||
1153 | serializer.WriteUint32(id); | |||
1154 | } | |||
1155 | ||||
1156 | uint32_t WebSnapshotSerializer::GetStringId(Handle<String> string, | |||
1157 | bool& in_place) { | |||
1158 | // Internalize strings so that they're unique. | |||
1159 | string = factory()->InternalizeString(string); | |||
1160 | ||||
1161 | // Strings referred to more than one places are inserted in string_ids_. | |||
1162 | // Strings referred to by only one place aren't. | |||
1163 | #ifdef DEBUG | |||
1164 | auto result = all_strings_.FindOrInsert(string); | |||
1165 | DCHECK(result.already_exists)((void) 0); | |||
1166 | #endif | |||
1167 | int id = 0; | |||
1168 | in_place = !string_ids_.Lookup(*string, &id); | |||
1169 | return static_cast<uint32_t>(id); | |||
1170 | } | |||
1171 | ||||
1172 | uint32_t WebSnapshotSerializer::GetMapId(Map map) { | |||
1173 | int id; | |||
1174 | bool return_value = map_ids_.Lookup(map, &id); | |||
1175 | DCHECK(return_value)((void) 0); | |||
1176 | USE(return_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{return_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
1177 | return static_cast<uint32_t>(id); | |||
1178 | } | |||
1179 | ||||
1180 | uint32_t WebSnapshotSerializer::GetFunctionId(JSFunction function) { | |||
1181 | int id; | |||
1182 | bool return_value = function_ids_.Lookup(function, &id); | |||
1183 | DCHECK(return_value)((void) 0); | |||
1184 | USE(return_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{return_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
1185 | return static_cast<uint32_t>(function_ids_.size() - 1 - id); | |||
1186 | } | |||
1187 | ||||
1188 | uint32_t WebSnapshotSerializer::GetClassId(JSFunction function) { | |||
1189 | int id; | |||
1190 | bool return_value = class_ids_.Lookup(function, &id); | |||
1191 | DCHECK(return_value)((void) 0); | |||
1192 | USE(return_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{return_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
1193 | return static_cast<uint32_t>(class_ids_.size() - 1 - id); | |||
1194 | } | |||
1195 | ||||
1196 | uint32_t WebSnapshotSerializer::GetContextId(Context context) { | |||
1197 | int id; | |||
1198 | bool return_value = context_ids_.Lookup(context, &id); | |||
1199 | DCHECK(return_value)((void) 0); | |||
1200 | USE(return_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{return_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
1201 | return static_cast<uint32_t>(context_ids_.size() - 1 - id); | |||
1202 | } | |||
1203 | ||||
1204 | uint32_t WebSnapshotSerializer::GetArrayId(JSArray array) { | |||
1205 | int id; | |||
1206 | bool return_value = array_ids_.Lookup(array, &id); | |||
1207 | DCHECK(return_value)((void) 0); | |||
1208 | USE(return_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{return_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
1209 | return static_cast<uint32_t>(array_ids_.size() - 1 - id); | |||
1210 | } | |||
1211 | ||||
1212 | uint32_t WebSnapshotSerializer::GetObjectId(JSObject object) { | |||
1213 | int id; | |||
1214 | bool return_value = object_ids_.Lookup(object, &id); | |||
1215 | DCHECK(return_value)((void) 0); | |||
1216 | USE(return_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{return_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
1217 | return static_cast<uint32_t>(object_ids_.size() - 1 - id); | |||
1218 | } | |||
1219 | ||||
1220 | uint32_t WebSnapshotSerializer::GetExternalId(HeapObject object) { | |||
1221 | int id; | |||
| ||||
1222 | bool return_value = external_objects_ids_.Lookup(object, &id); | |||
1223 | DCHECK(return_value)((void) 0); | |||
1224 | USE(return_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{return_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
1225 | return static_cast<uint32_t>(id); | |||
| ||||
1226 | } | |||
1227 | ||||
1228 | Handle<FixedArray> WebSnapshotSerializer::GetExternals() { | |||
1229 | return external_objects_ids_.Values(isolate_); | |||
1230 | } | |||
1231 | ||||
1232 | WebSnapshotDeserializer::WebSnapshotDeserializer(v8::Isolate* isolate, | |||
1233 | const uint8_t* data, | |||
1234 | size_t buffer_size) | |||
1235 | : WebSnapshotDeserializer(reinterpret_cast<i::Isolate*>(isolate), | |||
1236 | Handle<Object>(), {data, buffer_size}) {} | |||
1237 | ||||
1238 | WebSnapshotDeserializer::WebSnapshotDeserializer( | |||
1239 | Isolate* isolate, Handle<Script> snapshot_as_script) | |||
1240 | : WebSnapshotDeserializer( | |||
1241 | isolate, handle(snapshot_as_script->name(), isolate), | |||
1242 | ExtractScriptBuffer(isolate, snapshot_as_script)) {} | |||
1243 | ||||
1244 | WebSnapshotDeserializer::WebSnapshotDeserializer( | |||
1245 | Isolate* isolate, Handle<Object> script_name, | |||
1246 | base::Vector<const uint8_t> buffer) | |||
1247 | : WebSnapshotSerializerDeserializer(isolate), | |||
1248 | script_name_(script_name), | |||
1249 | deserializer_(isolate_, buffer.data(), buffer.length()), | |||
1250 | roots_(isolate) { | |||
1251 | Handle<FixedArray> empty_array = factory()->empty_fixed_array(); | |||
1252 | strings_handle_ = empty_array; | |||
1253 | maps_handle_ = empty_array; | |||
1254 | contexts_handle_ = empty_array; | |||
1255 | functions_handle_ = empty_array; | |||
1256 | classes_handle_ = empty_array; | |||
1257 | arrays_handle_ = empty_array; | |||
1258 | objects_handle_ = empty_array; | |||
1259 | external_references_handle_ = empty_array; | |||
1260 | isolate_->heap()->AddGCEpilogueCallback(UpdatePointersCallback, | |||
1261 | v8::kGCTypeAll, this); | |||
1262 | } | |||
1263 | ||||
1264 | WebSnapshotDeserializer::~WebSnapshotDeserializer() { | |||
1265 | isolate_->heap()->RemoveGCEpilogueCallback(UpdatePointersCallback, this); | |||
1266 | } | |||
1267 | ||||
1268 | void WebSnapshotDeserializer::UpdatePointers() { | |||
1269 | strings_ = *strings_handle_; | |||
1270 | maps_ = *maps_handle_; | |||
1271 | contexts_ = *contexts_handle_; | |||
1272 | functions_ = *functions_handle_; | |||
1273 | classes_ = *classes_handle_; | |||
1274 | arrays_ = *arrays_handle_; | |||
1275 | objects_ = *objects_handle_; | |||
1276 | external_references_ = *external_references_handle_; | |||
1277 | } | |||
1278 | ||||
1279 | // static | |||
1280 | base::Vector<const uint8_t> WebSnapshotDeserializer::ExtractScriptBuffer( | |||
1281 | Isolate* isolate, Handle<Script> snapshot_as_script) { | |||
1282 | Handle<String> source = | |||
1283 | handle(String::cast(snapshot_as_script->source()), isolate); | |||
1284 | if (source->IsExternalOneByteString()) { | |||
1285 | const v8::String::ExternalOneByteStringResource* resource = | |||
1286 | ExternalOneByteString::cast(*source).resource(); | |||
1287 | return {reinterpret_cast<const uint8_t*>(resource->data()), | |||
1288 | resource->length()}; | |||
1289 | } else if (source->IsSeqOneByteString()) { | |||
1290 | SeqOneByteString source_as_seq = SeqOneByteString::cast(*source); | |||
1291 | size_t length = source_as_seq.length(); | |||
1292 | std::unique_ptr<uint8_t[]> data_copy(new uint8_t[length]); | |||
1293 | { | |||
1294 | DisallowGarbageCollection no_gc; | |||
1295 | uint8_t* data = source_as_seq.GetChars(no_gc); | |||
1296 | memcpy(data_copy.get(), data, length); | |||
1297 | } | |||
1298 | return {data_copy.get(), length}; | |||
1299 | } else if (source->IsExternalTwoByteString()) { | |||
1300 | // TODO(v8:11525): Implement end-to-end snapshot processing which gets rid | |||
1301 | // of the need to copy the data here. | |||
1302 | const v8::String::ExternalStringResource* resource = | |||
1303 | ExternalTwoByteString::cast(*source).resource(); | |||
1304 | size_t length = resource->length(); | |||
1305 | std::unique_ptr<uint8_t[]> data_copy(new uint8_t[length]); | |||
1306 | { | |||
1307 | DisallowGarbageCollection no_gc; | |||
1308 | const uint16_t* data = resource->data(); | |||
1309 | uint8_t* data_copy_ptr = data_copy.get(); | |||
1310 | for (size_t i = 0; i < length; ++i) { | |||
1311 | data_copy_ptr[i] = static_cast<uint8_t>(data[i]); | |||
1312 | } | |||
1313 | } | |||
1314 | return {data_copy.get(), length}; | |||
1315 | } else if (source->IsSeqTwoByteString()) { | |||
1316 | SeqTwoByteString source_as_seq = SeqTwoByteString::cast(*source); | |||
1317 | size_t length = source_as_seq.length(); | |||
1318 | std::unique_ptr<uint8_t[]> data_copy(new uint8_t[length]); | |||
1319 | { | |||
1320 | DisallowGarbageCollection no_gc; | |||
1321 | uint16_t* data = source_as_seq.GetChars(no_gc); | |||
1322 | uint8_t* data_copy_ptr = data_copy.get(); | |||
1323 | for (size_t i = 0; i < length; ++i) { | |||
1324 | data_copy_ptr[i] = static_cast<uint8_t>(data[i]); | |||
1325 | } | |||
1326 | } | |||
1327 | return {data_copy.get(), length}; | |||
1328 | } | |||
1329 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
1330 | } | |||
1331 | ||||
1332 | void WebSnapshotDeserializer::Throw(const char* message) { | |||
1333 | string_count_ = 0; | |||
1334 | map_count_ = 0; | |||
1335 | context_count_ = 0; | |||
1336 | class_count_ = 0; | |||
1337 | function_count_ = 0; | |||
1338 | object_count_ = 0; | |||
1339 | deferred_references_->SetLength(0); | |||
1340 | ||||
1341 | // Make sure we don't read any more data | |||
1342 | deserializer_.position_ = deserializer_.end_; | |||
1343 | ||||
1344 | WebSnapshotSerializerDeserializer::Throw(message); | |||
1345 | } | |||
1346 | ||||
1347 | bool WebSnapshotDeserializer::Deserialize( | |||
1348 | MaybeHandle<FixedArray> external_references, bool skip_exports) { | |||
1349 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize); | |||
1350 | if (external_references.ToHandle(&external_references_handle_)) { | |||
1351 | external_references_ = *external_references_handle_; | |||
1352 | } else { | |||
1353 | external_references_handle_ = roots_.empty_fixed_array_handle(); | |||
1354 | } | |||
1355 | ||||
1356 | if (deserialized_) { | |||
1357 | Throw("Can't reuse WebSnapshotDeserializer"); | |||
1358 | return false; | |||
1359 | } | |||
1360 | deserialized_ = true; | |||
1361 | auto buffer_size = deserializer_.end_ - deserializer_.position_; | |||
1362 | ||||
1363 | base::ElapsedTimer timer; | |||
1364 | if (FLAG_trace_web_snapshot) { | |||
1365 | timer.Start(); | |||
1366 | } | |||
1367 | if (!DeserializeSnapshot(skip_exports)) { | |||
1368 | return false; | |||
1369 | } | |||
1370 | if (!DeserializeScript()) { | |||
1371 | return false; | |||
1372 | } | |||
1373 | ||||
1374 | if (FLAG_trace_web_snapshot) { | |||
1375 | double ms = timer.Elapsed().InMillisecondsF(); | |||
1376 | PrintF("[Deserializing snapshot (%zu bytes) took %0.3f ms]\n", buffer_size, | |||
1377 | ms); | |||
1378 | } | |||
1379 | return true; | |||
1380 | } | |||
1381 | ||||
1382 | bool WebSnapshotDeserializer::DeserializeSnapshot(bool skip_exports) { | |||
1383 | deferred_references_ = ArrayList::New(isolate_, 30); | |||
1384 | ||||
1385 | const void* magic_bytes; | |||
1386 | if (!deserializer_.ReadRawBytes(sizeof(kMagicNumber), &magic_bytes) || | |||
1387 | memcmp(magic_bytes, kMagicNumber, sizeof(kMagicNumber)) != 0) { | |||
1388 | Throw("Invalid magic number"); | |||
1389 | return false; | |||
1390 | } | |||
1391 | ||||
1392 | DeserializeStrings(); | |||
1393 | DeserializeMaps(); | |||
1394 | DeserializeContexts(); | |||
1395 | DeserializeFunctions(); | |||
1396 | DeserializeArrays(); | |||
1397 | DeserializeObjects(); | |||
1398 | DeserializeClasses(); | |||
1399 | ProcessDeferredReferences(); | |||
1400 | DeserializeExports(skip_exports); | |||
1401 | DCHECK_EQ(0, deferred_references_->Length())((void) 0); | |||
1402 | ||||
1403 | return !has_error(); | |||
1404 | } | |||
1405 | ||||
1406 | bool WebSnapshotDeserializer::DeserializeScript() { | |||
1407 | // If there is more data, treat it as normal JavaScript. | |||
1408 | DCHECK_LE(deserializer_.position_, deserializer_.end_)((void) 0); | |||
1409 | auto remaining_bytes = deserializer_.end_ - deserializer_.position_; | |||
1410 | if (remaining_bytes > 0 && remaining_bytes < v8::String::kMaxLength) { | |||
1411 | v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_); | |||
1412 | v8::Local<v8::String> source = | |||
1413 | v8::String::NewFromUtf8( | |||
1414 | v8_isolate, reinterpret_cast<const char*>(deserializer_.position_), | |||
1415 | NewStringType::kNormal, static_cast<int>(remaining_bytes)) | |||
1416 | .ToLocalChecked(); | |||
1417 | ||||
1418 | ScriptOrigin origin(v8_isolate, Utils::ToLocal(script_name_)); | |||
1419 | ||||
1420 | ScriptCompiler::Source script_source(source, origin); | |||
1421 | Local<UnboundScript> script; | |||
1422 | if (!ScriptCompiler::CompileUnboundScript(v8_isolate, &script_source) | |||
1423 | .ToLocal(&script)) { | |||
1424 | // The exception has already been reported. | |||
1425 | DCHECK(!isolate_->has_pending_exception())((void) 0); | |||
1426 | return false; | |||
1427 | } | |||
1428 | Local<Value> result; | |||
1429 | if (!script->BindToCurrentContext() | |||
1430 | ->Run(v8_isolate->GetCurrentContext()) | |||
1431 | .ToLocal(&result)) { | |||
1432 | // The exception has already been reported. | |||
1433 | DCHECK(!isolate_->has_pending_exception())((void) 0); | |||
1434 | return false; | |||
1435 | } | |||
1436 | } | |||
1437 | ||||
1438 | // TODO(v8:11525): Add verification mode; verify the objects we just produced. | |||
1439 | return !has_error(); | |||
1440 | } | |||
1441 | ||||
1442 | void WebSnapshotDeserializer::DeserializeStrings() { | |||
1443 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Strings); | |||
1444 | if (!deserializer_.ReadUint32(&string_count_) || | |||
1445 | string_count_ > kMaxItemCount) { | |||
1446 | Throw("Malformed string table"); | |||
1447 | return; | |||
1448 | } | |||
1449 | STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength)static_assert(kMaxItemCount <= FixedArray::kMaxLength, "kMaxItemCount <= FixedArray::kMaxLength" ); | |||
1450 | strings_handle_ = factory()->NewFixedArray(string_count_); | |||
1451 | strings_ = *strings_handle_; | |||
1452 | for (uint32_t i = 0; i < string_count_; ++i) { | |||
1453 | MaybeHandle<String> maybe_string = | |||
1454 | deserializer_.ReadUtf8String(AllocationType::kOld); | |||
1455 | Handle<String> string; | |||
1456 | if (!maybe_string.ToHandle(&string)) { | |||
1457 | Throw("Malformed string"); | |||
1458 | return; | |||
1459 | } | |||
1460 | strings_.set(i, *string); | |||
1461 | } | |||
1462 | } | |||
1463 | ||||
1464 | String WebSnapshotDeserializer::ReadString(bool internalize) { | |||
1465 | DCHECK(!strings_handle_->is_null())((void) 0); | |||
1466 | uint32_t string_id; | |||
1467 | if (!deserializer_.ReadUint32(&string_id) || string_id >= string_count_) { | |||
1468 | Throw("malformed string id\n"); | |||
1469 | return roots_.empty_string(); | |||
1470 | } | |||
1471 | String string = String::cast(strings_.get(string_id)); | |||
1472 | if (internalize && !string.IsInternalizedString(isolate_)) { | |||
1473 | string = *factory()->InternalizeString(handle(string, isolate_)); | |||
1474 | strings_.set(string_id, string); | |||
1475 | } | |||
1476 | return string; | |||
1477 | } | |||
1478 | ||||
1479 | String WebSnapshotDeserializer::ReadInPlaceString(bool internalize) { | |||
1480 | MaybeHandle<String> maybe_string = | |||
1481 | deserializer_.ReadUtf8String(AllocationType::kOld); | |||
1482 | Handle<String> string; | |||
1483 | if (!maybe_string.ToHandle(&string)) { | |||
1484 | Throw("Malformed string"); | |||
1485 | return roots_.empty_string(); | |||
1486 | } | |||
1487 | if (internalize) { | |||
1488 | string = factory()->InternalizeString(string); | |||
1489 | } | |||
1490 | return *string; | |||
1491 | } | |||
1492 | ||||
1493 | void WebSnapshotDeserializer::DeserializeMaps() { | |||
1494 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Maps); | |||
1495 | if (!deserializer_.ReadUint32(&map_count_) || map_count_ > kMaxItemCount) { | |||
1496 | Throw("Malformed shape table"); | |||
1497 | return; | |||
1498 | } | |||
1499 | STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength)static_assert(kMaxItemCount <= FixedArray::kMaxLength, "kMaxItemCount <= FixedArray::kMaxLength" ); | |||
1500 | maps_handle_ = factory()->NewFixedArray(map_count_); | |||
1501 | maps_ = *maps_handle_; | |||
1502 | for (uint32_t i = 0; i < map_count_; ++i) { | |||
1503 | uint32_t map_type; | |||
1504 | if (!deserializer_.ReadUint32(&map_type)) { | |||
1505 | Throw("Malformed shape"); | |||
1506 | return; | |||
1507 | } | |||
1508 | bool has_custom_property_attributes; | |||
1509 | switch (map_type) { | |||
1510 | case PropertyAttributesType::DEFAULT: | |||
1511 | has_custom_property_attributes = false; | |||
1512 | break; | |||
1513 | case PropertyAttributesType::CUSTOM: | |||
1514 | has_custom_property_attributes = true; | |||
1515 | break; | |||
1516 | default: | |||
1517 | Throw("Unsupported map type"); | |||
1518 | return; | |||
1519 | } | |||
1520 | ||||
1521 | uint32_t prototype_id; | |||
1522 | if (!deserializer_.ReadUint32(&prototype_id) || | |||
1523 | prototype_id > kMaxItemCount) { | |||
1524 | Throw("Malformed shape"); | |||
1525 | return; | |||
1526 | } | |||
1527 | ||||
1528 | uint32_t property_count; | |||
1529 | if (!deserializer_.ReadUint32(&property_count)) { | |||
1530 | Throw("Malformed shape"); | |||
1531 | return; | |||
1532 | } | |||
1533 | // TODO(v8:11525): Consider passing the upper bound as a param and | |||
1534 | // systematically enforcing it on the ValueSerializer side. | |||
1535 | if (property_count > kMaxNumberOfDescriptors) { | |||
1536 | Throw("Malformed shape: too many properties"); | |||
1537 | return; | |||
1538 | } | |||
1539 | ||||
1540 | if (property_count == 0) { | |||
1541 | DisallowGarbageCollection no_gc; | |||
1542 | Map empty_map = | |||
1543 | isolate_->native_context()->object_function().initial_map(); | |||
1544 | maps_.set(i, empty_map); | |||
1545 | continue; | |||
1546 | } | |||
1547 | ||||
1548 | Handle<DescriptorArray> descriptors = | |||
1549 | factory()->NewDescriptorArray(property_count, 0); | |||
1550 | for (InternalIndex i : InternalIndex::Range(property_count)) { | |||
1551 | PropertyAttributes attributes = PropertyAttributes::NONE; | |||
1552 | if (has_custom_property_attributes) { | |||
1553 | uint32_t flags; | |||
1554 | if (!deserializer_.ReadUint32(&flags)) { | |||
1555 | Throw("Malformed shape"); | |||
1556 | return; | |||
1557 | } | |||
1558 | attributes = FlagsToAttributes(flags); | |||
1559 | } | |||
1560 | ||||
1561 | Handle<String> key(ReadString(true), isolate_); | |||
1562 | ||||
1563 | // Use the "none" representation until we see the first object having this | |||
1564 | // map. At that point, modify the representation. | |||
1565 | Descriptor desc = Descriptor::DataField( | |||
1566 | isolate_, key, i.as_int(), attributes, Representation::None()); | |||
1567 | descriptors->Set(i, &desc); | |||
1568 | } | |||
1569 | DCHECK_EQ(descriptors->number_of_descriptors(), property_count)((void) 0); | |||
1570 | descriptors->Sort(); | |||
1571 | ||||
1572 | Handle<Map> map = factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize, | |||
1573 | HOLEY_ELEMENTS, 0); | |||
1574 | map->InitializeDescriptors(isolate_, *descriptors); | |||
1575 | // TODO(v8:11525): Set 'constructor'. | |||
1576 | ||||
1577 | if (prototype_id == 0) { | |||
1578 | // Use Object.prototype as the prototype. | |||
1579 | map->set_prototype(isolate_->native_context()->initial_object_prototype(), | |||
1580 | UPDATE_WRITE_BARRIER); | |||
1581 | } else { | |||
1582 | // TODO(v8::11525): Implement stricter checks, e.g., disallow cycles. | |||
1583 | --prototype_id; | |||
1584 | if (prototype_id < current_object_count_) { | |||
1585 | map->set_prototype(HeapObject::cast(objects_.get(prototype_id)), | |||
1586 | UPDATE_WRITE_BARRIER); | |||
1587 | } else { | |||
1588 | // The object hasn't been deserialized yet. | |||
1589 | AddDeferredReference(map, 0, OBJECT_ID, prototype_id); | |||
1590 | } | |||
1591 | } | |||
1592 | maps_.set(i, *map); | |||
1593 | } | |||
1594 | } | |||
1595 | ||||
1596 | void WebSnapshotDeserializer::DeserializeContexts() { | |||
1597 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Contexts); | |||
1598 | if (!deserializer_.ReadUint32(&context_count_) || | |||
1599 | context_count_ > kMaxItemCount) { | |||
1600 | Throw("Malformed context table"); | |||
1601 | return; | |||
1602 | } | |||
1603 | STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength)static_assert(kMaxItemCount <= FixedArray::kMaxLength, "kMaxItemCount <= FixedArray::kMaxLength" ); | |||
1604 | contexts_handle_ = factory()->NewFixedArray(context_count_); | |||
1605 | contexts_ = *contexts_handle_; | |||
1606 | for (uint32_t i = 0; i < context_count_; ++i) { | |||
1607 | uint32_t context_type; | |||
1608 | if (!deserializer_.ReadUint32(&context_type)) { | |||
1609 | Throw("Malformed context type"); | |||
1610 | return; | |||
1611 | } | |||
1612 | ||||
1613 | uint32_t parent_context_id; | |||
1614 | // Parent context is serialized before child context. Note: not >= on | |||
1615 | // purpose, we're going to subtract 1 later. | |||
1616 | if (!deserializer_.ReadUint32(&parent_context_id) || | |||
1617 | parent_context_id > i) { | |||
1618 | Throw("Malformed context"); | |||
1619 | return; | |||
1620 | } | |||
1621 | ||||
1622 | uint32_t variable_count; | |||
1623 | if (!deserializer_.ReadUint32(&variable_count)) { | |||
1624 | Throw("Malformed context"); | |||
1625 | return; | |||
1626 | } | |||
1627 | // TODO(v8:11525): Enforce upper limit for variable count. | |||
1628 | Handle<ScopeInfo> scope_info = | |||
1629 | CreateScopeInfo(variable_count, parent_context_id > 0, | |||
1630 | static_cast<ContextType>(context_type)); | |||
1631 | ||||
1632 | Handle<Context> parent_context; | |||
1633 | if (parent_context_id > 0) { | |||
1634 | parent_context = | |||
1635 | handle(Context::cast(contexts_.get(parent_context_id - 1)), isolate_); | |||
1636 | scope_info->set_outer_scope_info(parent_context->scope_info()); | |||
1637 | } else { | |||
1638 | parent_context = handle(isolate_->context(), isolate_); | |||
1639 | } | |||
1640 | ||||
1641 | const int context_local_base = ScopeInfo::kVariablePartIndex; | |||
1642 | const int context_local_info_base = context_local_base + variable_count; | |||
1643 | for (int variable_index = 0; | |||
1644 | variable_index < static_cast<int>(variable_count); ++variable_index) { | |||
1645 | { | |||
1646 | String name = ReadString(true); | |||
1647 | scope_info->set(context_local_base + variable_index, name); | |||
1648 | } | |||
1649 | ||||
1650 | // TODO(v8:11525): Support variable modes etc. | |||
1651 | uint32_t info = | |||
1652 | ScopeInfo::VariableModeBits::encode(VariableMode::kLet) | | |||
1653 | ScopeInfo::InitFlagBit::encode( | |||
1654 | InitializationFlag::kNeedsInitialization) | | |||
1655 | ScopeInfo::MaybeAssignedFlagBit::encode( | |||
1656 | MaybeAssignedFlag::kMaybeAssigned) | | |||
1657 | ScopeInfo::ParameterNumberBits::encode( | |||
1658 | ScopeInfo::ParameterNumberBits::kMax) | | |||
1659 | ScopeInfo::IsStaticFlagBit::encode(IsStaticFlag::kNotStatic); | |||
1660 | scope_info->set(context_local_info_base + variable_index, | |||
1661 | Smi::FromInt(info)); | |||
1662 | } | |||
1663 | ||||
1664 | // Allocate the FunctionContext after setting up the ScopeInfo to avoid | |||
1665 | // pointing to a ScopeInfo which is not set up yet. | |||
1666 | Handle<Context> context; | |||
1667 | switch (context_type) { | |||
1668 | case ContextType::FUNCTION: | |||
1669 | context = factory()->NewFunctionContext(parent_context, scope_info); | |||
1670 | break; | |||
1671 | case ContextType::BLOCK: | |||
1672 | context = factory()->NewBlockContext(parent_context, scope_info); | |||
1673 | break; | |||
1674 | default: | |||
1675 | Throw("Unsupported context type"); | |||
1676 | return; | |||
1677 | } | |||
1678 | int context_header_length = scope_info->ContextHeaderLength(); | |||
1679 | for (int variable_index = 0; | |||
1680 | variable_index < static_cast<int>(variable_count); ++variable_index) { | |||
1681 | int context_index = context_header_length + variable_index; | |||
1682 | Object value = ReadValue(context, context_index); | |||
1683 | context->set(context_index, value); | |||
1684 | } | |||
1685 | contexts_.set(i, *context); | |||
1686 | } | |||
1687 | } | |||
1688 | ||||
1689 | Handle<ScopeInfo> WebSnapshotDeserializer::CreateScopeInfo( | |||
1690 | uint32_t variable_count, bool has_parent, ContextType context_type) { | |||
1691 | // TODO(v8:11525): Decide how to handle language modes. (The code below sets | |||
1692 | // the language mode as strict.) | |||
1693 | // TODO(v8:11525): Support (context-allocating) receiver. | |||
1694 | // TODO(v8:11525): Support function variable & function name. | |||
1695 | // TODO(v8:11525): Support classes. | |||
1696 | ||||
1697 | ScopeType scope_type; | |||
1698 | int flags = | |||
1699 | ScopeInfo::SloppyEvalCanExtendVarsBit::encode(false) | | |||
1700 | ScopeInfo::LanguageModeBit::encode(LanguageMode::kStrict) | | |||
1701 | ScopeInfo::DeclarationScopeBit::encode(false) | | |||
1702 | ScopeInfo::ReceiverVariableBits::encode(VariableAllocationInfo::NONE) | | |||
1703 | ScopeInfo::ClassScopeHasPrivateBrandBit::encode(false) | | |||
1704 | ScopeInfo::HasSavedClassVariableBit::encode(false) | | |||
1705 | ScopeInfo::HasNewTargetBit::encode(false) | | |||
1706 | ScopeInfo::FunctionVariableBits::encode(VariableAllocationInfo::NONE) | | |||
1707 | ScopeInfo::HasInferredFunctionNameBit::encode(false) | | |||
1708 | ScopeInfo::IsAsmModuleBit::encode(false) | | |||
1709 | ScopeInfo::HasSimpleParametersBit::encode(false) | | |||
1710 | ScopeInfo::FunctionKindBits::encode(FunctionKind::kNormalFunction) | | |||
1711 | ScopeInfo::HasOuterScopeInfoBit::encode(has_parent) | | |||
1712 | ScopeInfo::IsDebugEvaluateScopeBit::encode(false) | | |||
1713 | ScopeInfo::ForceContextAllocationBit::encode(false) | | |||
1714 | ScopeInfo::PrivateNameLookupSkipsOuterClassBit::encode(false) | | |||
1715 | ScopeInfo::HasContextExtensionSlotBit::encode(false) | | |||
1716 | ScopeInfo::IsReplModeScopeBit::encode(false) | | |||
1717 | ScopeInfo::HasLocalsBlockListBit::encode(false); | |||
1718 | switch (context_type) { | |||
1719 | case ContextType::FUNCTION: | |||
1720 | scope_type = ScopeType::FUNCTION_SCOPE; | |||
1721 | flags |= ScopeInfo::DeclarationScopeBit::encode(true) | | |||
1722 | ScopeInfo::HasSimpleParametersBit::encode(true); | |||
1723 | break; | |||
1724 | case ContextType::BLOCK: | |||
1725 | scope_type = ScopeType::CLASS_SCOPE; | |||
1726 | flags |= ScopeInfo::ForceContextAllocationBit::encode(true); | |||
1727 | break; | |||
1728 | default: | |||
1729 | // Default to a CLASS_SCOPE, so that the rest of the code can be executed | |||
1730 | // without failures. | |||
1731 | scope_type = ScopeType::CLASS_SCOPE; | |||
1732 | Throw("Unsupported context type"); | |||
1733 | } | |||
1734 | flags |= ScopeInfo::ScopeTypeBits::encode(scope_type); | |||
1735 | const int length = ScopeInfo::kVariablePartIndex + | |||
1736 | (ScopeInfo::NeedsPositionInfo(scope_type) | |||
1737 | ? ScopeInfo::kPositionInfoEntries | |||
1738 | : 0) + | |||
1739 | (has_parent ? 1 : 0) + 2 * variable_count; | |||
1740 | Handle<ScopeInfo> scope_info = factory()->NewScopeInfo(length); | |||
1741 | { | |||
1742 | DisallowGarbageCollection no_gc; | |||
1743 | ScopeInfo raw = *scope_info; | |||
1744 | ||||
1745 | raw.set_flags(flags); | |||
1746 | DCHECK(!raw.IsEmpty())((void) 0); | |||
1747 | ||||
1748 | raw.set_context_local_count(variable_count); | |||
1749 | // TODO(v8:11525): Support parameters. | |||
1750 | raw.set_parameter_count(0); | |||
1751 | if (raw.HasPositionInfo()) { | |||
1752 | raw.SetPositionInfo(0, 0); | |||
1753 | } | |||
1754 | } | |||
1755 | return scope_info; | |||
1756 | } | |||
1757 | ||||
1758 | Handle<JSFunction> WebSnapshotDeserializer::CreateJSFunction( | |||
1759 | int shared_function_info_index, uint32_t start_position, uint32_t length, | |||
1760 | uint32_t parameter_count, uint32_t flags, uint32_t context_id) { | |||
1761 | // TODO(v8:11525): Deduplicate the SFIs for class methods. | |||
1762 | FunctionKind kind = FunctionFlagsToFunctionKind(flags); | |||
1763 | Handle<SharedFunctionInfo> shared = factory()->NewSharedFunctionInfo( | |||
1764 | factory()->empty_string(), MaybeHandle<Code>(), Builtin::kCompileLazy, | |||
1765 | kind); | |||
1766 | Handle<UncompiledData> uncompiled_data = | |||
1767 | factory()->NewUncompiledDataWithoutPreparseData( | |||
1768 | roots_.empty_string_handle(), start_position, | |||
1769 | start_position + length); | |||
1770 | { | |||
1771 | DisallowGarbageCollection no_gc; | |||
1772 | SharedFunctionInfo raw = *shared; | |||
1773 | if (IsConciseMethod(kind)) { | |||
1774 | raw.set_syntax_kind(FunctionSyntaxKind::kAccessorOrMethod); | |||
1775 | } | |||
1776 | raw.set_script(*script_); | |||
1777 | raw.set_function_literal_id(shared_function_info_index); | |||
1778 | raw.set_internal_formal_parameter_count(JSParameterCount(parameter_count)); | |||
1779 | // TODO(v8:11525): Decide how to handle language modes. | |||
1780 | raw.set_language_mode(LanguageMode::kStrict); | |||
1781 | raw.set_uncompiled_data(*uncompiled_data); | |||
1782 | raw.set_allows_lazy_compilation(true); | |||
1783 | shared_function_infos_.Set(shared_function_info_index, | |||
1784 | HeapObjectReference::Weak(raw)); | |||
1785 | } | |||
1786 | shared_function_info_table_ = ObjectHashTable::Put( | |||
1787 | shared_function_info_table_, | |||
1788 | handle(Smi::FromInt(start_position), isolate_), | |||
1789 | handle(Smi::FromInt(shared_function_info_index), isolate_)); | |||
1790 | ||||
1791 | Handle<JSFunction> function = | |||
1792 | Factory::JSFunctionBuilder(isolate_, shared, isolate_->native_context()) | |||
1793 | .Build(); | |||
1794 | if (context_id > 0) { | |||
1795 | DCHECK_LT(context_id - 1, context_count_)((void) 0); | |||
1796 | // Guards raw pointer "context" below. | |||
1797 | DisallowHeapAllocation no_heap_access; | |||
1798 | Context context = Context::cast(contexts_.get(context_id - 1)); | |||
1799 | function->set_context(context); | |||
1800 | shared->set_outer_scope_info(context.scope_info()); | |||
1801 | } | |||
1802 | return function; | |||
1803 | } | |||
1804 | ||||
1805 | void WebSnapshotDeserializer::DeserializeFunctions() { | |||
1806 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Functions); | |||
1807 | if (!deserializer_.ReadUint32(&function_count_) || | |||
1808 | function_count_ > kMaxItemCount) { | |||
1809 | Throw("Malformed function table"); | |||
1810 | return; | |||
1811 | } | |||
1812 | STATIC_ASSERT(kMaxItemCount + 1 <= FixedArray::kMaxLength)static_assert(kMaxItemCount + 1 <= FixedArray::kMaxLength, "kMaxItemCount + 1 <= FixedArray::kMaxLength"); | |||
1813 | functions_handle_ = factory()->NewFixedArray(function_count_); | |||
1814 | functions_ = *functions_handle_; | |||
1815 | ||||
1816 | // Overallocate the array for SharedFunctionInfos; functions which we | |||
1817 | // deserialize soon will create more SharedFunctionInfos when called. | |||
1818 | shared_function_infos_handle_ = factory()->NewWeakFixedArray( | |||
1819 | WeakArrayList::CapacityForLength(function_count_ + 1), | |||
1820 | AllocationType::kOld); | |||
1821 | shared_function_infos_ = *shared_function_infos_handle_; | |||
1822 | shared_function_info_table_ = ObjectHashTable::New(isolate_, function_count_); | |||
1823 | script_ = factory()->NewScript(factory()->empty_string()); | |||
1824 | { | |||
1825 | DisallowGarbageCollection no_gc; | |||
1826 | Script raw = *script_; | |||
1827 | raw.set_type(Script::TYPE_WEB_SNAPSHOT); | |||
1828 | raw.set_shared_function_infos(shared_function_infos_); | |||
1829 | raw.set_shared_function_info_table(*shared_function_info_table_); | |||
1830 | } | |||
1831 | ||||
1832 | for (; current_function_count_ < function_count_; ++current_function_count_) { | |||
1833 | uint32_t context_id; | |||
1834 | // Note: > (not >= on purpose, we will subtract 1). | |||
1835 | if (!deserializer_.ReadUint32(&context_id) || context_id > context_count_) { | |||
1836 | Throw("Malformed function"); | |||
1837 | return; | |||
1838 | } | |||
1839 | { | |||
1840 | String source = ReadString(false); | |||
1841 | DisallowGarbageCollection no_gc; | |||
1842 | if (current_function_count_ == 0) { | |||
1843 | script_->set_source(source); | |||
1844 | } else { | |||
1845 | // TODO(v8:11525): Support multiple source snippets. | |||
1846 | DCHECK_EQ(script_->source(), source)((void) 0); | |||
1847 | } | |||
1848 | } | |||
1849 | ||||
1850 | uint32_t start_position; | |||
1851 | uint32_t length; | |||
1852 | uint32_t parameter_count; | |||
1853 | uint32_t flags; | |||
1854 | if (!deserializer_.ReadUint32(&start_position) || | |||
1855 | !deserializer_.ReadUint32(&length) || | |||
1856 | !deserializer_.ReadUint32(¶meter_count) || | |||
1857 | !deserializer_.ReadUint32(&flags)) { | |||
1858 | Throw("Malformed function"); | |||
1859 | return; | |||
1860 | } | |||
1861 | ||||
1862 | // Index 0 is reserved for top-level shared function info (which web | |||
1863 | // snapshot scripts don't have). | |||
1864 | Handle<JSFunction> function = | |||
1865 | CreateJSFunction(current_function_count_ + 1, start_position, length, | |||
1866 | parameter_count, flags, context_id); | |||
1867 | functions_.set(current_function_count_, *function); | |||
1868 | ||||
1869 | ReadFunctionPrototype(function); | |||
1870 | } | |||
1871 | } | |||
1872 | ||||
1873 | void WebSnapshotDeserializer::DeserializeClasses() { | |||
1874 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Classes); | |||
1875 | if (!deserializer_.ReadUint32(&class_count_) || | |||
1876 | class_count_ > kMaxItemCount) { | |||
1877 | Throw("Malformed class table"); | |||
1878 | return; | |||
1879 | } | |||
1880 | STATIC_ASSERT(kMaxItemCount + 1 <= FixedArray::kMaxLength)static_assert(kMaxItemCount + 1 <= FixedArray::kMaxLength, "kMaxItemCount + 1 <= FixedArray::kMaxLength"); | |||
1881 | classes_handle_ = factory()->NewFixedArray(class_count_); | |||
1882 | classes_ = *classes_handle_; | |||
1883 | ||||
1884 | // Grow the array for SharedFunctionInfos. | |||
1885 | shared_function_infos_handle_ = WeakFixedArray::EnsureSpace( | |||
1886 | isolate_, shared_function_infos_handle_, | |||
1887 | WeakArrayList::CapacityForLength(function_count_ + 1 + class_count_)); | |||
1888 | shared_function_infos_ = *shared_function_infos_handle_; | |||
1889 | script_->set_shared_function_infos(shared_function_infos_); | |||
1890 | ||||
1891 | for (; current_class_count_ < class_count_; ++current_class_count_) { | |||
1892 | uint32_t context_id; | |||
1893 | // Note: > (not >= on purpose, we will subtract 1). | |||
1894 | if (!deserializer_.ReadUint32(&context_id) || context_id > context_count_) { | |||
1895 | Throw("Malformed class"); | |||
1896 | return; | |||
1897 | } | |||
1898 | ||||
1899 | { | |||
1900 | String source = ReadString(false); | |||
1901 | if (current_function_count_ + current_class_count_ == 0) { | |||
1902 | script_->set_source(source); | |||
1903 | } else { | |||
1904 | // TODO(v8:11525): Support multiple source snippets. | |||
1905 | DCHECK_EQ(script_->source(), source)((void) 0); | |||
1906 | } | |||
1907 | } | |||
1908 | ||||
1909 | uint32_t start_position; | |||
1910 | uint32_t length; | |||
1911 | uint32_t parameter_count; | |||
1912 | uint32_t flags; | |||
1913 | if (!deserializer_.ReadUint32(&start_position) || | |||
1914 | !deserializer_.ReadUint32(&length) || | |||
1915 | !deserializer_.ReadUint32(¶meter_count) || | |||
1916 | !deserializer_.ReadUint32(&flags)) { | |||
1917 | Throw("Malformed class"); | |||
1918 | return; | |||
1919 | } | |||
1920 | ||||
1921 | // Index 0 is reserved for top-level shared function info (which web | |||
1922 | // snapshot scripts don't have). | |||
1923 | Handle<JSFunction> function = CreateJSFunction( | |||
1924 | function_count_ + current_class_count_ + 1, start_position, length, | |||
1925 | parameter_count, flags, context_id); | |||
1926 | classes_.set(current_class_count_, *function); | |||
1927 | ||||
1928 | ReadFunctionPrototype(function); | |||
1929 | } | |||
1930 | } | |||
1931 | ||||
1932 | void WebSnapshotDeserializer::DeserializeObjects() { | |||
1933 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Objects); | |||
1934 | if (!deserializer_.ReadUint32(&object_count_) || | |||
1935 | object_count_ > kMaxItemCount) { | |||
1936 | Throw("Malformed objects table"); | |||
1937 | return; | |||
1938 | } | |||
1939 | STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength)static_assert(kMaxItemCount <= FixedArray::kMaxLength, "kMaxItemCount <= FixedArray::kMaxLength" ); | |||
1940 | objects_handle_ = factory()->NewFixedArray(object_count_); | |||
1941 | objects_ = *objects_handle_; | |||
1942 | for (; current_object_count_ < object_count_; ++current_object_count_) { | |||
1943 | uint32_t map_id; | |||
1944 | if (!deserializer_.ReadUint32(&map_id) || map_id >= map_count_) { | |||
1945 | Throw("Malformed object"); | |||
1946 | return; | |||
1947 | } | |||
1948 | Map raw_map = Map::cast(maps_.get(map_id)); | |||
1949 | Handle<DescriptorArray> descriptors = | |||
1950 | handle(raw_map.instance_descriptors(kRelaxedLoad), isolate_); | |||
1951 | int no_properties = raw_map.NumberOfOwnDescriptors(); | |||
1952 | // TODO(v8:11525): In-object properties. | |||
1953 | Handle<Map> map(raw_map, isolate_); | |||
1954 | Handle<PropertyArray> property_array = | |||
1955 | factory()->NewPropertyArray(no_properties); | |||
1956 | for (int i = 0; i < no_properties; ++i) { | |||
1957 | Object value = ReadValue(property_array, i); | |||
1958 | DisallowGarbageCollection no_gc; | |||
1959 | // Read the representation from the map. | |||
1960 | DescriptorArray raw_descriptors = *descriptors; | |||
1961 | PropertyDetails details = raw_descriptors.GetDetails(InternalIndex(i)); | |||
1962 | CHECK_EQ(details.location(), PropertyLocation::kField)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(details.location())>::type , typename ::v8::base::pass_value_or_ref<decltype(PropertyLocation ::kField)>::type>((details.location()), (PropertyLocation ::kField)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal ("Check failed: %s.", "details.location()" " " "==" " " "PropertyLocation::kField" ); } } while (false); } while (false); | |||
1963 | CHECK_EQ(PropertyKind::kData, details.kind())do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(PropertyKind::kData)>::type , typename ::v8::base::pass_value_or_ref<decltype(details. kind())>::type>((PropertyKind::kData), (details.kind()) ); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "PropertyKind::kData" " " "==" " " "details.kind()"); } } while (false); } while (false); | |||
1964 | Representation r = details.representation(); | |||
1965 | if (r.IsNone()) { | |||
1966 | // Switch over to wanted_representation. | |||
1967 | details = details.CopyWithRepresentation(Representation::Tagged()); | |||
1968 | raw_descriptors.SetDetails(InternalIndex(i), details); | |||
1969 | } else if (!r.Equals(Representation::Tagged())) { | |||
1970 | // TODO(v8:11525): Support this case too. | |||
1971 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
1972 | } | |||
1973 | property_array->set(i, value); | |||
1974 | } | |||
1975 | Handle<JSObject> object = factory()->NewJSObjectFromMap(map); | |||
1976 | object->set_raw_properties_or_hash(*property_array, kRelaxedStore); | |||
1977 | ||||
1978 | uint32_t max_element_index = 0; | |||
1979 | if (!deserializer_.ReadUint32(&max_element_index) || | |||
1980 | max_element_index > kMaxItemCount + 1) { | |||
1981 | Throw("Malformed object"); | |||
1982 | return; | |||
1983 | } | |||
1984 | if (max_element_index > 0) { | |||
1985 | --max_element_index; // Subtract 1 to get the real max_element_index. | |||
1986 | Handle<FixedArray> elements = | |||
1987 | factory()->NewFixedArray(max_element_index + 1); | |||
1988 | // Read (index, value) pairs until we encounter one where index == | |||
1989 | // max_element_index. | |||
1990 | while (true) { | |||
1991 | uint32_t index; | |||
1992 | if (!deserializer_.ReadUint32(&index) || index > max_element_index) { | |||
1993 | Throw("Malformed object"); | |||
1994 | return; | |||
1995 | } | |||
1996 | Object value = ReadValue(elements, index); | |||
1997 | elements->set(index, value); | |||
1998 | if (index == max_element_index) { | |||
1999 | break; | |||
2000 | } | |||
2001 | } | |||
2002 | object->set_elements(*elements); | |||
2003 | // Objects always get HOLEY_ELEMENTS. | |||
2004 | DCHECK(!IsSmiElementsKind(object->map().elements_kind()))((void) 0); | |||
2005 | DCHECK(!IsDoubleElementsKind(object->map().elements_kind()))((void) 0); | |||
2006 | DCHECK(IsHoleyElementsKind(object->map().elements_kind()))((void) 0); | |||
2007 | } | |||
2008 | objects_.set(static_cast<int>(current_object_count_), *object); | |||
2009 | } | |||
2010 | } | |||
2011 | ||||
2012 | void WebSnapshotDeserializer::DeserializeArrays() { | |||
2013 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Arrays); | |||
2014 | if (!deserializer_.ReadUint32(&array_count_) || | |||
2015 | object_count_ > kMaxItemCount) { | |||
2016 | Throw("Malformed array table"); | |||
2017 | return; | |||
2018 | } | |||
2019 | STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength)static_assert(kMaxItemCount <= FixedArray::kMaxLength, "kMaxItemCount <= FixedArray::kMaxLength" ); | |||
2020 | arrays_handle_ = factory()->NewFixedArray(array_count_); | |||
2021 | arrays_ = *arrays_handle_; | |||
2022 | for (; current_array_count_ < array_count_; ++current_array_count_) { | |||
2023 | uint32_t length; | |||
2024 | if (!deserializer_.ReadUint32(&length) || length > kMaxItemCount) { | |||
2025 | Throw("Malformed array"); | |||
2026 | return; | |||
2027 | } | |||
2028 | Handle<FixedArray> elements = factory()->NewFixedArray(length); | |||
2029 | ElementsKind elements_kind = PACKED_SMI_ELEMENTS; | |||
2030 | for (uint32_t i = 0; i < length; ++i) { | |||
2031 | Object value = ReadValue(elements, i); | |||
2032 | DisallowGarbageCollection no_gc; | |||
2033 | if (!value.IsSmi()) { | |||
2034 | elements_kind = PACKED_ELEMENTS; | |||
2035 | } | |||
2036 | elements->set(static_cast<int>(i), value); | |||
2037 | } | |||
2038 | Handle<JSArray> array = | |||
2039 | factory()->NewJSArrayWithElements(elements, elements_kind, length); | |||
2040 | arrays_.set(static_cast<int>(current_array_count_), *array); | |||
2041 | } | |||
2042 | } | |||
2043 | ||||
2044 | void WebSnapshotDeserializer::DeserializeExports(bool skip_exports) { | |||
2045 | RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Exports); | |||
2046 | uint32_t count; | |||
2047 | if (!deserializer_.ReadUint32(&count) || count > kMaxItemCount) { | |||
2048 | Throw("Malformed export table"); | |||
2049 | return; | |||
2050 | } | |||
2051 | ||||
2052 | if (skip_exports) { | |||
2053 | // In the skip_exports mode, we read the exports but don't do anything about | |||
2054 | // them. This is useful for stress testing; otherwise the GlobalDictionary | |||
2055 | // handling below dominates. | |||
2056 | for (uint32_t i = 0; i < count; ++i) { | |||
2057 | Handle<String> export_name(ReadString(true), isolate_); | |||
2058 | // No deferred references should occur at this point, since all objects | |||
2059 | // have been deserialized. | |||
2060 | Object export_value = ReadValue(); | |||
2061 | USE(export_name)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{export_name }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
2062 | USE(export_value)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{export_value }; (void)unused_tmp_array_for_use_macro; } while (false); | |||
2063 | } | |||
2064 | return; | |||
2065 | } | |||
2066 | ||||
2067 | // Pre-reserve the space for the properties we're going to add to the global | |||
2068 | // object. | |||
2069 | Handle<JSGlobalObject> global = isolate_->global_object(); | |||
2070 | Handle<GlobalDictionary> dictionary( | |||
2071 | global->global_dictionary(isolate_, kAcquireLoad), isolate_); | |||
2072 | ||||
2073 | dictionary = GlobalDictionary::EnsureCapacity( | |||
2074 | isolate_, dictionary, dictionary->NumberOfElements() + count, | |||
2075 | AllocationType::kYoung); | |||
2076 | bool has_exported_values = false; | |||
2077 | ||||
2078 | // TODO(v8:11525): The code below skips checks, in particular | |||
2079 | // LookupIterator::UpdateProtectors and | |||
2080 | // LookupIterator::ExtendingNonExtensible. | |||
2081 | InternalIndex entry = InternalIndex::NotFound(); | |||
2082 | for (uint32_t i = 0; i < count; ++i) { | |||
2083 | Handle<String> export_name(ReadString(true), isolate_); | |||
2084 | // No deferred references should occur at this point, since all objects have | |||
2085 | // been deserialized. | |||
2086 | Object export_value = ReadValue(); | |||
2087 | ||||
2088 | if (export_name->length() == 0 && i == 0) { | |||
2089 | // Hack: treat the first empty-string-named export value as a return value | |||
2090 | // from the deserializer. | |||
2091 | CHECK_EQ(i, 0)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(i)>::type, typename ::v8:: base::pass_value_or_ref<decltype(0)>::type>((i), (0) ); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "i" " " "==" " " "0"); } } while (false); } while (false); | |||
2092 | return_value_ = handle(export_value, isolate_); | |||
2093 | continue; | |||
2094 | } | |||
2095 | ||||
2096 | DisallowGarbageCollection no_gc; | |||
2097 | // Check for the correctness of the snapshot (thus far) before producing | |||
2098 | // something observable. TODO(v8:11525): Strictly speaking, we should | |||
2099 | // produce observable effects only when we know that the whole snapshot is | |||
2100 | // correct. | |||
2101 | if (has_error()) return; | |||
2102 | ||||
2103 | PropertyDetails property_details = | |||
2104 | PropertyDetails(PropertyKind::kData, NONE, | |||
2105 | PropertyCell::InitialType(isolate_, export_value)); | |||
2106 | Handle<Object> export_value_handle(export_value, isolate_); | |||
2107 | AllowGarbageCollection allow_gc; | |||
2108 | Handle<PropertyCell> transition_cell = factory()->NewPropertyCell( | |||
2109 | export_name, property_details, export_value_handle); | |||
2110 | dictionary = | |||
2111 | GlobalDictionary::Add(isolate_, dictionary, export_name, | |||
2112 | transition_cell, property_details, &entry); | |||
2113 | has_exported_values = true; | |||
2114 | } | |||
2115 | ||||
2116 | if (!has_exported_values) return; | |||
2117 | ||||
2118 | global->set_global_dictionary(*dictionary, kReleaseStore); | |||
2119 | JSObject::InvalidatePrototypeChains(global->map(isolate_)); | |||
2120 | } | |||
2121 | ||||
2122 | Object WebSnapshotDeserializer::ReadValue(Handle<HeapObject> container, | |||
2123 | uint32_t container_index) { | |||
2124 | uint32_t value_type; | |||
2125 | // TODO(v8:11525): Consider adding a ReadByte. | |||
2126 | if (!deserializer_.ReadUint32(&value_type)) { | |||
2127 | Throw("Malformed variable"); | |||
2128 | // Set "value" here so that the "keep on trucking" error handling won't fail | |||
2129 | // when dereferencing the handle. | |||
2130 | return Smi::zero(); | |||
2131 | } | |||
2132 | switch (value_type) { | |||
2133 | case ValueType::FALSE_CONSTANT: | |||
2134 | return roots_.false_value(); | |||
2135 | case ValueType::TRUE_CONSTANT: | |||
2136 | return roots_.true_value(); | |||
2137 | case ValueType::NULL_CONSTANT: | |||
2138 | return roots_.null_value(); | |||
2139 | case ValueType::UNDEFINED_CONSTANT: | |||
2140 | return roots_.undefined_value(); | |||
2141 | case ValueType::INTEGER: | |||
2142 | return ReadInteger(); | |||
2143 | case ValueType::DOUBLE: | |||
2144 | return ReadNumber(); | |||
2145 | case ValueType::STRING_ID: | |||
2146 | return ReadString(false); | |||
2147 | case ValueType::ARRAY_ID: | |||
2148 | return ReadArray(container, container_index); | |||
2149 | case ValueType::OBJECT_ID: | |||
2150 | return ReadObject(container, container_index); | |||
2151 | case ValueType::FUNCTION_ID: | |||
2152 | return ReadFunction(container, container_index); | |||
2153 | case ValueType::CLASS_ID: | |||
2154 | return ReadClass(container, container_index); | |||
2155 | case ValueType::REGEXP: | |||
2156 | return ReadRegexp(); | |||
2157 | case ValueType::EXTERNAL_ID: | |||
2158 | return ReadExternalReference(); | |||
2159 | case ValueType::IN_PLACE_STRING_ID: | |||
2160 | return ReadInPlaceString(false); | |||
2161 | default: | |||
2162 | // TODO(v8:11525): Handle other value types. | |||
2163 | Throw("Unsupported value type"); | |||
2164 | return Smi::zero(); | |||
2165 | } | |||
2166 | } | |||
2167 | ||||
2168 | Object WebSnapshotDeserializer::ReadInteger() { | |||
2169 | Maybe<int32_t> number = deserializer_.ReadZigZag<int32_t>(); | |||
2170 | if (number.IsNothing()) { | |||
2171 | Throw("Malformed integer"); | |||
2172 | return Smi::zero(); | |||
2173 | } | |||
2174 | return *factory()->NewNumberFromInt(number.FromJust()); | |||
2175 | } | |||
2176 | ||||
2177 | Object WebSnapshotDeserializer::ReadNumber() { | |||
2178 | double number; | |||
2179 | if (!deserializer_.ReadDouble(&number)) { | |||
2180 | Throw("Malformed double"); | |||
2181 | return Smi::zero(); | |||
2182 | } | |||
2183 | return *factory()->NewNumber(number); | |||
2184 | } | |||
2185 | ||||
2186 | Object WebSnapshotDeserializer::ReadArray(Handle<HeapObject> container, | |||
2187 | uint32_t index) { | |||
2188 | uint32_t array_id; | |||
2189 | if (!deserializer_.ReadUint32(&array_id) || array_id >= kMaxItemCount) { | |||
2190 | Throw("Malformed variable"); | |||
2191 | return Smi::zero(); | |||
2192 | } | |||
2193 | if (array_id < current_array_count_) { | |||
2194 | return arrays_.get(array_id); | |||
2195 | } | |||
2196 | // The array hasn't been deserialized yet. | |||
2197 | return AddDeferredReference(container, index, ARRAY_ID, array_id); | |||
2198 | } | |||
2199 | ||||
2200 | Object WebSnapshotDeserializer::ReadObject(Handle<HeapObject> container, | |||
2201 | uint32_t index) { | |||
2202 | uint32_t object_id; | |||
2203 | if (!deserializer_.ReadUint32(&object_id) || object_id > kMaxItemCount) { | |||
2204 | Throw("Malformed variable"); | |||
2205 | return Smi::zero(); | |||
2206 | } | |||
2207 | if (object_id < current_object_count_) { | |||
2208 | return objects_.get(object_id); | |||
2209 | } | |||
2210 | // The object hasn't been deserialized yet. | |||
2211 | return AddDeferredReference(container, index, OBJECT_ID, object_id); | |||
2212 | } | |||
2213 | ||||
2214 | Object WebSnapshotDeserializer::ReadFunction(Handle<HeapObject> container, | |||
2215 | uint32_t index) { | |||
2216 | uint32_t function_id; | |||
2217 | if (!deserializer_.ReadUint32(&function_id) || | |||
2218 | function_id >= function_count_) { | |||
2219 | Throw("Malformed object property"); | |||
2220 | return Smi::zero(); | |||
2221 | } | |||
2222 | if (function_id < current_function_count_) { | |||
2223 | return functions_.get(function_id); | |||
2224 | } | |||
2225 | // The function hasn't been deserialized yet. | |||
2226 | return AddDeferredReference(container, index, FUNCTION_ID, function_id); | |||
2227 | } | |||
2228 | ||||
2229 | Object WebSnapshotDeserializer::ReadClass(Handle<HeapObject> container, | |||
2230 | uint32_t index) { | |||
2231 | uint32_t class_id; | |||
2232 | if (!deserializer_.ReadUint32(&class_id) || class_id >= kMaxItemCount) { | |||
2233 | Throw("Malformed object property"); | |||
2234 | return Smi::zero(); | |||
2235 | } | |||
2236 | if (class_id < current_class_count_) { | |||
2237 | return classes_.get(class_id); | |||
2238 | } | |||
2239 | // The class hasn't been deserialized yet. | |||
2240 | return AddDeferredReference(container, index, CLASS_ID, class_id); | |||
2241 | } | |||
2242 | ||||
2243 | Object WebSnapshotDeserializer::ReadRegexp() { | |||
2244 | Handle<String> pattern(ReadString(false), isolate_); | |||
2245 | Handle<String> flags_string(ReadString(false), isolate_); | |||
2246 | base::Optional<JSRegExp::Flags> flags = | |||
2247 | JSRegExp::FlagsFromString(isolate_, flags_string); | |||
2248 | if (!flags.has_value()) { | |||
2249 | Throw("Malformed flags in regular expression"); | |||
2250 | return Smi::zero(); | |||
2251 | } | |||
2252 | MaybeHandle<JSRegExp> maybe_regexp = | |||
2253 | JSRegExp::New(isolate_, pattern, flags.value()); | |||
2254 | Handle<JSRegExp> regexp; | |||
2255 | if (!maybe_regexp.ToHandle(®exp)) { | |||
2256 | Throw("Malformed RegExp"); | |||
2257 | return Smi::zero(); | |||
2258 | } | |||
2259 | return *regexp; | |||
2260 | } | |||
2261 | ||||
2262 | Object WebSnapshotDeserializer::ReadExternalReference() { | |||
2263 | uint32_t ref_id; | |||
2264 | if (!deserializer_.ReadUint32(&ref_id) || | |||
2265 | ref_id >= static_cast<uint32_t>(external_references_.length())) { | |||
2266 | Throw("Invalid external reference"); | |||
2267 | return Smi::zero(); | |||
2268 | } | |||
2269 | return external_references_.get(ref_id); | |||
2270 | } | |||
2271 | ||||
2272 | void WebSnapshotDeserializer::ReadFunctionPrototype( | |||
2273 | Handle<JSFunction> function) { | |||
2274 | uint32_t object_id; | |||
2275 | ||||
2276 | if (!deserializer_.ReadUint32(&object_id) || object_id > kMaxItemCount + 1) { | |||
2277 | Throw("Malformed class / function"); | |||
2278 | return; | |||
2279 | } | |||
2280 | if (object_id == 0) { | |||
2281 | // No prototype. | |||
2282 | return; | |||
2283 | } | |||
2284 | --object_id; | |||
2285 | if (object_id < current_object_count_) { | |||
2286 | if (!SetFunctionPrototype(*function, | |||
2287 | JSReceiver::cast(objects_.get(object_id)))) { | |||
2288 | Throw("Can't reuse function prototype"); | |||
2289 | return; | |||
2290 | } | |||
2291 | } else { | |||
2292 | // The object hasn't been deserialized yet. | |||
2293 | AddDeferredReference(function, 0, OBJECT_ID, object_id); | |||
2294 | } | |||
2295 | } | |||
2296 | ||||
2297 | bool WebSnapshotDeserializer::SetFunctionPrototype(JSFunction function, | |||
2298 | JSReceiver prototype) { | |||
2299 | DisallowGarbageCollection no_gc; | |||
2300 | // TODO(v8:11525): Enforce the invariant that no two prototypes share a map. | |||
2301 | Map map = prototype.map(); | |||
2302 | map.set_is_prototype_map(true); | |||
2303 | if (!map.constructor_or_back_pointer().IsNullOrUndefined(isolate_)) { | |||
2304 | return false; | |||
2305 | } | |||
2306 | map.set_constructor_or_back_pointer(function); | |||
2307 | function.set_prototype_or_initial_map(prototype, kReleaseStore); | |||
2308 | return true; | |||
2309 | } | |||
2310 | ||||
2311 | HeapObject WebSnapshotDeserializer::AddDeferredReference( | |||
2312 | Handle<HeapObject> container, uint32_t index, ValueType target_type, | |||
2313 | uint32_t target_index) { | |||
2314 | if (container.is_null()) { | |||
2315 | const char* message = "Invalid reference"; | |||
2316 | switch (target_type) { | |||
2317 | case ARRAY_ID: | |||
2318 | message = "Invalid array reference"; | |||
2319 | break; | |||
2320 | case OBJECT_ID: | |||
2321 | message = "Invalid object reference"; | |||
2322 | break; | |||
2323 | case CLASS_ID: | |||
2324 | message = "Invalid class reference"; | |||
2325 | break; | |||
2326 | case FUNCTION_ID: | |||
2327 | message = "Invalid function reference"; | |||
2328 | break; | |||
2329 | default: | |||
2330 | break; | |||
2331 | } | |||
2332 | Throw(message); | |||
2333 | return roots_.undefined_value(); | |||
2334 | } | |||
2335 | DCHECK(container->IsPropertyArray() || container->IsContext() ||((void) 0) | |||
2336 | container->IsFixedArray() || container->IsJSFunction() ||((void) 0) | |||
2337 | container->IsMap())((void) 0); | |||
2338 | deferred_references_ = ArrayList::Add( | |||
2339 | isolate_, deferred_references_, container, Smi::FromInt(index), | |||
2340 | Smi::FromInt(target_type), Smi::FromInt(target_index)); | |||
2341 | // Use HeapObject as placeholder since this might break elements kinds. | |||
2342 | return roots_.undefined_value(); | |||
2343 | } | |||
2344 | ||||
2345 | void WebSnapshotDeserializer::ProcessDeferredReferences() { | |||
2346 | // Check for error now, since the FixedArrays below might not have been | |||
2347 | // created if there was an error. | |||
2348 | if (has_error()) return; | |||
2349 | ||||
2350 | DisallowGarbageCollection no_gc; | |||
2351 | ArrayList raw_deferred_references = *deferred_references_; | |||
2352 | ||||
2353 | // Deferred references is a list of (object, index, target type, target index) | |||
2354 | // tuples. | |||
2355 | for (int i = 0; i < raw_deferred_references.Length() - 3; i += 4) { | |||
2356 | HeapObject container = HeapObject::cast(raw_deferred_references.Get(i)); | |||
2357 | int index = raw_deferred_references.Get(i + 1).ToSmi().value(); | |||
2358 | ValueType target_type = static_cast<ValueType>( | |||
2359 | raw_deferred_references.Get(i + 2).ToSmi().value()); | |||
2360 | int target_index = raw_deferred_references.Get(i + 3).ToSmi().value(); | |||
2361 | Object target; | |||
2362 | switch (target_type) { | |||
2363 | case FUNCTION_ID: | |||
2364 | if (static_cast<uint32_t>(target_index) >= function_count_) { | |||
2365 | // Throw can allocate, but it's ok, since we're not using the raw | |||
2366 | // pointers after that. | |||
2367 | AllowGarbageCollection allow_gc; | |||
2368 | Throw("Invalid function reference"); | |||
2369 | return; | |||
2370 | } | |||
2371 | target = functions_.get(target_index); | |||
2372 | break; | |||
2373 | case CLASS_ID: | |||
2374 | if (static_cast<uint32_t>(target_index) >= class_count_) { | |||
2375 | AllowGarbageCollection allow_gc; | |||
2376 | Throw("Invalid class reference"); | |||
2377 | return; | |||
2378 | } | |||
2379 | target = classes_.get(target_index); | |||
2380 | break; | |||
2381 | case ARRAY_ID: | |||
2382 | if (static_cast<uint32_t>(target_index) >= array_count_) { | |||
2383 | AllowGarbageCollection allow_gc; | |||
2384 | Throw("Invalid array reference"); | |||
2385 | return; | |||
2386 | } | |||
2387 | target = arrays_.get(target_index); | |||
2388 | break; | |||
2389 | case OBJECT_ID: | |||
2390 | if (static_cast<uint32_t>(target_index) >= object_count_) { | |||
2391 | AllowGarbageCollection allow_gc; | |||
2392 | Throw("Invalid object reference"); | |||
2393 | return; | |||
2394 | } | |||
2395 | target = objects_.get(target_index); | |||
2396 | break; | |||
2397 | default: | |||
2398 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
2399 | } | |||
2400 | InstanceType instance_type = container.map().instance_type(); | |||
2401 | if (InstanceTypeChecker::IsPropertyArray(instance_type)) { | |||
2402 | PropertyArray::cast(container).set(index, target); | |||
2403 | } else if (InstanceTypeChecker::IsContext(instance_type)) { | |||
2404 | Context::cast(container).set(index, target); | |||
2405 | } else if (InstanceTypeChecker::IsFixedArray(instance_type)) { | |||
2406 | FixedArray::cast(container).set(index, target); | |||
2407 | } else if (InstanceTypeChecker::IsJSFunction(instance_type)) { | |||
2408 | // The only deferred reference allowed for a JSFunction is the function | |||
2409 | // prototype. | |||
2410 | DCHECK_EQ(index, 0)((void) 0); | |||
2411 | DCHECK(target.IsJSReceiver())((void) 0); | |||
2412 | if (!SetFunctionPrototype(JSFunction::cast(container), | |||
2413 | JSReceiver::cast(target))) { | |||
2414 | AllowGarbageCollection allow_gc; | |||
2415 | Throw("Can't reuse function prototype"); | |||
2416 | return; | |||
2417 | } | |||
2418 | } else if (InstanceTypeChecker::IsMap(instance_type)) { | |||
2419 | // The only deferred reference allowed for a Map is the __proto__. | |||
2420 | DCHECK_EQ(index, 0)((void) 0); | |||
2421 | DCHECK(target.IsJSReceiver())((void) 0); | |||
2422 | Map::cast(container).set_prototype(HeapObject::cast(target), | |||
2423 | UPDATE_WRITE_BARRIER); | |||
2424 | } else { | |||
2425 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
2426 | } | |||
2427 | } | |||
2428 | deferred_references_->SetLength(0); | |||
2429 | } | |||
2430 | ||||
2431 | } // namespace internal | |||
2432 | } // namespace v8 |
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 | #ifndef V8_SNAPSHOT_SERIALIZER_H_ |
6 | #define V8_SNAPSHOT_SERIALIZER_H_ |
7 | |
8 | #include "src/codegen/external-reference-encoder.h" |
9 | #include "src/common/assert-scope.h" |
10 | #include "src/execution/isolate.h" |
11 | #include "src/handles/global-handles.h" |
12 | #include "src/logging/log.h" |
13 | #include "src/objects/objects.h" |
14 | #include "src/snapshot/embedded/embedded-data.h" |
15 | #include "src/snapshot/serializer-deserializer.h" |
16 | #include "src/snapshot/snapshot-source-sink.h" |
17 | #include "src/snapshot/snapshot.h" |
18 | #include "src/utils/identity-map.h" |
19 | |
20 | namespace v8 { |
21 | namespace internal { |
22 | |
23 | class CodeAddressMap : public CodeEventLogger { |
24 | public: |
25 | explicit CodeAddressMap(Isolate* isolate) : CodeEventLogger(isolate) { |
26 | isolate->logger()->AddCodeEventListener(this); |
27 | } |
28 | |
29 | ~CodeAddressMap() override { |
30 | isolate_->logger()->RemoveCodeEventListener(this); |
31 | } |
32 | |
33 | void CodeMoveEvent(AbstractCode from, AbstractCode to) override { |
34 | address_to_name_map_.Move(from.address(), to.address()); |
35 | } |
36 | |
37 | void CodeDisableOptEvent(Handle<AbstractCode> code, |
38 | Handle<SharedFunctionInfo> shared) override {} |
39 | |
40 | const char* Lookup(Address address) { |
41 | return address_to_name_map_.Lookup(address); |
42 | } |
43 | |
44 | private: |
45 | class NameMap { |
46 | public: |
47 | NameMap() : impl_() {} |
48 | NameMap(const NameMap&) = delete; |
49 | NameMap& operator=(const NameMap&) = delete; |
50 | |
51 | ~NameMap() { |
52 | for (base::HashMap::Entry* p = impl_.Start(); p != nullptr; |
53 | p = impl_.Next(p)) { |
54 | DeleteArray(static_cast<const char*>(p->value)); |
55 | } |
56 | } |
57 | |
58 | void Insert(Address code_address, const char* name, int name_size) { |
59 | base::HashMap::Entry* entry = FindOrCreateEntry(code_address); |
60 | if (entry->value == nullptr) { |
61 | entry->value = CopyName(name, name_size); |
62 | } |
63 | } |
64 | |
65 | const char* Lookup(Address code_address) { |
66 | base::HashMap::Entry* entry = FindEntry(code_address); |
67 | return (entry != nullptr) ? static_cast<const char*>(entry->value) |
68 | : nullptr; |
69 | } |
70 | |
71 | void Remove(Address code_address) { |
72 | base::HashMap::Entry* entry = FindEntry(code_address); |
73 | if (entry != nullptr) { |
74 | DeleteArray(static_cast<char*>(entry->value)); |
75 | RemoveEntry(entry); |
76 | } |
77 | } |
78 | |
79 | void Move(Address from, Address to) { |
80 | if (from == to) return; |
81 | base::HashMap::Entry* from_entry = FindEntry(from); |
82 | DCHECK_NOT_NULL(from_entry)((void) 0); |
83 | void* value = from_entry->value; |
84 | RemoveEntry(from_entry); |
85 | base::HashMap::Entry* to_entry = FindOrCreateEntry(to); |
86 | DCHECK_NULL(to_entry->value)((void) 0); |
87 | to_entry->value = value; |
88 | } |
89 | |
90 | private: |
91 | static char* CopyName(const char* name, int name_size) { |
92 | char* result = NewArray<char>(name_size + 1); |
93 | for (int i = 0; i < name_size; ++i) { |
94 | char c = name[i]; |
95 | if (c == '\0') c = ' '; |
96 | result[i] = c; |
97 | } |
98 | result[name_size] = '\0'; |
99 | return result; |
100 | } |
101 | |
102 | base::HashMap::Entry* FindOrCreateEntry(Address code_address) { |
103 | return impl_.LookupOrInsert(reinterpret_cast<void*>(code_address), |
104 | ComputeAddressHash(code_address)); |
105 | } |
106 | |
107 | base::HashMap::Entry* FindEntry(Address code_address) { |
108 | return impl_.Lookup(reinterpret_cast<void*>(code_address), |
109 | ComputeAddressHash(code_address)); |
110 | } |
111 | |
112 | void RemoveEntry(base::HashMap::Entry* entry) { |
113 | impl_.Remove(entry->key, entry->hash); |
114 | } |
115 | |
116 | base::HashMap impl_; |
117 | }; |
118 | |
119 | void LogRecordedBuffer(Handle<AbstractCode> code, |
120 | MaybeHandle<SharedFunctionInfo>, const char* name, |
121 | int length) override { |
122 | address_to_name_map_.Insert(code->address(), name, length); |
123 | } |
124 | |
125 | #if V8_ENABLE_WEBASSEMBLY1 |
126 | void LogRecordedBuffer(const wasm::WasmCode* code, const char* name, |
127 | int length) override { |
128 | UNREACHABLE()V8_Fatal("unreachable code"); |
129 | } |
130 | #endif // V8_ENABLE_WEBASSEMBLY |
131 | |
132 | NameMap address_to_name_map_; |
133 | }; |
134 | |
135 | class ObjectCacheIndexMap { |
136 | public: |
137 | explicit ObjectCacheIndexMap(Heap* heap) : map_(heap), next_index_(0) {} |
138 | ObjectCacheIndexMap(const ObjectCacheIndexMap&) = delete; |
139 | ObjectCacheIndexMap& operator=(const ObjectCacheIndexMap&) = delete; |
140 | |
141 | // If |obj| is in the map, immediately return true. Otherwise add it to the |
142 | // map and return false. In either case set |*index_out| to the index |
143 | // associated with the map. |
144 | bool LookupOrInsert(HeapObject obj, int* index_out) { |
145 | auto find_result = map_.FindOrInsert(obj); |
146 | if (!find_result.already_exists) { |
147 | *find_result.entry = next_index_++; |
148 | } |
149 | *index_out = *find_result.entry; |
150 | return find_result.already_exists; |
151 | } |
152 | bool LookupOrInsert(Handle<HeapObject> obj, int* index_out) { |
153 | return LookupOrInsert(*obj, index_out); |
154 | } |
155 | |
156 | bool Lookup(HeapObject obj, int* index_out) const { |
157 | int* index = map_.Find(obj); |
158 | if (index == nullptr) { |
159 | return false; |
160 | } |
161 | *index_out = *index; |
162 | return true; |
163 | } |
164 | |
165 | Handle<FixedArray> Values(Isolate* isolate); |
166 | |
167 | int size() const { return next_index_; } |
168 | |
169 | private: |
170 | IdentityMap<int, base::DefaultAllocationPolicy> map_; |
171 | int next_index_; |
172 | }; |
173 | |
174 | class Serializer : public SerializerDeserializer { |
175 | public: |
176 | Serializer(Isolate* isolate, Snapshot::SerializerFlags flags); |
177 | ~Serializer() override { DCHECK_EQ(unresolved_forward_refs_, 0)((void) 0); } |
178 | Serializer(const Serializer&) = delete; |
179 | Serializer& operator=(const Serializer&) = delete; |
180 | |
181 | const std::vector<byte>* Payload() const { return sink_.data(); } |
182 | |
183 | bool ReferenceMapContains(Handle<HeapObject> o) { |
184 | return reference_map()->LookupReference(o) != nullptr; |
185 | } |
186 | |
187 | Isolate* isolate() const { return isolate_; } |
188 | |
189 | // The pointer compression cage base value used for decompression of all |
190 | // tagged values except references to Code objects. |
191 | PtrComprCageBase cage_base() const { |
192 | #if V8_COMPRESS_POINTERS |
193 | return cage_base_; |
194 | #else |
195 | return PtrComprCageBase{}; |
196 | #endif // V8_COMPRESS_POINTERS |
197 | } |
198 | |
199 | int TotalAllocationSize() const; |
200 | |
201 | protected: |
202 | using PendingObjectReferences = std::vector<int>*; |
203 | |
204 | class ObjectSerializer; |
205 | class V8_NODISCARD[[nodiscard]] RecursionScope { |
206 | public: |
207 | explicit RecursionScope(Serializer* serializer) : serializer_(serializer) { |
208 | serializer_->recursion_depth_++; |
209 | } |
210 | ~RecursionScope() { serializer_->recursion_depth_--; } |
211 | bool ExceedsMaximum() { |
212 | return serializer_->recursion_depth_ >= kMaxRecursionDepth; |
213 | } |
214 | |
215 | private: |
216 | static const int kMaxRecursionDepth = 32; |
217 | Serializer* serializer_; |
218 | }; |
219 | |
220 | // Compares obj with not_mapped_symbol root. When V8_EXTERNAL_CODE_SPACE is |
221 | // enabled it compares full pointers. |
222 | V8_INLINEinline __attribute__((always_inline)) bool IsNotMappedSymbol(HeapObject obj) const; |
223 | |
224 | void SerializeDeferredObjects(); |
225 | void SerializeObject(Handle<HeapObject> o); |
226 | virtual void SerializeObjectImpl(Handle<HeapObject> o) = 0; |
227 | |
228 | virtual bool MustBeDeferred(HeapObject object); |
229 | |
230 | void VisitRootPointers(Root root, const char* description, |
231 | FullObjectSlot start, FullObjectSlot end) override; |
232 | void SerializeRootObject(FullObjectSlot slot); |
233 | |
234 | void PutRoot(RootIndex root_index); |
235 | void PutSmiRoot(FullObjectSlot slot); |
236 | void PutBackReference(HeapObject object, SerializerReference reference); |
237 | void PutAttachedReference(SerializerReference reference); |
238 | void PutNextChunk(SnapshotSpace space); |
239 | void PutRepeat(int repeat_count); |
240 | |
241 | // Emit a marker noting that this slot is a forward reference to the an |
242 | // object which has not yet been serialized. |
243 | void PutPendingForwardReference(PendingObjectReferences& ref); |
244 | // Resolve the given previously registered forward reference to the current |
245 | // object. |
246 | void ResolvePendingForwardReference(int obj); |
247 | |
248 | // Returns true if the object was successfully serialized as a root. |
249 | bool SerializeRoot(HeapObject obj); |
250 | |
251 | // Returns true if the object was successfully serialized as hot object. |
252 | bool SerializeHotObject(HeapObject obj); |
253 | |
254 | // Returns true if the object was successfully serialized as back reference. |
255 | bool SerializeBackReference(HeapObject obj); |
256 | |
257 | // Returns true if the object was successfully serialized as pending object. |
258 | bool SerializePendingObject(HeapObject obj); |
259 | |
260 | // Returns true if the given heap object is a bytecode handler code object. |
261 | bool ObjectIsBytecodeHandler(HeapObject obj) const; |
262 | |
263 | ExternalReferenceEncoder::Value EncodeExternalReference(Address addr); |
264 | |
265 | Maybe<ExternalReferenceEncoder::Value> TryEncodeExternalReference( |
266 | Address addr) { |
267 | return external_reference_encoder_.TryEncode(addr); |
268 | } |
269 | |
270 | // GetInt reads 4 bytes at once, requiring padding at the end. |
271 | // Use padding_offset to specify the space you want to use after padding. |
272 | void Pad(int padding_offset = 0); |
273 | |
274 | // We may not need the code address map for logging for every instance |
275 | // of the serializer. Initialize it on demand. |
276 | void InitializeCodeAddressMap(); |
277 | |
278 | Code CopyCode(Code code); |
279 | |
280 | void QueueDeferredObject(HeapObject obj) { |
281 | DCHECK_NULL(reference_map_.LookupReference(obj))((void) 0); |
282 | deferred_objects_.Push(obj); |
283 | } |
284 | |
285 | // Register that the the given object shouldn't be immediately serialized, but |
286 | // will be serialized later and any references to it should be pending forward |
287 | // references. |
288 | void RegisterObjectIsPending(HeapObject obj); |
289 | |
290 | // Resolve the given pending object reference with the current object. |
291 | void ResolvePendingObject(HeapObject obj); |
292 | |
293 | void OutputStatistics(const char* name); |
294 | |
295 | void CountAllocation(Map map, int size, SnapshotSpace space); |
296 | |
297 | #ifdef DEBUG |
298 | void PushStack(Handle<HeapObject> o) { stack_.Push(*o); } |
299 | void PopStack(); |
300 | void PrintStack(); |
301 | void PrintStack(std::ostream&); |
302 | #endif // DEBUG |
303 | |
304 | SerializerReferenceMap* reference_map() { return &reference_map_; } |
305 | const RootIndexMap* root_index_map() const { return &root_index_map_; } |
306 | |
307 | SnapshotByteSink sink_; // Used directly by subclasses. |
308 | |
309 | bool allow_unknown_external_references_for_testing() const { |
310 | return (flags_ & Snapshot::kAllowUnknownExternalReferencesForTesting) != 0; |
311 | } |
312 | bool allow_active_isolate_for_testing() const { |
313 | return (flags_ & Snapshot::kAllowActiveIsolateForTesting) != 0; |
314 | } |
315 | |
316 | bool reconstruct_read_only_and_shared_object_caches_for_testing() const { |
317 | return (flags_ & |
318 | Snapshot::kReconstructReadOnlyAndSharedObjectCachesForTesting) != 0; |
319 | } |
320 | |
321 | private: |
322 | // A circular queue of hot objects. This is added to in the same order as in |
323 | // Deserializer::HotObjectsList, but this stores the objects as an array of |
324 | // raw addresses that are considered strong roots. This allows objects to be |
325 | // added to the list without having to extend their handle's lifetime. |
326 | // |
327 | // We should never allow this class to return Handles to objects in the queue, |
328 | // as the object in the queue may change if kSize other objects are added to |
329 | // the queue during that Handle's lifetime. |
330 | class HotObjectsList { |
331 | public: |
332 | explicit HotObjectsList(Heap* heap); |
333 | ~HotObjectsList(); |
334 | HotObjectsList(const HotObjectsList&) = delete; |
335 | HotObjectsList& operator=(const HotObjectsList&) = delete; |
336 | |
337 | void Add(HeapObject object) { |
338 | circular_queue_[index_] = object.ptr(); |
339 | index_ = (index_ + 1) & kSizeMask; |
340 | } |
341 | |
342 | static const int kNotFound = -1; |
343 | |
344 | int Find(HeapObject object) { |
345 | DCHECK(!AllowGarbageCollection::IsAllowed())((void) 0); |
346 | for (int i = 0; i < kSize; i++) { |
347 | if (circular_queue_[i] == object.ptr()) { |
348 | return i; |
349 | } |
350 | } |
351 | return kNotFound; |
352 | } |
353 | |
354 | private: |
355 | static const int kSize = kHotObjectCount; |
356 | static const int kSizeMask = kSize - 1; |
357 | STATIC_ASSERT(base::bits::IsPowerOfTwo(kSize))static_assert(base::bits::IsPowerOfTwo(kSize), "base::bits::IsPowerOfTwo(kSize)" ); |
358 | Heap* heap_; |
359 | StrongRootsEntry* strong_roots_entry_; |
360 | Address circular_queue_[kSize] = {kNullAddress}; |
361 | int index_ = 0; |
362 | }; |
363 | |
364 | // Disallow GC during serialization. |
365 | // TODO(leszeks, v8:10815): Remove this constraint. |
366 | DISALLOW_GARBAGE_COLLECTION(no_gc_) |
367 | |
368 | Isolate* isolate_; |
369 | #if V8_COMPRESS_POINTERS |
370 | const PtrComprCageBase cage_base_; |
371 | #endif // V8_COMPRESS_POINTERS |
372 | HotObjectsList hot_objects_; |
373 | SerializerReferenceMap reference_map_; |
374 | ExternalReferenceEncoder external_reference_encoder_; |
375 | RootIndexMap root_index_map_; |
376 | std::unique_ptr<CodeAddressMap> code_address_map_; |
377 | std::vector<byte> code_buffer_; |
378 | GlobalHandleVector<HeapObject> |
379 | deferred_objects_; // To handle stack overflow. |
380 | int num_back_refs_ = 0; |
381 | |
382 | // Objects which have started being serialized, but haven't yet been allocated |
383 | // with the allocator, are considered "pending". References to them don't have |
384 | // an allocation to backref to, so instead they are registered as pending |
385 | // forward references, which are resolved once the object is allocated. |
386 | // |
387 | // Forward references are registered in a deterministic order, and can |
388 | // therefore be identified by an incrementing integer index, which is |
389 | // effectively an index into a vector of the currently registered forward |
390 | // refs. The references in this vector might not be resolved in order, so we |
391 | // can only clear it (and reset the indices) when there are no unresolved |
392 | // forward refs remaining. |
393 | int next_forward_ref_id_ = 0; |
394 | int unresolved_forward_refs_ = 0; |
395 | IdentityMap<PendingObjectReferences, base::DefaultAllocationPolicy> |
396 | forward_refs_per_pending_object_; |
397 | |
398 | // Used to keep track of the off-heap backing stores used by TypedArrays/ |
399 | // ArrayBuffers. Note that the index begins at 1 and not 0, because when a |
400 | // TypedArray has an on-heap backing store, the backing_store pointer in the |
401 | // corresponding ArrayBuffer will be null, which makes it indistinguishable |
402 | // from index 0. |
403 | uint32_t seen_backing_stores_index_ = 1; |
404 | |
405 | int recursion_depth_ = 0; |
406 | const Snapshot::SerializerFlags flags_; |
407 | |
408 | size_t allocation_size_[kNumberOfSnapshotSpaces] = {0}; |
409 | #ifdef OBJECT_PRINT1 |
410 | static constexpr int kInstanceTypes = LAST_TYPE + 1; |
411 | std::unique_ptr<int[]> instance_type_count_[kNumberOfSnapshotSpaces]; |
412 | std::unique_ptr<size_t[]> instance_type_size_[kNumberOfSnapshotSpaces]; |
413 | #endif // OBJECT_PRINT |
414 | |
415 | #ifdef DEBUG |
416 | GlobalHandleVector<HeapObject> back_refs_; |
417 | GlobalHandleVector<HeapObject> stack_; |
418 | #endif // DEBUG |
419 | }; |
420 | |
421 | class RelocInfoIterator; |
422 | |
423 | class Serializer::ObjectSerializer : public ObjectVisitor { |
424 | public: |
425 | ObjectSerializer(Serializer* serializer, Handle<HeapObject> obj, |
426 | SnapshotByteSink* sink) |
427 | : isolate_(serializer->isolate()), |
428 | serializer_(serializer), |
429 | object_(obj), |
430 | sink_(sink), |
431 | bytes_processed_so_far_(0) { |
432 | #ifdef DEBUG |
433 | serializer_->PushStack(obj); |
434 | #endif // DEBUG |
435 | } |
436 | ~ObjectSerializer() override { |
437 | #ifdef DEBUG |
438 | serializer_->PopStack(); |
439 | #endif // DEBUG |
440 | } |
441 | void Serialize(); |
442 | void SerializeObject(); |
443 | void SerializeDeferred(); |
444 | void VisitPointers(HeapObject host, ObjectSlot start, |
445 | ObjectSlot end) override; |
446 | void VisitPointers(HeapObject host, MaybeObjectSlot start, |
447 | MaybeObjectSlot end) override; |
448 | void VisitCodePointer(HeapObject host, CodeObjectSlot slot) override; |
449 | void VisitEmbeddedPointer(Code host, RelocInfo* target) override; |
450 | void VisitExternalReference(Foreign host, Address* p) override; |
451 | void VisitExternalReference(Code host, RelocInfo* rinfo) override; |
452 | void VisitExternalPointer(HeapObject host, ExternalPointer_t ptr) override; |
453 | void VisitInternalReference(Code host, RelocInfo* rinfo) override; |
454 | void VisitCodeTarget(Code host, RelocInfo* target) override; |
455 | void VisitRuntimeEntry(Code host, RelocInfo* reloc) override; |
456 | void VisitOffHeapTarget(Code host, RelocInfo* target) override; |
457 | |
458 | Isolate* isolate() { return isolate_; } |
459 | |
460 | private: |
461 | class RelocInfoObjectPreSerializer; |
462 | |
463 | void SerializePrologue(SnapshotSpace space, int size, Map map); |
464 | |
465 | // This function outputs or skips the raw data between the last pointer and |
466 | // up to the current position. |
467 | void SerializeContent(Map map, int size); |
468 | void OutputExternalReference(Address target, int target_size, bool sandboxify, |
469 | ExternalPointerTag tag); |
470 | void OutputRawData(Address up_to); |
471 | void SerializeCode(Map map, int size); |
472 | uint32_t SerializeBackingStore(void* backing_store, int32_t byte_length, |
473 | Maybe<int32_t> max_byte_length); |
474 | void SerializeJSTypedArray(); |
475 | void SerializeJSArrayBuffer(); |
476 | void SerializeExternalString(); |
477 | void SerializeExternalStringAsSequentialString(); |
478 | |
479 | Isolate* isolate_; |
480 | Serializer* serializer_; |
481 | Handle<HeapObject> object_; |
482 | SnapshotByteSink* sink_; |
483 | int bytes_processed_so_far_; |
484 | }; |
485 | |
486 | } // namespace internal |
487 | } // namespace v8 |
488 | |
489 | #endif // V8_SNAPSHOT_SERIALIZER_H_ |