| 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_ |