blob: 4dc0f3f091dbe502e8cfc2c74e3c0fd101db3ed3 [file] [log] [blame]
// 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 <queue>
#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 <typename T>
class Local;
namespace internal {
class Context;
class Map;
class Object;
class String;
struct WebSnapshotData : public std::enable_shared_from_this<WebSnapshotData> {
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<uint32_t>(FixedArray::kMaxLength - 1);
// This ensures indices and lengths can be converted between uint32_t and int
// without problems:
STATIC_ASSERT(kMaxItemCount < std::numeric_limits<int32_t>::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<bool, 0, 1>;
using GeneratorFunctionBitField = AsyncFunctionBitField::Next<bool, 1>;
using ArrowFunctionBitField = GeneratorFunctionBitField::Next<bool, 1>;
using MethodBitField = ArrowFunctionBitField::Next<bool, 1>;
using StaticBitField = MethodBitField::Next<bool, 1>;
using ClassConstructorBitField = StaticBitField::Next<bool, 1>;
using DefaultConstructorBitField = ClassConstructorBitField::Next<bool, 1>;
using DerivedConstructorBitField = DefaultConstructorBitField::Next<bool, 1>;
using ReadOnlyBitField = base::BitField<bool, 0, 1>;
using ConfigurableBitField = ReadOnlyBitField::Next<bool, 1>;
using EnumerableBitField = ConfigurableBitField::Next<bool, 1>;
};
class V8_EXPORT WebSnapshotSerializer
: public WebSnapshotSerializerDeserializer {
public:
explicit WebSnapshotSerializer(v8::Isolate* isolate);
explicit WebSnapshotSerializer(Isolate* isolate);
~WebSnapshotSerializer();
bool TakeSnapshot(v8::Local<v8::Context> context,
v8::Local<v8::PrimitiveArray> exports,
WebSnapshotData& data_out);
bool TakeSnapshot(Handle<Object> object, MaybeHandle<FixedArray> block_list,
WebSnapshotData& data_out);
// For inspecting the state after taking a snapshot.
uint32_t string_count() const {
return static_cast<uint32_t>(string_ids_.size());
}
uint32_t map_count() const { return static_cast<uint32_t>(map_ids_.size()); }
uint32_t context_count() const {
return static_cast<uint32_t>(context_ids_.size());
}
uint32_t function_count() const {
return static_cast<uint32_t>(function_ids_.size());
}
uint32_t class_count() const {
return static_cast<uint32_t>(class_ids_.size());
}
uint32_t array_count() const {
return static_cast<uint32_t>(array_ids_.size());
}
uint32_t object_count() const {
return static_cast<uint32_t>(object_ids_.size());
}
uint32_t external_objects_count() const {
return static_cast<uint32_t>(external_objects_ids_.size());
}
Handle<FixedArray> 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<HeapObject> object);
void DiscoverString(Handle<String> string,
AllowInPlace can_be_in_place = AllowInPlace::No);
void DiscoverMap(Handle<Map> map);
void DiscoverFunction(Handle<JSFunction> function);
void DiscoverClass(Handle<JSFunction> function);
void DiscoverContextAndPrototype(Handle<JSFunction> function);
void DiscoverContext(Handle<Context> context);
void DiscoverArray(Handle<JSArray> array);
void DiscoverObject(Handle<JSObject> object);
void DiscoverSource(Handle<JSFunction> function);
void ConstructSource();
void SerializeFunctionInfo(ValueSerializer* serializer,
Handle<JSFunction> function);
void SerializeString(Handle<String> string, ValueSerializer& serializer);
void SerializeMap(Handle<Map> map);
void SerializeFunction(Handle<JSFunction> function);
void SerializeClass(Handle<JSFunction> function);
void SerializeContext(Handle<Context> context);
void SerializeArray(Handle<JSArray> array);
void SerializeObject(Handle<JSObject> object);
void SerializeExport(Handle<Object> object, Handle<String> export_name);
void WriteValue(Handle<Object> object, ValueSerializer& serializer);
void WriteStringMaybeInPlace(Handle<String> string,
ValueSerializer& serializer);
void WriteStringId(Handle<String> string, ValueSerializer& serializer);
uint32_t GetStringId(Handle<String> 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<ArrayList> contexts_;
Handle<ArrayList> functions_;
Handle<ArrayList> classes_;
Handle<ArrayList> arrays_;
Handle<ArrayList> objects_;
Handle<ArrayList> strings_;
Handle<ArrayList> 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<Handle<HeapObject>> 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<int, base::DefaultAllocationPolicy> all_strings_;
// For constructing the minimal, "compacted", source string to cover all
// function bodies.
Handle<String> full_source_;
uint32_t source_id_;
// Ordered set of (start, end) pairs of all functions we've discovered.
std::set<std::pair<int, int>> 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<int, int> 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<Script> snapshot_as_script);
~WebSnapshotDeserializer();
bool Deserialize(MaybeHandle<FixedArray> external_references = {});
// For inspecting the state after deserializing a snapshot.
uint32_t string_count() const { return string_count_; }
uint32_t map_count() const { return map_count_; }
uint32_t context_count() const { return context_count_; }
uint32_t function_count() const { return function_count_; }
uint32_t class_count() const { return class_count_; }
uint32_t array_count() const { return array_count_; }
uint32_t object_count() const { return object_count_; }
static void UpdatePointersCallback(v8::Isolate* isolate, v8::GCType type,
v8::GCCallbackFlags flags,
void* deserializer) {
reinterpret_cast<WebSnapshotDeserializer*>(deserializer)->UpdatePointers();
}
void UpdatePointers();
MaybeHandle<Object> value() const { return return_value_; }
private:
WebSnapshotDeserializer(Isolate* isolate, Handle<Object> script_name,
base::Vector<const uint8_t> buffer);
base::Vector<const uint8_t> ExtractScriptBuffer(
Isolate* isolate, Handle<Script> snapshot_as_script);
bool DeserializeSnapshot();
bool DeserializeScript();
WebSnapshotDeserializer(const WebSnapshotDeserializer&) = delete;
WebSnapshotDeserializer& operator=(const WebSnapshotDeserializer&) = delete;
void DeserializeStrings();
void DeserializeMaps();
void DeserializeContexts();
Handle<ScopeInfo> CreateScopeInfo(uint32_t variable_count, bool has_parent,
ContextType context_type);
Handle<JSFunction> CreateJSFunction(int index, uint32_t start,
uint32_t length, uint32_t parameter_count,
uint32_t flags, uint32_t context_id);
void DeserializeFunctionData(uint32_t count, uint32_t current_count);
void DeserializeFunctions();
void DeserializeClasses();
void DeserializeArrays();
void DeserializeObjects();
void DeserializeExports();
Object ReadValue(
Handle<HeapObject> object_for_deferred_reference = Handle<HeapObject>(),
uint32_t index_for_deferred_reference = 0);
Object ReadInteger();
Object ReadNumber();
String ReadString(bool internalize = false);
String ReadInPlaceString(bool internalize = false);
Object ReadArray(Handle<HeapObject> container, uint32_t container_index);
Object ReadObject(Handle<HeapObject> container, uint32_t container_index);
Object ReadFunction(Handle<HeapObject> container, uint32_t container_index);
Object ReadClass(Handle<HeapObject> container, uint32_t container_index);
Object ReadRegexp();
Object ReadExternalReference();
void ReadFunctionPrototype(Handle<JSFunction> function);
bool SetFunctionPrototype(JSFunction function, JSReceiver prototype);
HeapObject AddDeferredReference(Handle<HeapObject> container, uint32_t index,
ValueType target_type,
uint32_t target_object_index);
void ProcessDeferredReferences();
// Not virtual, on purpose (because it doesn't need to be).
void Throw(const char* message);
Handle<FixedArray> strings_handle_;
FixedArray strings_;
Handle<FixedArray> maps_handle_;
FixedArray maps_;
Handle<FixedArray> contexts_handle_;
FixedArray contexts_;
Handle<FixedArray> functions_handle_;
FixedArray functions_;
Handle<FixedArray> classes_handle_;
FixedArray classes_;
Handle<FixedArray> arrays_handle_;
FixedArray arrays_;
Handle<FixedArray> objects_handle_;
FixedArray objects_;
Handle<FixedArray> external_references_handle_;
FixedArray external_references_;
Handle<ArrayList> deferred_references_;
Handle<WeakFixedArray> shared_function_infos_handle_;
WeakFixedArray shared_function_infos_;
Handle<ObjectHashTable> shared_function_info_table_;
Handle<Script> script_;
Handle<Object> script_name_;
Handle<Object> return_value_;
uint32_t string_count_ = 0;
uint32_t map_count_ = 0;
uint32_t context_count_ = 0;
uint32_t function_count_ = 0;
uint32_t current_function_count_ = 0;
uint32_t class_count_ = 0;
uint32_t current_class_count_ = 0;
uint32_t array_count_ = 0;
uint32_t current_array_count_ = 0;
uint32_t object_count_ = 0;
uint32_t current_object_count_ = 0;
ValueDeserializer deserializer_;
ReadOnlyRoots roots_;
bool deserialized_ = false;
};
} // namespace internal
} // namespace v8
#endif // V8_WEB_SNAPSHOT_WEB_SNAPSHOT_H_