// 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/bigint.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, // It corresponds to the hole value. NO_ELEMENT_CONSTANT, INTEGER, DOUBLE, REGEXP, STRING_ID, ARRAY_ID, OBJECT_ID, FUNCTION_ID, CLASS_ID, SYMBOL_ID, EXTERNAL_ID, BUILTIN_OBJECT_ID, IN_PLACE_STRING_ID, ARRAY_BUFFER_ID, TYPED_ARRAY_ID, DATA_VIEW_ID, BIGINT_ID }; enum SymbolType : uint8_t { kNonGlobalNoDesription = 0, kNonGlobal = 1, kGlobal = 2 }; enum ElementsType : uint8_t { kDense = 0, kSparse = 1 }; enum TypedArrayType : uint8_t { kInt8Array, kUint8Array, kUint8ClampedArray, kInt16Array, kUint16Array, kInt32Array, kUint32Array, kFloat32Array, kFloat64Array, kBigInt64Array, kBigUint64Array, }; static inline ExternalArrayType TypedArrayTypeToExternalArrayType( TypedArrayType type); static inline TypedArrayType ExternalArrayTypeToTypedArrayType( ExternalArrayType type); static constexpr uint8_t kMagicNumber[4] = {'+', '+', '+', ';'}; enum ContextType : uint8_t { FUNCTION, BLOCK }; enum PropertyAttributesType : uint8_t { DEFAULT, CUSTOM }; uint8_t FunctionKindToFunctionFlags(FunctionKind kind); FunctionKind FunctionFlagsToFunctionKind(uint8_t flags); bool IsFunctionOrMethod(uint8_t flags); bool IsConstructor(uint8_t flags); uint8_t GetDefaultAttributeFlags(); uint8_t AttributesToFlags(PropertyDetails details); PropertyAttributes FlagsToAttributes(uint8_t flags); uint8_t ArrayBufferViewKindToFlags( Handle array_buffer_view); uint8_t ArrayBufferKindToFlags(Handle array_buffer); uint32_t BigIntSignAndLengthToFlags(Handle bigint); uint32_t BigIntFlagsToBitField(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 < static_cast(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); void IterateBuiltinObjects( std::function, Handle)> func); static constexpr int kBuiltinObjectCount = 12; inline Factory* factory() const { return isolate_->factory(); } Isolate* isolate_; const char* error_message_ = nullptr; // Encode JSArrayBufferFlags, including was_detached, is_shared, is_resizable. // DetachedBitField indicates whether the ArrayBuffer was detached. using DetachedBitField = base::BitField; // SharedBitField indicates whether the ArrayBuffer is SharedArrayBuffer. using SharedBitField = DetachedBitField::Next; // ResizableBitField indicates whether the ArrayBuffer is ResizableArrayBuffer // or GrowableSharedArrayBuffer. using ResizableBitField = SharedBitField::Next; // Encode JSArrayBufferViewFlags, including is_length_tracking, see // https://github.com/tc39/proposal-resizablearraybuffer. // LengthTrackingBitField indicates whether the ArrayBufferView should track // the length of the backing buffer, that is whether the ArrayBufferView is // constructed without the specified length argument. using LengthTrackingBitField = base::BitField; // Encode BigInt's sign and digits length. using BigIntSignBitField = base::BitField; using BigIntLengthBitField = BigIntSignBitField::Next; static_assert(BigIntLengthBitField::kSize == BigInt::LengthBits::kSize); private: WebSnapshotSerializerDeserializer(const WebSnapshotSerializerDeserializer&) = delete; WebSnapshotSerializerDeserializer& operator=( const WebSnapshotSerializerDeserializer&) = delete; 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 symbol_count() const { return static_cast(symbol_ids_.size()); } uint32_t bigint_count() const { return static_cast(bigint_ids_.size()); } uint32_t map_count() const { return static_cast(map_ids_.size()); } uint32_t builtin_object_count() const { return static_cast(builtin_object_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 array_buffer_count() const { return static_cast(array_buffer_ids_.size()); } uint32_t typed_array_count() const { return static_cast(typed_array_ids_.size()); } uint32_t data_view_count() const { return static_cast(data_view_ids_.size()); } uint32_t object_count() const { return static_cast(object_ids_.size()); } uint32_t external_object_count() const { return static_cast(external_object_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 ShallowDiscoverBuiltinObjects(v8::Local context); void Discover(Handle object); void DiscoverString(Handle string, AllowInPlace can_be_in_place = AllowInPlace::No); void DiscoverSymbol(Handle symbol); void DiscoverBigInt(Handle bigint); void DiscoverMap(Handle map, bool allow_property_in_descriptor = false); void DiscoverPropertyKey(Handle key); void DiscoverMapForFunction(Handle function); void DiscoverFunction(Handle function); void DiscoverClass(Handle function); void DiscoverContextAndPrototype(Handle function); void DiscoverContext(Handle context); void DiscoverArray(Handle array); void DiscoverTypedArray(Handle typed_array); void DiscoverDataView(Handle data_view); void DiscoverArrayBuffer(Handle array_buffer); void DiscoverElements(Handle object); void DiscoverObject(Handle object); bool DiscoverIfBuiltinObject(Handle object); void DiscoverSource(Handle function); template void DiscoverObjectPropertiesWithDictionaryMap(T dict); bool ShouldBeSerialized(Handle key); void ConstructSource(); void SerializeFunctionInfo(Handle function, ValueSerializer& serializer); void SerializeFunctionProperties(Handle function, ValueSerializer& serializer); void SerializeString(Handle string, ValueSerializer& serializer); void SerializeSymbol(Handle symbol); void SerializeBigInt(Handle bigint); void SerializeMap(Handle map); void SerializeBuiltinObject(uint32_t name_id); void SerializeObjectPrototype(Handle map, ValueSerializer& serializer); template void SerializeObjectPropertiesWithDictionaryMap(T dict); void SerializeFunction(Handle function); void SerializeClass(Handle function); void SerializeContext(Handle context, uint32_t id); void SerializeArray(Handle array); void SerializeElements(Handle object, ValueSerializer& serializer, Maybe length); void SerializeObject(Handle object); void SerializeArrayBufferView(Handle array_buffer_view, ValueSerializer& serializer); void SerializeArrayBuffer(Handle array_buffer); void SerializeTypedArray(Handle typed_array); void SerializeDataView(Handle data_view); 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 GetSymbolId(Symbol symbol); uint32_t GetBigIntId(BigInt bigint); 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 GetTypedArrayId(JSTypedArray typed_array); uint32_t GetDataViewId(JSDataView data_view); uint32_t GetArrayBufferId(JSArrayBuffer array_buffer); uint32_t GetObjectId(JSObject object); bool GetExternalId(HeapObject object, uint32_t* id = nullptr); // Returns index into builtin_object_name_strings_. bool GetBuiltinObjectNameIndex(HeapObject object, uint32_t& index); bool GetBuiltinObjectId(HeapObject object, uint32_t& id); ValueSerializer string_serializer_; ValueSerializer symbol_serializer_; ValueSerializer bigint_serializer_; ValueSerializer map_serializer_; ValueSerializer builtin_object_serializer_; ValueSerializer context_serializer_; ValueSerializer function_serializer_; ValueSerializer class_serializer_; ValueSerializer array_serializer_; ValueSerializer typed_array_serializer_; ValueSerializer array_buffer_serializer_; ValueSerializer data_view_serializer_; ValueSerializer object_serializer_; ValueSerializer export_serializer_; // These are needed for being able to serialize items in order. Handle strings_; Handle symbols_; Handle bigints_; Handle maps_; Handle contexts_; Handle functions_; Handle classes_; Handle arrays_; Handle typed_arrays_; Handle array_buffers_; Handle data_views_; Handle objects_; // IndexMap to keep track of explicitly blocked external objects and // non-serializable/not-supported objects (e.g. API Objects). ObjectCacheIndexMap external_object_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 symbol_ids_; ObjectCacheIndexMap bigint_ids_; ObjectCacheIndexMap map_ids_; ObjectCacheIndexMap context_ids_; ObjectCacheIndexMap function_ids_; ObjectCacheIndexMap class_ids_; ObjectCacheIndexMap array_ids_; ObjectCacheIndexMap typed_array_ids_; ObjectCacheIndexMap array_buffer_ids_; ObjectCacheIndexMap data_view_ids_; ObjectCacheIndexMap object_ids_; uint32_t export_count_ = 0; // For handling references to builtin objects: // -------------------------------- // String objects for the names of all known builtins. Handle builtin_object_name_strings_; // Map object -> index in builtin_name_strings_ for all known builtins. ObjectCacheIndexMap builtin_object_to_name_; // Map object -> index in builtins_. Includes only builtins which will be // incluced in the snapshot. ObjectCacheIndexMap builtin_object_ids_; // For creating the Builtin wrappers in the snapshot. Includes only builtins // which will be incluced in the snapshot. Each element is the id of the // builtin name string in the snapshot. std::vector builtin_objects_; // -------------------------------- 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. // -------------------------------- // Script id -> offset of the script source code in full_source_. std::map script_offsets_; 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