// Copyright 2021 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/web-snapshot/web-snapshot.h" #include #include "include/v8-isolate.h" #include "include/v8-local-handle.h" #include "include/v8-object.h" #include "include/v8-primitive.h" #include "include/v8-script.h" #include "src/api/api-inl.h" #include "src/handles/handles.h" #include "src/logging/runtime-call-stats-scope.h" #include "src/objects/bigint.h" #include "src/objects/contexts-inl.h" #include "src/objects/js-array-buffer-inl.h" #include "src/objects/js-regexp-inl.h" #include "src/objects/script.h" namespace v8 { namespace internal { constexpr uint8_t WebSnapshotSerializerDeserializer::kMagicNumber[4]; constexpr int WebSnapshotSerializerDeserializer::kBuiltinObjectCount; // When encountering an error during deserializing, we note down the error but // don't bail out from processing the snapshot further. This is to speed up // deserialization; the error case is now slower since we don't bail out, but // the non-error case is faster, since we don't repeatedly check for errors. // (Invariant: we might fill our internal data structures with arbitrary data, // but it shouldn't have an observable effect.) // This doesn't increase the complexity of processing the data in a robust and // secure way. We cannot trust the data anyway, so every upcoming byte can have // an arbitrary value, not depending on whether or not we've encountered an // error before. void WebSnapshotSerializerDeserializer::Throw(const char* message) { if (error_message_ != nullptr) { return; } error_message_ = message; if (!isolate_->has_pending_exception()) { isolate_->Throw(*factory()->NewError( MessageTemplate::kWebSnapshotError, factory()->NewStringFromAsciiChecked(error_message_))); } } void WebSnapshotSerializerDeserializer::IterateBuiltinObjects( std::function, Handle)> func) { // TODO(v8:11525): Add more builtins. auto roots = ReadOnlyRoots(isolate_); func(handle(roots.Error_string(), isolate_), handle(isolate_->context().error_function(), isolate_)); func(factory()->NewStringFromAsciiChecked("Error.prototype"), handle(isolate_->context().error_function().instance_prototype(), isolate_)); func(handle(roots.Object_string(), isolate_), handle(isolate_->context().object_function(), isolate_)); func(factory()->NewStringFromAsciiChecked("Object.prototype"), handle(isolate_->context().initial_object_prototype(), isolate_)); func(handle(roots.Function_string(), isolate_), handle(isolate_->context().function_function(), isolate_)); func(factory()->NewStringFromAsciiChecked("Function.prototype"), handle(isolate_->context().function_prototype(), isolate_)); // TODO(v8:11525): There are no obvious names for these, since AsyncFunction // etc are not properties of the global object. func(factory()->NewStringFromAsciiChecked("AsyncFunction"), handle(isolate_->context().async_function_constructor(), isolate_)); func( factory()->NewStringFromAsciiChecked("AsyncFunction"), handle( isolate_->context().async_function_constructor().instance_prototype(), isolate_)); auto generator_function = handle(JSFunction::cast(isolate_->context() .generator_function_map() .constructor_or_back_pointer()), isolate_); func(factory()->NewStringFromAsciiChecked("GeneratorFunction"), generator_function); func(factory()->NewStringFromAsciiChecked("GeneratorFunction.prototype"), handle(generator_function->instance_prototype(), isolate_)); auto async_generator_function = handle(JSFunction::cast(isolate_->context() .async_generator_function_map() .constructor_or_back_pointer()), isolate_); func(factory()->NewStringFromAsciiChecked("AsyncGeneratorFunction"), async_generator_function); func(factory()->NewStringFromAsciiChecked("AsyncGeneratorFunction.prototype"), handle(async_generator_function->instance_prototype(), isolate_)); static_assert(kBuiltinObjectCount == 12); } uint8_t WebSnapshotSerializerDeserializer::FunctionKindToFunctionFlags( FunctionKind kind) { // TODO(v8:11525): Support more function kinds. switch (kind) { case FunctionKind::kNormalFunction: case FunctionKind::kArrowFunction: case FunctionKind::kGeneratorFunction: case FunctionKind::kAsyncFunction: case FunctionKind::kAsyncArrowFunction: case FunctionKind::kAsyncGeneratorFunction: case FunctionKind::kBaseConstructor: case FunctionKind::kDefaultBaseConstructor: case FunctionKind::kDerivedConstructor: case FunctionKind::kDefaultDerivedConstructor: case FunctionKind::kConciseMethod: case FunctionKind::kAsyncConciseMethod: case FunctionKind::kStaticConciseMethod: case FunctionKind::kStaticAsyncConciseMethod: case FunctionKind::kStaticConciseGeneratorMethod: case FunctionKind::kStaticAsyncConciseGeneratorMethod: break; default: Throw("Unsupported function kind"); } auto flags = AsyncFunctionBitField::encode(IsAsyncFunction(kind)) | GeneratorFunctionBitField::encode(IsGeneratorFunction(kind)) | ArrowFunctionBitField::encode(IsArrowFunction(kind)) | MethodBitField::encode(IsConciseMethod(kind)) | StaticBitField::encode(IsStatic(kind)) | ClassConstructorBitField::encode(IsClassConstructor(kind)) | DefaultConstructorBitField::encode(IsDefaultConstructor(kind)) | DerivedConstructorBitField::encode(IsDerivedConstructor(kind)); return flags; } // TODO(v8:11525): Optionally, use an enum instead. FunctionKind WebSnapshotSerializerDeserializer::FunctionFlagsToFunctionKind( uint8_t flags) { FunctionKind kind; if (IsFunctionOrMethod(flags)) { if (ArrowFunctionBitField::decode(flags) && MethodBitField::decode(flags)) { kind = FunctionKind::kInvalid; } else { uint32_t index = AsyncFunctionBitField::decode(flags) << 0 | GeneratorFunctionBitField::decode(flags) << 1 | (ArrowFunctionBitField::decode(flags) || StaticBitField::decode(flags)) << 2 | MethodBitField::decode(flags) << 3; static const FunctionKind kFunctionKinds[] = { // kNormalFunction // is_generator = false FunctionKind::kNormalFunction, // is_async = false FunctionKind::kAsyncFunction, // is_async = true // is_generator = true FunctionKind::kGeneratorFunction, // is_async = false FunctionKind::kAsyncGeneratorFunction, // is_async = true // kArrowFunction // is_generator = false FunctionKind::kArrowFunction, // is_async = false FunctionKind::kAsyncArrowFunction, // is_async = true // is_generator = true FunctionKind::kInvalid, // is_async = false FunctionKind::kInvalid, // is_async = true // kNonStaticMethod // is_generator = false FunctionKind::kConciseMethod, // is_async = false FunctionKind::kAsyncConciseMethod, // is_async = true // is_generator = true // TODO(v8::11525) Support FunctionKind::kConciseGeneratorMethod. FunctionKind::kInvalid, // is_async = false // TODO(v8::11525) Support FunctionKind::kAsyncConciseGeneratorMethod. FunctionKind::kInvalid, // is_async = true // kStaticMethod // is_generator = false FunctionKind::kStaticConciseMethod, // is_async = false FunctionKind::kStaticAsyncConciseMethod, // is_async = true // is_generator = true FunctionKind::kStaticConciseGeneratorMethod, // is_async = false FunctionKind::kStaticAsyncConciseGeneratorMethod // is_async = true }; kind = kFunctionKinds[index]; } } else if (IsConstructor(flags)) { static const FunctionKind kFunctionKinds[] = { // is_derived = false FunctionKind::kBaseConstructor, // is_default = false FunctionKind::kDefaultBaseConstructor, // is_default = true // is_derived = true FunctionKind::kDerivedConstructor, // is_default = false FunctionKind::kDefaultDerivedConstructor // is_default = true }; kind = kFunctionKinds[flags >> DefaultConstructorBitField::kShift]; } else { kind = FunctionKind::kInvalid; } if (kind == FunctionKind::kInvalid) { Throw("Invalid function flags\n"); } return kind; } bool WebSnapshotSerializerDeserializer::IsFunctionOrMethod(uint8_t flags) { uint32_t mask = AsyncFunctionBitField::kMask | GeneratorFunctionBitField::kMask | ArrowFunctionBitField::kMask | MethodBitField::kMask | StaticBitField::kMask; return (flags & mask) == flags; } bool WebSnapshotSerializerDeserializer::IsConstructor(uint8_t flags) { uint32_t mask = ClassConstructorBitField::kMask | DefaultConstructorBitField::kMask | DerivedConstructorBitField::kMask; return ClassConstructorBitField::decode(flags) && (flags & mask) == flags; } uint8_t WebSnapshotSerializerDeserializer::GetDefaultAttributeFlags() { auto flags = ReadOnlyBitField::encode(false) | ConfigurableBitField::encode(true) | EnumerableBitField::encode(true); return flags; } uint8_t WebSnapshotSerializerDeserializer::AttributesToFlags( PropertyDetails details) { auto flags = ReadOnlyBitField::encode(details.IsReadOnly()) | ConfigurableBitField::encode(details.IsConfigurable()) | EnumerableBitField::encode(details.IsEnumerable()); return flags; } PropertyAttributes WebSnapshotSerializerDeserializer::FlagsToAttributes( uint8_t flags) { int attributes = ReadOnlyBitField::decode(flags) * READ_ONLY + !ConfigurableBitField::decode(flags) * DONT_DELETE + !EnumerableBitField::decode(flags) * DONT_ENUM; return PropertyAttributesFromInt(attributes); } WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate) : WebSnapshotSerializer(reinterpret_cast(isolate)) { } WebSnapshotSerializer::WebSnapshotSerializer(Isolate* isolate) : WebSnapshotSerializerDeserializer(isolate), string_serializer_(isolate_, nullptr), symbol_serializer_(isolate_, nullptr), bigint_serializer_(isolate_, nullptr), map_serializer_(isolate_, nullptr), builtin_object_serializer_(isolate_, nullptr), context_serializer_(isolate_, nullptr), function_serializer_(isolate_, nullptr), class_serializer_(isolate_, nullptr), array_serializer_(isolate_, nullptr), typed_array_serializer_(isolate_, nullptr), array_buffer_serializer_(isolate_, nullptr), data_view_serializer_(isolate_, nullptr), object_serializer_(isolate_, nullptr), export_serializer_(isolate_, nullptr), external_object_ids_(isolate_->heap()), string_ids_(isolate_->heap()), symbol_ids_(isolate_->heap()), bigint_ids_(isolate_->heap()), map_ids_(isolate_->heap()), context_ids_(isolate_->heap()), function_ids_(isolate_->heap()), class_ids_(isolate_->heap()), array_ids_(isolate_->heap()), typed_array_ids_(isolate->heap()), array_buffer_ids_(isolate->heap()), data_view_ids_(isolate->heap()), object_ids_(isolate_->heap()), builtin_object_to_name_(isolate_->heap()), builtin_object_ids_(isolate_->heap()), all_strings_(isolate_->heap()) { auto empty_array_list = factory()->empty_array_list(); strings_ = empty_array_list; symbols_ = empty_array_list; bigints_ = empty_array_list; maps_ = empty_array_list; contexts_ = empty_array_list; functions_ = empty_array_list; classes_ = empty_array_list; arrays_ = empty_array_list; array_buffers_ = empty_array_list; typed_arrays_ = empty_array_list; data_views_ = empty_array_list; objects_ = empty_array_list; } WebSnapshotSerializer::~WebSnapshotSerializer() {} bool WebSnapshotSerializer::TakeSnapshot( Handle object, MaybeHandle maybe_externals, WebSnapshotData& data_out) { if (string_ids_.size() > 0) { Throw("Can't reuse WebSnapshotSerializer"); return false; } if (!maybe_externals.is_null()) { ShallowDiscoverExternals(*maybe_externals.ToHandleChecked()); } v8::Local context = reinterpret_cast(isolate_)->GetCurrentContext(); ShallowDiscoverBuiltinObjects(context); if (object->IsHeapObject()) Discover(Handle::cast(object)); ConstructSource(); // The export is serialized with the empty string as name; we need to // "discover" the name here. DiscoverString(factory()->empty_string()); SerializeExport(object, factory()->empty_string()); WriteSnapshot(data_out.buffer, data_out.buffer_size); if (has_error()) { isolate_->ReportPendingMessages(); return false; } return true; } bool WebSnapshotSerializer::TakeSnapshot(v8::Local context, v8::Local exports, WebSnapshotData& data_out) { if (string_ids_.size() > 0) { Throw("Can't reuse WebSnapshotSerializer"); return false; } v8::Isolate* v8_isolate = reinterpret_cast(isolate_); ShallowDiscoverBuiltinObjects(context); Handle export_objects = isolate_->factory()->NewFixedArray(exports->Length()); for (int i = 0, length = exports->Length(); i < length; ++i) { v8::Local str = exports->Get(v8_isolate, i)->ToString(context).ToLocalChecked(); if (str->Length() == 0) { continue; } // Discover the export name. DiscoverString(Handle::cast(Utils::OpenHandle(*str))); v8::ScriptCompiler::Source source(str); auto script = ScriptCompiler::Compile(context, &source).ToLocalChecked(); v8::MaybeLocal script_result = script->Run(context); v8::Local v8_object; if (script_result.IsEmpty() || !script_result.ToLocalChecked()->ToObject(context).ToLocal( &v8_object)) { Throw("Exported object not found"); return false; } auto object = Handle::cast(Utils::OpenHandle(*v8_object)); export_objects->set(i, *object); Discover(object); // The error messages will be confusing if we continue running code when // already in the error state. if (has_error()) { isolate_->ReportPendingMessages(); return false; } } ConstructSource(); for (int i = 0, length = exports->Length(); i < length; ++i) { v8::Local str = exports->Get(v8_isolate, i)->ToString(context).ToLocalChecked(); if (str->Length() == 0) { continue; } SerializeExport(handle(export_objects->get(i), isolate_), Handle::cast(Utils::OpenHandle(*str))); } WriteSnapshot(data_out.buffer, data_out.buffer_size); if (has_error()) { isolate_->ReportPendingMessages(); return false; } return true; } void WebSnapshotSerializer::SerializePendingItems() { // The information about string reference counts is now complete. The strings // in strings_ are not in place and can be serialized now. The in-place // strings will be serialized as part of their respective objects. for (int i = 0; i < strings_->Length(); ++i) { Handle string = handle(String::cast(strings_->Get(i)), isolate_); SerializeString(string, string_serializer_); } for (int i = 0; i < symbols_->Length(); ++i) { Handle symbol = handle(Symbol::cast(symbols_->Get(i)), isolate_); SerializeSymbol(symbol); } for (int i = 0; i < bigints_->Length(); ++i) { Handle bigint = handle(BigInt::cast(bigints_->Get(i)), isolate_); SerializeBigInt(bigint); } for (int i = 0; i < maps_->Length(); ++i) { Handle map = handle(Map::cast(maps_->Get(i)), isolate_); SerializeMap(map); } for (auto name_id : builtin_objects_) { SerializeBuiltinObject(name_id); } for (int i = 0; i < contexts_->Length(); ++i) { Handle context = handle(Context::cast(contexts_->Get(i)), isolate_); SerializeContext(context, static_cast(i)); } // Serialize the items in the reverse order. The items at the end of the // functions_ etc get lower IDs and vice versa. IDs which items use for // referring to each other are reversed by GetId() functions. for (int i = functions_->Length() - 1; i >= 0; --i) { Handle function = handle(JSFunction::cast(functions_->Get(i)), isolate_); SerializeFunction(function); } for (int i = classes_->Length() - 1; i >= 0; --i) { Handle function = handle(JSFunction::cast(classes_->Get(i)), isolate_); SerializeClass(function); } for (int i = arrays_->Length() - 1; i >= 0; --i) { Handle array = handle(JSArray::cast(arrays_->Get(i)), isolate_); SerializeArray(array); } for (int i = array_buffers_->Length() - 1; i >= 0; --i) { Handle array_buffer = handle(JSArrayBuffer::cast(array_buffers_->Get(i)), isolate_); SerializeArrayBuffer(array_buffer); } for (int i = typed_arrays_->Length() - 1; i >= 0; --i) { Handle typed_array = handle(JSTypedArray::cast(typed_arrays_->Get(i)), isolate_); SerializeTypedArray(typed_array); } for (int i = data_views_->Length() - 1; i >= 0; --i) { Handle data_view = handle(JSDataView::cast(data_views_->Get(i)), isolate_); SerializeDataView(data_view); } for (int i = objects_->Length() - 1; i >= 0; --i) { Handle object = handle(JSObject::cast(objects_->Get(i)), isolate_); SerializeObject(object); } } // Format (full snapshot): // - Magic number (4 bytes) // - String count // - For each string: // - Serialized string // - Symbol count // - For each symbol: // - Serialized symbol // - Builtin object count // - For each builtin object: // - Id of the builtin object name string // - Shape count // - For each shape: // - Serialized shape // - Context count // - For each context: // - Serialized context // - Function count // - For each function: // - Serialized function // - Object count // - For each object: // - Serialized object // - Export count // - For each export: // - Serialized export void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer, size_t& buffer_size) { if (has_error()) { return; } SerializePendingItems(); ValueSerializer total_serializer(isolate_, nullptr); size_t needed_size = sizeof(kMagicNumber) + string_serializer_.buffer_size_ + symbol_serializer_.buffer_size_ + bigint_serializer_.buffer_size_ + builtin_object_serializer_.buffer_size_ + map_serializer_.buffer_size_ + context_serializer_.buffer_size_ + function_serializer_.buffer_size_ + class_serializer_.buffer_size_ + array_serializer_.buffer_size_ + array_buffer_serializer_.buffer_size_ + typed_array_serializer_.buffer_size_ + data_view_serializer_.buffer_size_ + object_serializer_.buffer_size_ + export_serializer_.buffer_size_ + 14 * sizeof(uint32_t); if (total_serializer.ExpandBuffer(needed_size).IsNothing()) { Throw("Out of memory"); return; } total_serializer.WriteRawBytes(kMagicNumber, 4); WriteObjects(total_serializer, string_count(), string_serializer_, "strings"); WriteObjects(total_serializer, symbol_count(), symbol_serializer_, "symbols"); WriteObjects(total_serializer, bigint_count(), bigint_serializer_, "bigints"); WriteObjects(total_serializer, builtin_object_count(), builtin_object_serializer_, "builtin_objects"); WriteObjects(total_serializer, map_count(), map_serializer_, "maps"); WriteObjects(total_serializer, context_count(), context_serializer_, "contexts"); WriteObjects(total_serializer, function_count(), function_serializer_, "functions"); WriteObjects(total_serializer, array_count(), array_serializer_, "arrays"); WriteObjects(total_serializer, array_buffer_count(), array_buffer_serializer_, "array buffers"); WriteObjects(total_serializer, typed_array_count(), typed_array_serializer_, "typed arrays"); WriteObjects(total_serializer, data_view_count(), data_view_serializer_, "data views"); WriteObjects(total_serializer, object_count(), object_serializer_, "objects"); WriteObjects(total_serializer, class_count(), class_serializer_, "classes"); WriteObjects(total_serializer, export_count_, export_serializer_, "exports"); if (has_error()) { return; } auto result = total_serializer.Release(); buffer = result.first; buffer_size = result.second; } void WebSnapshotSerializer::WriteObjects(ValueSerializer& destination, size_t count, ValueSerializer& source, const char* name) { if (count > std::numeric_limits::max()) { Throw("Too many objects"); return; } destination.WriteUint32(static_cast(count)); destination.WriteRawBytes(source.buffer_, source.buffer_size_); } bool WebSnapshotSerializer::InsertIntoIndexMap(ObjectCacheIndexMap& map, HeapObject heap_object, uint32_t& id) { DisallowGarbageCollection no_gc; int index_out; bool found = map.LookupOrInsert(heap_object, &index_out); id = static_cast(index_out); return found; } // Format: // - Length // - Raw bytes (data) void WebSnapshotSerializer::SerializeString(Handle string, ValueSerializer& serializer) { DisallowGarbageCollection no_gc; String::FlatContent flat = string->GetFlatContent(no_gc); DCHECK(flat.IsFlat()); if (flat.IsOneByte()) { base::Vector chars = flat.ToOneByteVector(); serializer.WriteUint32(chars.length()); serializer.WriteRawBytes(chars.begin(), chars.length() * sizeof(uint8_t)); } else if (flat.IsTwoByte()) { v8::Isolate* v8_isolate = reinterpret_cast(isolate_); v8::Local api_string = Utils::ToLocal(string); int length = api_string->Utf8Length(v8_isolate); std::unique_ptr buffer(new char[length]); api_string->WriteUtf8(v8_isolate, buffer.get(), length); serializer.WriteUint32(length); serializer.WriteRawBytes(buffer.get(), length * sizeof(uint8_t)); } else { UNREACHABLE(); } } // Format (serialized symbol): // - 0 if the symbol is non-global and there's no description, 1 if the symbol // is non-global and there is a description, 2 if the symbol is global (there // must be a description). void WebSnapshotSerializer::SerializeSymbol(Handle symbol) { if (symbol->description().IsUndefined()) { CHECK(!symbol->is_in_public_symbol_table()); symbol_serializer_.WriteUint32(SymbolType::kNonGlobalNoDesription); } else { symbol_serializer_.WriteUint32(symbol->is_in_public_symbol_table() ? SymbolType::kGlobal : SymbolType::kNonGlobal); WriteStringId(handle(String::cast(symbol->description()), isolate_), symbol_serializer_); } } // Format (serialized bigint) // - BigIntFlags, including sign and byte length. // - digit bytes. void WebSnapshotSerializer::SerializeBigInt(Handle bigint) { uint32_t flags = BigIntSignAndLengthToFlags(bigint); bigint_serializer_.WriteUint32(flags); int byte_length = BigIntLengthBitField::decode(flags); uint8_t* dest; if (bigint_serializer_.ReserveRawBytes(byte_length).To(&dest)) { bigint->SerializeDigits(dest); } else { Throw("Serialize BigInt failed"); return; } } bool WebSnapshotSerializer::ShouldBeSerialized(Handle key) { // Don't serialize class_positions_symbol property in Class. if (key->Equals(*factory()->class_positions_symbol())) { return false; } return true; } // Format (serialized shape): // - PropertyAttributesType // - Property count // - For each property // - Name: STRING_ID + String id or SYMBOL_ID + Symbol id or in-place string // - If the PropertyAttributesType is CUSTOM: attributes // - __proto__: Serialized value void WebSnapshotSerializer::SerializeMap(Handle map) { DCHECK(!map->is_dictionary_map()); int first_custom_index = -1; std::vector> keys; std::vector attributes; keys.reserve(map->NumberOfOwnDescriptors()); attributes.reserve(map->NumberOfOwnDescriptors()); for (InternalIndex i : map->IterateOwnDescriptors()) { PropertyDetails details = map->instance_descriptors(kRelaxedLoad).GetDetails(i); // If there are non-field properties in a map that doesn't allow them, i.e., // a non-function map, DiscoverMap has already thrown. if (details.location() != PropertyLocation::kField) { continue; } Handle key(map->instance_descriptors(kRelaxedLoad).GetKey(i), isolate_); if (!ShouldBeSerialized(key)) { continue; } keys.push_back(key); if (first_custom_index >= 0 || details.IsReadOnly() || !details.IsConfigurable() || details.IsDontEnum()) { if (first_custom_index == -1) first_custom_index = i.as_int(); attributes.push_back(AttributesToFlags(details)); } } map_serializer_.WriteUint32(first_custom_index == -1 ? PropertyAttributesType::DEFAULT : PropertyAttributesType::CUSTOM); map_serializer_.WriteUint32(static_cast(keys.size())); uint8_t default_flags = GetDefaultAttributeFlags(); for (size_t i = 0; i < keys.size(); ++i) { if (keys[i]->IsString()) { WriteStringMaybeInPlace(Handle::cast(keys[i]), map_serializer_); } else if (keys[i]->IsSymbol()) { map_serializer_.WriteByte(ValueType::SYMBOL_ID); map_serializer_.WriteUint32(GetSymbolId(Symbol::cast(*keys[i]))); } else { // This error should've been recognized in the discovery phase. CHECK(false); } if (first_custom_index >= 0) { if (static_cast(i) < first_custom_index) { map_serializer_.WriteByte(default_flags); } else { map_serializer_.WriteByte(attributes[i - first_custom_index]); } } } WriteValue(handle(map->prototype(), isolate_), map_serializer_); } void WebSnapshotSerializer::SerializeBuiltinObject(uint32_t name_id) { builtin_object_serializer_.WriteUint32(name_id); } // Construct the minimal source string to be included in the snapshot. Maintain // the "inner function is textually inside its outer function" relationship. // Example: // Input: // Full source: abcdefghijklmnopqrstuvwxyzåäö // Functions: 11111111 22222222 3 // Inner functions: 44 55 666 // Output: // Constructed source: defghijkstuvwxyzö // Functions: 11111111222222223 // Inner functions 44 55 666 void WebSnapshotSerializer::ConstructSource() { if (source_intervals_.empty()) { return; } Handle source_string = factory()->empty_string(); int current_interval_start = 0; int current_interval_end = 0; for (const auto& interval : source_intervals_) { DCHECK_LE(current_interval_start, interval.first); // Iterated in order. DCHECK_LE(interval.first, interval.second); if (interval.second <= current_interval_end) { // This interval is fully within the current interval. We don't need to // include any new source code, just record the position conversion. auto offset_within_parent = interval.first - current_interval_start; source_offset_to_compacted_source_offset_[interval.first] = source_offset_to_compacted_source_offset_[current_interval_start] + offset_within_parent; continue; } // Start a new interval. current_interval_start = interval.first; current_interval_end = interval.second; source_offset_to_compacted_source_offset_[current_interval_start] = source_string->length(); MaybeHandle new_source_string = factory()->NewConsString( source_string, factory()->NewSubString(full_source_, current_interval_start, current_interval_end)); if (!new_source_string.ToHandle(&source_string)) { Throw("Cannot construct source string"); return; } } DiscoverString(source_string); bool in_place = false; source_id_ = GetStringId(source_string, in_place); DCHECK(!in_place); } void WebSnapshotSerializer::SerializeFunctionProperties( Handle function, ValueSerializer& serializer) { Handle map(function->map(), isolate_); if (function->map() == isolate_->context().get(function->shared().function_map_index())) { serializer.WriteUint32(0); return; } else { serializer.WriteUint32(GetMapId(function->map()) + 1); } for (InternalIndex i : map->IterateOwnDescriptors()) { PropertyDetails details = map->instance_descriptors(kRelaxedLoad).GetDetails(i); if (details.location() == PropertyLocation::kDescriptor) { continue; } if (!ShouldBeSerialized( handle(map->instance_descriptors().GetKey(i), isolate_))) { continue; } FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); Handle value = JSObject::FastPropertyAt( isolate_, function, details.representation(), field_index); WriteValue(value, serializer); } } void WebSnapshotSerializer::SerializeFunctionInfo(Handle function, ValueSerializer& serializer) { if (!function->shared().HasSourceCode()) { Throw("Function without source code"); return; } { DisallowGarbageCollection no_gc; Context context = function->context(); if (context.IsNativeContext() || context.IsScriptContext()) { serializer.WriteUint32(0); } else { DCHECK(context.IsFunctionContext() || context.IsBlockContext()); uint32_t context_id = GetContextId(context); serializer.WriteUint32(context_id + 1); } } serializer.WriteUint32(source_id_); Handle