// 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. #ifndef V8_WEB_SNAPSHOT_WEB_SNAPSHOT_H_ #define V8_WEB_SNAPSHOT_WEB_SNAPSHOT_H_ #include #include "src/handles/handles.h" #include "src/objects/value-serializer.h" #include "src/snapshot/serializer.h" // For ObjectCacheIndexMap namespace v8 { class Context; class Isolate; template class Local; namespace internal { class Context; class Map; class Object; class String; struct WebSnapshotData : public std::enable_shared_from_this { uint8_t* buffer = nullptr; size_t buffer_size = 0; WebSnapshotData() = default; WebSnapshotData(const WebSnapshotData&) = delete; WebSnapshotData& operator=(const WebSnapshotData&) = delete; ~WebSnapshotData() { free(buffer); } }; class WebSnapshotSerializerDeserializer { public: inline bool has_error() const { return error_message_ != nullptr; } const char* error_message() const { return error_message_; } enum ValueType : uint8_t { FALSE_CONSTANT, TRUE_CONSTANT, NULL_CONSTANT, UNDEFINED_CONSTANT, INTEGER, DOUBLE, STRING_ID, ARRAY_ID, OBJECT_ID, FUNCTION_ID, CLASS_ID, REGEXP, EXTERNAL_ID, IN_PLACE_STRING_ID }; static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'}; enum ContextType : uint8_t { FUNCTION, BLOCK }; enum PropertyAttributesType : uint8_t { DEFAULT, CUSTOM }; uint32_t FunctionKindToFunctionFlags(FunctionKind kind); FunctionKind FunctionFlagsToFunctionKind(uint32_t flags); bool IsFunctionOrMethod(uint32_t flags); bool IsConstructor(uint32_t flags); uint32_t GetDefaultAttributeFlags(); uint32_t AttributesToFlags(PropertyDetails details); PropertyAttributes FlagsToAttributes(uint32_t flags); // The maximum count of items for each value type (strings, objects etc.) static constexpr uint32_t kMaxItemCount = static_cast(FixedArray::kMaxLength - 1); // This ensures indices and lengths can be converted between uint32_t and int // without problems: STATIC_ASSERT(kMaxItemCount < std::numeric_limits::max()); protected: explicit WebSnapshotSerializerDeserializer(Isolate* isolate) : isolate_(isolate) {} // Not virtual, on purpose (because it doesn't need to be). void Throw(const char* message); inline Factory* factory() const { return isolate_->factory(); } Isolate* isolate_; const char* error_message_ = nullptr; private: WebSnapshotSerializerDeserializer(const WebSnapshotSerializerDeserializer&) = delete; WebSnapshotSerializerDeserializer& operator=( const WebSnapshotSerializerDeserializer&) = delete; // Keep most common function kinds in the 7 least significant bits to make the // flags fit in 1 byte. using AsyncFunctionBitField = base::BitField; using GeneratorFunctionBitField = AsyncFunctionBitField::Next; using ArrowFunctionBitField = GeneratorFunctionBitField::Next; using MethodBitField = ArrowFunctionBitField::Next; using StaticBitField = MethodBitField::Next; using ClassConstructorBitField = StaticBitField::Next; using DefaultConstructorBitField = ClassConstructorBitField::Next; using DerivedConstructorBitField = DefaultConstructorBitField::Next; using ReadOnlyBitField = base::BitField; using ConfigurableBitField = ReadOnlyBitField::Next; using EnumerableBitField = ConfigurableBitField::Next; }; class V8_EXPORT WebSnapshotSerializer : public WebSnapshotSerializerDeserializer { public: explicit WebSnapshotSerializer(v8::Isolate* isolate); explicit WebSnapshotSerializer(Isolate* isolate); ~WebSnapshotSerializer(); bool TakeSnapshot(v8::Local context, v8::Local exports, WebSnapshotData& data_out); bool TakeSnapshot(Handle object, MaybeHandle block_list, WebSnapshotData& data_out); // For inspecting the state after taking a snapshot. uint32_t string_count() const { return static_cast(string_ids_.size()); } uint32_t map_count() const { return static_cast(map_ids_.size()); } uint32_t context_count() const { return static_cast(context_ids_.size()); } uint32_t function_count() const { return static_cast(function_ids_.size()); } uint32_t class_count() const { return static_cast(class_ids_.size()); } uint32_t array_count() const { return static_cast(array_ids_.size()); } uint32_t object_count() const { return static_cast(object_ids_.size()); } uint32_t external_objects_count() const { return static_cast(external_objects_ids_.size()); } Handle GetExternals(); private: WebSnapshotSerializer(const WebSnapshotSerializer&) = delete; WebSnapshotSerializer& operator=(const WebSnapshotSerializer&) = delete; enum class AllowInPlace { No, // This reference cannot be replace with an in-place item. Yes, // This reference can be replaced with an in-place item. }; void SerializePendingItems(); void WriteSnapshot(uint8_t*& buffer, size_t& buffer_size); void WriteObjects(ValueSerializer& destination, size_t count, ValueSerializer& source, const char* name); // Returns true if the object was already in the map, false if it was added. bool InsertIntoIndexMap(ObjectCacheIndexMap& map, HeapObject heap_object, uint32_t& id); void ShallowDiscoverExternals(FixedArray externals); void Discover(Handle object); void DiscoverString(Handle string, AllowInPlace can_be_in_place = AllowInPlace::No); void DiscoverMap(Handle map); void DiscoverFunction(Handle function); void DiscoverClass(Handle function); void DiscoverContextAndPrototype(Handle function); void DiscoverContext(Handle context); void DiscoverArray(Handle array); void DiscoverObject(Handle object); void DiscoverSource(Handle function); void ConstructSource(); void SerializeFunctionInfo(ValueSerializer* serializer, Handle function); void SerializeString(Handle string, ValueSerializer& serializer); void SerializeMap(Handle map); void SerializeFunction(Handle function); void SerializeClass(Handle function); void SerializeContext(Handle context); void SerializeArray(Handle array); void SerializeObject(Handle object); void SerializeExport(Handle object, Handle export_name); void WriteValue(Handle object, ValueSerializer& serializer); void WriteStringMaybeInPlace(Handle string, ValueSerializer& serializer); void WriteStringId(Handle string, ValueSerializer& serializer); uint32_t GetStringId(Handle string, bool& in_place); uint32_t GetMapId(Map map); uint32_t GetFunctionId(JSFunction function); uint32_t GetClassId(JSFunction function); uint32_t GetContextId(Context context); uint32_t GetArrayId(JSArray array); uint32_t GetObjectId(JSObject object); uint32_t GetExternalId(HeapObject object); ValueSerializer string_serializer_; ValueSerializer map_serializer_; ValueSerializer context_serializer_; ValueSerializer function_serializer_; ValueSerializer class_serializer_; ValueSerializer array_serializer_; ValueSerializer object_serializer_; ValueSerializer export_serializer_; // These are needed for being able to serialize items in order. Handle contexts_; Handle functions_; Handle classes_; Handle arrays_; Handle objects_; Handle strings_; Handle maps_; // IndexMap to keep track of explicitly blocked external objects and // non-serializable/not-supported objects (e.g. API Objects). ObjectCacheIndexMap external_objects_ids_; // ObjectCacheIndexMap implements fast lookup item -> id. Some items (context, // function, class, array, object) can point to other items and we serialize // them in the reverse order. This ensures that the items this item points to // have a lower ID and will be deserialized first. ObjectCacheIndexMap string_ids_; ObjectCacheIndexMap map_ids_; ObjectCacheIndexMap context_ids_; ObjectCacheIndexMap function_ids_; ObjectCacheIndexMap class_ids_; ObjectCacheIndexMap array_ids_; ObjectCacheIndexMap object_ids_; uint32_t export_count_ = 0; std::queue> discovery_queue_; // For keeping track of which strings have exactly one reference. Strings are // inserted here when the first reference is discovered, and never removed. // Strings which have more than one reference get an ID and are inserted to // strings_. IdentityMap all_strings_; // For constructing the minimal, "compacted", source string to cover all // function bodies. Handle full_source_; uint32_t source_id_; // Ordered set of (start, end) pairs of all functions we've discovered. std::set> source_intervals_; // Maps function positions in the real source code into the function positions // in the constructed source code (which we'll include in the web snapshot). std::unordered_map source_offset_to_compacted_source_offset_; }; class V8_EXPORT WebSnapshotDeserializer : public WebSnapshotSerializerDeserializer { public: WebSnapshotDeserializer(v8::Isolate* v8_isolate, const uint8_t* data, size_t buffer_size); WebSnapshotDeserializer(Isolate* isolate, Handle