Commit 8439314d authored by Vicky Kontoura's avatar Vicky Kontoura Committed by V8 LUCI CQ

[web snapshot] Support classes

This CL adds support for classes with methods.

More specifically:
- A new ValueSerializer is added and classes are serialized separetely
from functions, although the common parts are handled in the same way
and abstracted away.
- The function prototype is serialized as an object and any missing
information is set up again during deserialization.
- FunctionFlagsToFunctionKinds() is updated to allow for more function
kinds.
- Context serialization is updated to support serializing BlockContexts
and creating ScopeInfos of type CLASS_SCOPE.
- Map serialization is updated to support properties with custom
attributes.

Bug: v8:11525, v8:11706
Change-Id: I16ca7cbc17b1811721081cda05124ce36073f9be
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3006416
Commit-Queue: Vicky Kontoura <vkont@google.com>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75893}
parent 1708ee63
......@@ -477,6 +477,7 @@ class RuntimeCallTimer final {
V(WebSnapshotDeserialize_Contexts) \
V(WebSnapshotDeserialize_Exports) \
V(WebSnapshotDeserialize_Functions) \
V(WebSnapshotDeserialize_Classes) \
V(WebSnapshotDeserialize_Maps) \
V(WebSnapshotDeserialize_Objects) \
V(WebSnapshotDeserialize_Strings)
......
......@@ -444,6 +444,16 @@ void WeakFixedArray::Set(int index, MaybeObject value, WriteBarrierMode mode) {
set_objects(index, value, mode);
}
Handle<WeakFixedArray> WeakFixedArray::EnsureSpace(Isolate* isolate,
Handle<WeakFixedArray> array,
int length) {
if (array->length() < length) {
int grow_by = length - array->length();
array = isolate->factory()->CopyWeakFixedArrayAndGrow(array, grow_by);
}
return array;
}
MaybeObjectSlot WeakFixedArray::data_start() {
return RawMaybeWeakField(kObjectsOffset);
}
......
......@@ -285,6 +285,10 @@ class WeakFixedArray
int index, MaybeObject value,
WriteBarrierMode mode = WriteBarrierMode::UPDATE_WRITE_BARRIER);
static inline Handle<WeakFixedArray> EnsureSpace(Isolate* isolate,
Handle<WeakFixedArray> array,
int length);
// Forward declare the non-atomic (set_)length defined in torque.
using TorqueGeneratedWeakFixedArray::length;
using TorqueGeneratedWeakFixedArray::set_length;
......
......@@ -51,39 +51,139 @@ uint32_t WebSnapshotSerializerDeserializer::FunctionKindToFunctionFlags(
case FunctionKind::kAsyncFunction:
case FunctionKind::kAsyncArrowFunction:
case FunctionKind::kAsyncGeneratorFunction:
case FunctionKind::kBaseConstructor:
case FunctionKind::kDefaultBaseConstructor:
case FunctionKind::kConciseMethod:
case FunctionKind::kAsyncConciseMethod:
break;
default:
Throw("Web Snapshot: Unsupported function kind");
}
auto thing = ArrowFunctionBitField::encode(IsArrowFunction(kind)) +
AsyncFunctionBitField::encode(IsAsyncFunction(kind)) +
GeneratorFunctionBitField::encode(IsGeneratorFunction(kind));
return thing;
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(
uint32_t flags) {
static const FunctionKind kFunctionKinds[] = {
// is_generator = false, is_async = false
FunctionKind::kNormalFunction, // is_arrow = false
FunctionKind::kArrowFunction, // is_arrow = true
// is_generator = false, is_async = true
FunctionKind::kAsyncFunction, // is_arrow = false
FunctionKind::kAsyncArrowFunction, // is_arrow = true
// is_generator = true, is_async = false
FunctionKind::kGeneratorFunction, // is_arrow = false
FunctionKind::kInvalid, // is_arrow = true
// is_generator = true, is_async = true
FunctionKind::kAsyncGeneratorFunction, // is_arrow = false
FunctionKind::kInvalid}; // is_arrow = true
FunctionKind kind = kFunctionKinds[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
// TODO(v8::11525) Support FunctionKind::kStaticConciseMethod.
FunctionKind::kInvalid, // is_async = false
// TODO(v8::11525) Support FunctionKind::kStaticAsyncConciseMethod.
FunctionKind::kInvalid, // is_async = true
// is_generator = true
// TODO(v8::11525) Support
// FunctionKind::kStaticConciseGeneratorMethod.
FunctionKind::kInvalid, // is_async = false
// TODO(v8::11525) Support
// FunctionKind::kStaticAsyncConciseGeneratorMethod.
FunctionKind::kInvalid // 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("Web Snapshots: Invalid function flags\n");
}
return kind;
}
bool WebSnapshotSerializerDeserializer::IsFunctionOrMethod(uint32_t flags) {
uint32_t mask = AsyncFunctionBitField::kMask |
GeneratorFunctionBitField::kMask |
ArrowFunctionBitField::kMask | MethodBitField::kMask |
StaticBitField::kMask;
return (flags & mask) == flags;
}
bool WebSnapshotSerializerDeserializer::IsConstructor(uint32_t flags) {
uint32_t mask = ClassConstructorBitField::kMask |
DefaultConstructorBitField::kMask |
DerivedConstructorBitField::kMask;
return ClassConstructorBitField::decode(flags) && (flags & mask) == flags;
}
uint32_t WebSnapshotSerializerDeserializer::GetDefaultAttributeFlags() {
auto flags = ReadOnlyBitField::encode(false) |
ConfigurableBitField::encode(true) |
EnumerableBitField::encode(true);
return flags;
}
uint32_t WebSnapshotSerializerDeserializer::AttributesToFlags(
PropertyDetails details) {
auto flags = ReadOnlyBitField::encode(details.IsReadOnly()) |
ConfigurableBitField::encode(details.IsConfigurable()) |
EnumerableBitField::encode(details.IsEnumerable());
return flags;
}
PropertyAttributes WebSnapshotSerializerDeserializer::FlagsToAttributes(
uint32_t flags) {
uint32_t attributes = ReadOnlyBitField::decode(flags) * READ_ONLY +
!ConfigurableBitField::decode(flags) * DONT_DELETE +
!EnumerableBitField::decode(flags) * DONT_ENUM;
return static_cast<PropertyAttributes>(attributes);
}
WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate)
: WebSnapshotSerializerDeserializer(
reinterpret_cast<v8::internal::Isolate*>(isolate)),
......@@ -91,6 +191,7 @@ WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate)
map_serializer_(isolate_, nullptr),
context_serializer_(isolate_, nullptr),
function_serializer_(isolate_, nullptr),
class_serializer_(isolate_, nullptr),
array_serializer_(isolate_, nullptr),
object_serializer_(isolate_, nullptr),
export_serializer_(isolate_, nullptr),
......@@ -98,6 +199,7 @@ WebSnapshotSerializer::WebSnapshotSerializer(v8::Isolate* isolate)
map_ids_(isolate_->heap()),
context_ids_(isolate_->heap()),
function_ids_(isolate_->heap()),
class_ids_(isolate_->heap()),
array_ids_(isolate_->heap()),
object_ids_(isolate_->heap()) {}
......@@ -178,8 +280,9 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
size_t needed_size =
string_serializer_.buffer_size_ + map_serializer_.buffer_size_ +
context_serializer_.buffer_size_ + function_serializer_.buffer_size_ +
array_serializer_.buffer_size_ + object_serializer_.buffer_size_ +
export_serializer_.buffer_size_ + 6 * sizeof(uint32_t);
class_serializer_.buffer_size_ + array_serializer_.buffer_size_ +
object_serializer_.buffer_size_ + export_serializer_.buffer_size_ +
8 * sizeof(uint32_t);
if (total_serializer.ExpandBuffer(needed_size).IsNothing()) {
Throw("Web snapshot: Out of memory");
return;
......@@ -203,6 +306,9 @@ void WebSnapshotSerializer::WriteSnapshot(uint8_t*& buffer,
total_serializer.WriteUint32(static_cast<uint32_t>(object_count()));
total_serializer.WriteRawBytes(object_serializer_.buffer_,
object_serializer_.buffer_size_);
total_serializer.WriteUint32(static_cast<uint32_t>(class_count()));
total_serializer.WriteRawBytes(class_serializer_.buffer_,
class_serializer_.buffer_size_);
total_serializer.WriteUint32(export_count_);
total_serializer.WriteRawBytes(export_serializer_.buffer_,
export_serializer_.buffer_size_);
......@@ -266,7 +372,11 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map, uint32_t& id) {
return;
}
int first_custom_index = -1;
std::vector<uint32_t> string_ids;
std::vector<uint32_t> attributes;
string_ids.reserve(map->NumberOfOwnDescriptors());
attributes.reserve(map->NumberOfOwnDescriptors());
for (InternalIndex i : map->IterateOwnDescriptors()) {
Handle<Name> key(map->instance_descriptors(kRelaxedLoad).GetKey(i),
isolate_);
......@@ -277,41 +387,59 @@ void WebSnapshotSerializer::SerializeMap(Handle<Map> map, uint32_t& id) {
PropertyDetails details =
map->instance_descriptors(kRelaxedLoad).GetDetails(i);
if (details.IsDontEnum()) {
Throw("Web snapshot: Non-enumerable properties not supported");
return;
}
if (details.location() != kField) {
Throw("Web snapshot: Properties which are not fields not supported");
return;
}
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));
}
uint32_t string_id = 0;
SerializeString(Handle<String>::cast(key), string_id);
string_ids.push_back(string_id);
// TODO(v8:11525): Support property attributes.
}
map_serializer_.WriteUint32(first_custom_index == -1
? PropertyAttributesType::DEFAULT
: PropertyAttributesType::CUSTOM);
map_serializer_.WriteUint32(static_cast<uint32_t>(string_ids.size()));
for (auto i : string_ids) {
map_serializer_.WriteUint32(i);
uint32_t default_flags = GetDefaultAttributeFlags();
for (size_t i = 0; i < string_ids.size(); ++i) {
if (first_custom_index >= 0) {
if (static_cast<int>(i) < first_custom_index) {
map_serializer_.WriteUint32(default_flags);
} else {
map_serializer_.WriteUint32(attributes[i - first_custom_index]);
}
}
map_serializer_.WriteUint32(string_ids[i]);
}
}
// Format (serialized function):
// - 0 if there's no context, 1 + context id otherwise
// - String id (source snippet)
// - Start position in the source snippet
// - Length in the source snippet
// - Flags (see FunctionFlags)
// TODO(v8:11525): Investigate whether the length is really needed.
void WebSnapshotSerializer::SerializeFunction(Handle<JSFunction> function,
uint32_t& id) {
if (InsertIntoIndexMap(function_ids_, function, id)) {
return;
}
void WebSnapshotSerializer::SerializeSource(ValueSerializer* serializer,
Handle<JSFunction> function) {
// TODO(v8:11525): Don't write the full source but instead, a set of minimal
// snippets which cover the serialized functions.
Handle<String> full_source(
String::cast(Script::cast(function->shared().script()).source()),
isolate_);
uint32_t source_id = 0;
SerializeString(full_source, source_id);
serializer->WriteUint32(source_id);
int start = function->shared().StartPosition();
serializer->WriteUint32(start);
int end = function->shared().EndPosition();
serializer->WriteUint32(end - start);
}
void WebSnapshotSerializer::SerializeFunctionInfo(ValueSerializer* serializer,
Handle<JSFunction> function) {
if (!function->shared().HasSourceCode()) {
Throw("Web snapshot: Function without source code");
return;
......@@ -319,36 +447,69 @@ void WebSnapshotSerializer::SerializeFunction(Handle<JSFunction> function,
Handle<Context> context(function->context(), isolate_);
if (context->IsNativeContext() || context->IsScriptContext()) {
function_serializer_.WriteUint32(0);
serializer->WriteUint32(0);
} else {
DCHECK(context->IsFunctionContext());
DCHECK(context->IsFunctionContext() || context->IsBlockContext());
uint32_t context_id = 0;
SerializeContext(context, context_id);
function_serializer_.WriteUint32(context_id + 1);
serializer->WriteUint32(context_id + 1);
}
// TODO(v8:11525): Don't write the full source but instead, a set of minimal
// snippets which cover the serialized functions.
Handle<String> full_source(
String::cast(Script::cast(function->shared().script()).source()),
isolate_);
uint32_t source_id = 0;
SerializeString(full_source, source_id);
function_serializer_.WriteUint32(source_id);
SerializeSource(serializer, function);
int start = function->shared().StartPosition();
function_serializer_.WriteUint32(start);
int end = function->shared().EndPosition();
function_serializer_.WriteUint32(end - start);
function_serializer_.WriteUint32(
serializer->WriteUint32(
FunctionKindToFunctionFlags(function->shared().kind()));
}
// Format (serialized function):
// - 0 if there's no context, 1 + context id otherwise
// - String id (source snippet)
// - Start position in the source snippet
// - Length in the source snippet
// - Flags (see FunctionFlags)
// TODO(v8:11525): Investigate whether the length is really needed.
// TODO(v8:11525): Serialize the formal parameter count.
void WebSnapshotSerializer::SerializeFunction(Handle<JSFunction> function,
uint32_t& id) {
if (InsertIntoIndexMap(function_ids_, function, id)) {
return;
}
SerializeFunctionInfo(&function_serializer_, function);
// TODO(v8:11525): Serialize .prototype.
// TODO(v8:11525): Support properties in functions.
// TODO(v8:11525): Support function referencing a function indirectly (e.g.,
// function -> context -> array -> function).
}
// Format (serialized class):
// - 1 + context id
// - String id (source snippet)
// - Start position in the source snippet
// - Length in the source snippet
// - Flags (see FunctionFlags)
// - Object id (function prototype)
void WebSnapshotSerializer::SerializeClass(Handle<JSFunction> function,
uint32_t& id) {
if (InsertIntoIndexMap(class_ids_, function, id)) {
return;
}
SerializeFunctionInfo(&class_serializer_, function);
Handle<JSObject> prototype =
Handle<JSObject>::cast(handle(function->prototype(), isolate_));
uint32_t prototype_id;
SerializeObject(prototype, prototype_id);
class_serializer_.WriteUint32(prototype_id);
// TODO(v8:11525): Support properties in classes.
// TODO(v8:11525): Support class referencing a class indirectly (e.g.,
// class -> context -> array -> class).
// TODO(v8:11525): Support class members.
}
// Format (serialized context):
// - 0 if there's no parent context, 1 + parent context id otherwise
// - Variable count
......@@ -376,6 +537,16 @@ void WebSnapshotSerializer::SerializeContext(Handle<Context> context,
InsertIntoIndexMap(context_ids_, context, id);
// TODO(v8:11525): Use less space for encoding the context type.
if (context->IsFunctionContext()) {
context_serializer_.WriteUint32(ContextType::FUNCTION);
} else if (context->IsBlockContext()) {
context_serializer_.WriteUint32(ContextType::BLOCK);
} else {
Throw("Web snapshot: Unsupported context type");
return;
}
context_serializer_.WriteUint32(parent_context_id);
Handle<ScopeInfo> scope_info(context->scope_info(), isolate_);
......@@ -519,11 +690,19 @@ void WebSnapshotSerializer::WriteValue(Handle<Object> object,
serializer.WriteUint32(ValueType::DOUBLE);
serializer.WriteDouble(HeapNumber::cast(*object).value());
break;
case JS_FUNCTION_TYPE:
SerializeFunction(Handle<JSFunction>::cast(object), id);
serializer.WriteUint32(ValueType::FUNCTION_ID);
case JS_FUNCTION_TYPE: {
Handle<JSFunction> function = Handle<JSFunction>::cast(object);
FunctionKind kind = function->shared().kind();
if (IsClassConstructor(kind)) {
SerializeClass(function, id);
serializer.WriteUint32(ValueType::CLASS_ID);
} else {
SerializeFunction(function, id);
serializer.WriteUint32(ValueType::FUNCTION_ID);
}
serializer.WriteUint32(id);
break;
}
case JS_OBJECT_TYPE:
SerializeObject(Handle<JSObject>::cast(object), id);
serializer.WriteUint32(ValueType::OBJECT_ID);
......@@ -573,6 +752,7 @@ void WebSnapshotDeserializer::Throw(const char* message) {
string_count_ = 0;
map_count_ = 0;
context_count_ = 0;
class_count_ = 0;
function_count_ = 0;
object_count_ = 0;
// Make sure we don't read any more data
......@@ -603,7 +783,13 @@ bool WebSnapshotDeserializer::UseWebSnapshot(const uint8_t* data,
DeserializeFunctions();
DeserializeArrays();
DeserializeObjects();
// It comes in handy to deserialize objects before classes. This
// way, we already have the function prototype for a class deserialized when
// processing the class and it's easier to adjust it as needed.
DeserializeClasses();
ProcessDeferredReferences();
DeserializeExports();
DCHECK_EQ(deferred_references_->Length(), 0);
if (deserializer_->position_ != deserializer_->end_) {
Throw("Web snapshot: Snapshot length mismatch");
return false;
......@@ -665,6 +851,24 @@ void WebSnapshotDeserializer::DeserializeMaps() {
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
maps_ = isolate_->factory()->NewFixedArray(map_count_);
for (uint32_t i = 0; i < map_count_; ++i) {
uint32_t map_type;
if (!deserializer_->ReadUint32(&map_type)) {
Throw("Web snapshot: Malformed shape");
return;
}
bool has_custom_property_attributes;
switch (map_type) {
case PropertyAttributesType::DEFAULT:
has_custom_property_attributes = false;
break;
case PropertyAttributesType::CUSTOM:
has_custom_property_attributes = true;
break;
default:
Throw("Web snapshot: Unsupported map type");
return;
}
uint32_t property_count;
if (!deserializer_->ReadUint32(&property_count)) {
Throw("Web snapshot: Malformed shape");
......@@ -688,13 +892,23 @@ void WebSnapshotDeserializer::DeserializeMaps() {
Handle<DescriptorArray> descriptors =
isolate_->factory()->NewDescriptorArray(0, property_count);
for (uint32_t p = 0; p < property_count; ++p) {
PropertyAttributes attributes = PropertyAttributes::NONE;
if (has_custom_property_attributes) {
uint32_t flags;
if (!deserializer_->ReadUint32(&flags)) {
Throw("Web snapshot: Malformed shape");
return;
}
attributes = FlagsToAttributes(flags);
}
Handle<String> key = ReadString(true);
// Use the "none" representation until we see the first object having this
// map. At that point, modify the representation.
Descriptor desc = Descriptor::DataField(
isolate_, key, static_cast<int>(p), PropertyAttributes::NONE,
Representation::None());
Descriptor desc =
Descriptor::DataField(isolate_, key, static_cast<int>(p), attributes,
Representation::None());
descriptors->Append(&desc);
}
......@@ -702,6 +916,7 @@ void WebSnapshotDeserializer::DeserializeMaps() {
JS_OBJECT_TYPE, JSObject::kHeaderSize * kTaggedSize, HOLEY_ELEMENTS, 0);
map->InitializeDescriptors(isolate_, *descriptors);
// TODO(v8:11525): Set 'constructor'.
// TODO(v8:11525): Set the correct prototype.
maps_->set(i, *map);
}
......@@ -717,6 +932,12 @@ void WebSnapshotDeserializer::DeserializeContexts() {
STATIC_ASSERT(kMaxItemCount <= FixedArray::kMaxLength);
contexts_ = isolate_->factory()->NewFixedArray(context_count_);
for (uint32_t i = 0; i < context_count_; ++i) {
uint32_t context_type;
if (!deserializer_->ReadUint32(&context_type)) {
Throw("Web snapshot: Malformed context type");
return;
}
uint32_t parent_context_id;
// Parent context is serialized before child context. Note: not >= on
// purpose, we're going to subtract 1 later.
......@@ -733,7 +954,8 @@ void WebSnapshotDeserializer::DeserializeContexts() {
}
// TODO(v8:11525): Enforce upper limit for variable count.
Handle<ScopeInfo> scope_info =
CreateScopeInfo(variable_count, parent_context_id > 0);
CreateScopeInfo(variable_count, parent_context_id > 0,
static_cast<ContextType>(context_type));
Handle<Context> parent_context;
if (parent_context_id > 0) {
......@@ -767,8 +989,20 @@ void WebSnapshotDeserializer::DeserializeContexts() {
// Allocate the FunctionContext after setting up the ScopeInfo to avoid
// pointing to a ScopeInfo which is not set up yet.
Handle<Context> context =
isolate_->factory()->NewFunctionContext(parent_context, scope_info);
Handle<Context> context;
switch (context_type) {
case ContextType::FUNCTION:
context =
isolate_->factory()->NewFunctionContext(parent_context, scope_info);
break;
case ContextType::BLOCK:
context =
isolate_->factory()->NewBlockContext(parent_context, scope_info);
break;
default:
Throw("Web snapshot: Unsupported context type");
return;
}
for (int variable_index = 0;
variable_index < static_cast<int>(variable_count); ++variable_index) {
Handle<Object> value;
......@@ -782,22 +1016,18 @@ void WebSnapshotDeserializer::DeserializeContexts() {
}
Handle<ScopeInfo> WebSnapshotDeserializer::CreateScopeInfo(
uint32_t variable_count, bool has_parent) {
uint32_t variable_count, bool has_parent, ContextType context_type) {
// TODO(v8:11525): Decide how to handle language modes. (The code below sets
// the language mode as strict.)
// TODO(v8:11525): Support (context-allocating) receiver.
// TODO(v8:11525): Support function variable & function name.
// TODO(v8:11525): Support classes.
const int length = ScopeInfo::kVariablePartIndex +
ScopeInfo::kPositionInfoEntries + (has_parent ? 1 : 0) +
2 * variable_count;
Handle<ScopeInfo> scope_info = isolate_->factory()->NewScopeInfo(length);
ScopeType scope_type;
int flags =
ScopeInfo::ScopeTypeBits::encode(ScopeType::FUNCTION_SCOPE) |
ScopeInfo::SloppyEvalCanExtendVarsBit::encode(false) |
ScopeInfo::LanguageModeBit::encode(LanguageMode::kStrict) |
ScopeInfo::DeclarationScopeBit::encode(true) |
ScopeInfo::DeclarationScopeBit::encode(false) |
ScopeInfo::ReceiverVariableBits::encode(VariableAllocationInfo::NONE) |
ScopeInfo::HasClassBrandBit::encode(false) |
ScopeInfo::HasSavedClassVariableIndexBit::encode(false) |
......@@ -805,7 +1035,7 @@ Handle<ScopeInfo> WebSnapshotDeserializer::CreateScopeInfo(
ScopeInfo::FunctionVariableBits::encode(VariableAllocationInfo::NONE) |
ScopeInfo::HasInferredFunctionNameBit::encode(false) |
ScopeInfo::IsAsmModuleBit::encode(false) |
ScopeInfo::HasSimpleParametersBit::encode(true) |
ScopeInfo::HasSimpleParametersBit::encode(false) |
ScopeInfo::FunctionKindBits::encode(FunctionKind::kNormalFunction) |
ScopeInfo::HasOuterScopeInfoBit::encode(has_parent) |
ScopeInfo::IsDebugEvaluateScopeBit::encode(false) |
......@@ -814,16 +1044,84 @@ Handle<ScopeInfo> WebSnapshotDeserializer::CreateScopeInfo(
ScopeInfo::HasContextExtensionSlotBit::encode(false) |
ScopeInfo::IsReplModeScopeBit::encode(false) |
ScopeInfo::HasLocalsBlockListBit::encode(false);
switch (context_type) {
case ContextType::FUNCTION:
scope_type = ScopeType::FUNCTION_SCOPE;
flags |= ScopeInfo::DeclarationScopeBit::encode(true) |
ScopeInfo::HasSimpleParametersBit::encode(true);
break;
case ContextType::BLOCK:
scope_type = ScopeType::CLASS_SCOPE;
flags |= ScopeInfo::ForceContextAllocationBit::encode(true);
break;
default:
// Default to a CLASS_SCOPE, so that the rest of the code can be executed
// without failures.
scope_type = ScopeType::CLASS_SCOPE;
Throw("Web snapshot: Unsupported context type");
}
flags |= ScopeInfo::ScopeTypeBits::encode(scope_type);
const int length = ScopeInfo::kVariablePartIndex +
(ScopeInfo::NeedsPositionInfo(scope_type)
? ScopeInfo::kPositionInfoEntries
: 0) +
(has_parent ? 1 : 0) + 2 * variable_count;
Handle<ScopeInfo> scope_info = isolate_->factory()->NewScopeInfo(length);
scope_info->set_flags(flags);
DCHECK(!scope_info->IsEmpty());
scope_info->set_context_local_count(variable_count);
// TODO(v8:11525): Support parameters.
scope_info->set_parameter_count(0);
scope_info->SetPositionInfo(0, 0);
if (scope_info->HasPositionInfo()) {
scope_info->SetPositionInfo(0, 0);
}
return scope_info;
}
Handle<JSFunction> WebSnapshotDeserializer::CreateJSFunction(
int shared_function_info_index, uint32_t start_position, uint32_t length,
uint32_t flags, uint32_t context_id) {
// TODO(v8:11525): Deduplicate the SFIs for class methods.
FunctionKind kind = FunctionFlagsToFunctionKind(flags);
Handle<SharedFunctionInfo> shared =
isolate_->factory()->NewSharedFunctionInfo(
isolate_->factory()->empty_string(), MaybeHandle<Code>(),
Builtin::kCompileLazy, kind);
if (IsConciseMethod(kind)) {
shared->set_syntax_kind(FunctionSyntaxKind::kAccessorOrMethod);
}
shared->set_script(*script_);
shared->set_function_literal_id(shared_function_info_index);
// TODO(v8:11525): Decide how to handle language modes.
shared->set_language_mode(LanguageMode::kStrict);
shared->set_uncompiled_data(
*isolate_->factory()->NewUncompiledDataWithoutPreparseData(
ReadOnlyRoots(isolate_).empty_string_handle(), start_position,
start_position + length));
shared->set_allows_lazy_compilation(true);
shared_function_infos_->Set(shared_function_info_index,
HeapObjectReference::Weak(*shared));
shared_function_info_table_ = ObjectHashTable::Put(
shared_function_info_table_,
handle(Smi::FromInt(start_position), isolate_),
handle(Smi::FromInt(shared_function_info_index), isolate_));
Handle<JSFunction> function =
Factory::JSFunctionBuilder(isolate_, shared, isolate_->native_context())
.Build();
if (context_id > 0) {
DCHECK_LT(context_id - 1, context_count_);
// Guards raw pointer "context" below.
DisallowHeapAllocation no_heap_access;
Context context = Context::cast(contexts_->get(context_id - 1));
function->set_context(context);
shared->set_outer_scope_info(context.scope_info());
}
return function;
}
void WebSnapshotDeserializer::DeserializeFunctions() {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Functions);
if (!deserializer_->ReadUint32(&function_count_) ||
......@@ -836,16 +1134,14 @@ void WebSnapshotDeserializer::DeserializeFunctions() {
// Overallocate the array for SharedFunctionInfos; functions which we
// deserialize soon will create more SharedFunctionInfos when called.
Handle<WeakFixedArray> infos(isolate_->factory()->NewWeakFixedArray(
shared_function_infos_ = isolate_->factory()->NewWeakFixedArray(
WeakArrayList::CapacityForLength(function_count_ + 1),
AllocationType::kOld));
Handle<ObjectHashTable> shared_function_info_table =
ObjectHashTable::New(isolate_, function_count_);
Handle<Script> script =
isolate_->factory()->NewScript(isolate_->factory()->empty_string());
script->set_type(Script::TYPE_WEB_SNAPSHOT);
script->set_shared_function_infos(*infos);
script->set_shared_function_info_table(*shared_function_info_table);
AllocationType::kOld);
shared_function_info_table_ = ObjectHashTable::New(isolate_, function_count_);
script_ = isolate_->factory()->NewScript(isolate_->factory()->empty_string());
script_->set_type(Script::TYPE_WEB_SNAPSHOT);
script_->set_shared_function_infos(*shared_function_infos_);
script_->set_shared_function_info_table(*shared_function_info_table_);
for (; current_function_count_ < function_count_; ++current_function_count_) {
uint32_t context_id;
......@@ -858,10 +1154,10 @@ void WebSnapshotDeserializer::DeserializeFunctions() {
Handle<String> source = ReadString(false);
if (current_function_count_ == 0) {
script->set_source(*source);
script_->set_source(*source);
} else {
// TODO(v8:11525): Support multiple source snippets.
DCHECK_EQ(script->source(), *source);
DCHECK_EQ(script_->source(), *source);
}
uint32_t start_position;
......@@ -874,46 +1170,86 @@ void WebSnapshotDeserializer::DeserializeFunctions() {
return;
}
// TODO(v8:11525): Support (exported) top level functions.
// TODO(v8:11525): Deduplicate the SFIs for inner functions the user creates
// post-deserialization (by calling the outer function, if it's also in the
// snapshot) against the ones we create here.
Handle<SharedFunctionInfo> shared =
isolate_->factory()->NewSharedFunctionInfo(
isolate_->factory()->empty_string(), MaybeHandle<Code>(),
Builtin::kCompileLazy, FunctionFlagsToFunctionKind(flags));
shared->set_script(*script);
// Index 0 is reserved for top-level shared function info (which web
// snapshot scripts don't have).
const int shared_function_info_index = current_function_count_ + 1;
shared->set_function_literal_id(shared_function_info_index);
// TODO(v8:11525): Decide how to handle language modes.
shared->set_language_mode(LanguageMode::kStrict);
shared->set_uncompiled_data(
*isolate_->factory()->NewUncompiledDataWithoutPreparseData(
ReadOnlyRoots(isolate_).empty_string_handle(), start_position,
start_position + length));
shared->set_allows_lazy_compilation(true);
infos->Set(shared_function_info_index, HeapObjectReference::Weak(*shared));
shared_function_info_table = ObjectHashTable::Put(
shared_function_info_table,
handle(Smi::FromInt(start_position), isolate_),
handle(Smi::FromInt(shared_function_info_index), isolate_));
Handle<JSFunction> function = CreateJSFunction(
current_function_count_ + 1, start_position, length, flags, context_id);
functions_->set(current_function_count_, *function);
}
}
void WebSnapshotDeserializer::DeserializeClasses() {
RCS_SCOPE(isolate_, RuntimeCallCounterId::kWebSnapshotDeserialize_Classes);
if (!deserializer_->ReadUint32(&class_count_) ||
class_count_ > kMaxItemCount) {
Throw("Web snapshot: Malformed class table");
return;
}
STATIC_ASSERT(kMaxItemCount + 1 <= FixedArray::kMaxLength);
classes_ = isolate_->factory()->NewFixedArray(class_count_);
// Grow the array for SharedFunctionInfos.
shared_function_infos_ = WeakFixedArray::EnsureSpace(
isolate_, shared_function_infos_,
WeakArrayList::CapacityForLength(function_count_ + 1 + class_count_));
script_->set_shared_function_infos(*shared_function_infos_);
for (; current_class_count_ < class_count_; ++current_class_count_) {
uint32_t context_id;
// Note: > (not >= on purpose, we will subtract 1).
if (!deserializer_->ReadUint32(&context_id) ||
context_id > context_count_) {
Throw("Web snapshot: Malformed class");
return;
}
Handle<String> source = ReadString(false);
if (current_function_count_ + current_class_count_ == 0) {
script_->set_source(*source);
} else {
// TODO(v8:11525): Support multiple source snippets.
DCHECK_EQ(script_->source(), *source);
}
uint32_t start_position;
uint32_t length;
uint32_t flags;
if (!deserializer_->ReadUint32(&start_position) ||
!deserializer_->ReadUint32(&length) ||
!deserializer_->ReadUint32(&flags)) {
Throw("Web snapshot: Malformed class");
return;
}
// Index 0 is reserved for top-level shared function info (which web
// snapshot scripts don't have).
Handle<JSFunction> function =
Factory::JSFunctionBuilder(isolate_, shared, isolate_->native_context())
.Build();
if (context_id > 0) {
DCHECK_LT(context_id - 1, context_count_);
// Guards raw pointer "context" below.
DisallowHeapAllocation no_heap_access;
Context context = Context::cast(contexts_->get(context_id - 1));
function->set_context(context);
shared->set_outer_scope_info(context.scope_info());
CreateJSFunction(function_count_ + current_class_count_ + 1,
start_position, length, flags, context_id);
classes_->set(current_class_count_, *function);
uint32_t function_prototype;
if (!deserializer_->ReadUint32(&function_prototype) ||
function_prototype >= object_count_) {
Throw("Web snapshot: Malformed class");
return;
}
functions_->set(current_function_count_, *function);
Handle<JSObject> prototype = Handle<JSObject>::cast(
handle(Object::cast(objects_->get(function_prototype)), isolate_));
// TODO(v8:11525): Enforce the invariant that no two prototypes share a map.
Map map = prototype->map();
map.set_is_prototype_map(true);
if (!map.constructor_or_back_pointer().IsNullOrUndefined()) {
Throw("Web snapshot: Map already has a constructor or back pointer set");
return;
}
map.set_constructor_or_back_pointer(*function);
function->set_prototype_or_initial_map(*prototype, kReleaseStore);
classes_->set(current_class_count_, *function);
}
}
......@@ -962,7 +1298,6 @@ void WebSnapshotDeserializer::DeserializeObjects() {
object->set_raw_properties_or_hash(*property_array, kRelaxedStore);
objects_->set(static_cast<int>(current_object_count_), *object);
}
ProcessDeferredReferences();
}
void WebSnapshotDeserializer::DeserializeArrays() {
......@@ -1131,7 +1466,7 @@ void WebSnapshotDeserializer::ReadValue(
}
representation = Representation::Tagged();
break;
case ValueType::FUNCTION_ID:
case ValueType::FUNCTION_ID: {
uint32_t function_id;
if (!deserializer_->ReadUint32(&function_id) ||
function_id >= function_count_) {
......@@ -1153,6 +1488,28 @@ void WebSnapshotDeserializer::ReadValue(
}
representation = Representation::Tagged();
break;
}
case ValueType::CLASS_ID: {
uint32_t class_id;
if (!deserializer_->ReadUint32(&class_id) || class_id >= kMaxItemCount) {
Throw("Web snapshot: Malformed object property");
return;
}
if (class_id < current_class_count_) {
value = handle(classes_->get(class_id), isolate_);
} else {
// The class hasn't been deserialized yet.
value = isolate_->factory()->undefined_value();
if (object_for_deferred_reference.is_null()) {
Throw("Web snapshot: Invalid object reference");
return;
}
AddDeferredReference(object_for_deferred_reference,
index_for_deferred_reference, CLASS_ID, class_id);
}
representation = Representation::Tagged();
break;
}
case ValueType::REGEXP: {
Handle<String> pattern = ReadString(false);
Handle<String> flags_string = ReadString(false);
......@@ -1194,6 +1551,7 @@ void WebSnapshotDeserializer::ProcessDeferredReferences() {
DisallowGarbageCollection no_gc;
ArrayList raw_deferred_references = *deferred_references_;
FixedArray raw_functions = *functions_;
FixedArray raw_classes = *classes_;
FixedArray raw_arrays = *arrays_;
FixedArray raw_objects = *objects_;
......@@ -1217,6 +1575,16 @@ void WebSnapshotDeserializer::ProcessDeferredReferences() {
}
target = raw_functions.get(target_index);
break;
case CLASS_ID:
if (static_cast<uint32_t>(target_index) >= class_count_) {
// Throw can allocate, but it's ok, since we're not using the raw
// pointers after that.
AllowGarbageCollection allow_gc;
Throw("Web Snapshots: Invalid class reference");
return;
}
target = raw_classes.get(target_index);
break;
case ARRAY_ID:
if (static_cast<uint32_t>(target_index) >= array_count_) {
AllowGarbageCollection allow_gc;
......@@ -1246,6 +1614,7 @@ void WebSnapshotDeserializer::ProcessDeferredReferences() {
UNREACHABLE();
}
}
deferred_references_->SetLength(0);
}
} // namespace internal
......
......@@ -52,11 +52,22 @@ class WebSnapshotSerializerDeserializer {
ARRAY_ID,
OBJECT_ID,
FUNCTION_ID,
CLASS_ID,
REGEXP
};
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 =
......@@ -81,9 +92,18 @@ class WebSnapshotSerializerDeserializer {
// Keep most common function kinds in the 7 least significant bits to make the
// flags fit in 1 byte.
using ArrowFunctionBitField = base::BitField<bool, 0, 1>;
using AsyncFunctionBitField = ArrowFunctionBitField::Next<bool, 1>;
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
......@@ -111,6 +131,10 @@ class V8_EXPORT WebSnapshotSerializer
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());
}
......@@ -129,10 +153,15 @@ class V8_EXPORT WebSnapshotSerializer
// Returns true if the object was already in the map, false if it was added.
bool InsertIntoIndexMap(ObjectCacheIndexMap& map, Handle<HeapObject> object,
uint32_t& id);
void SerializeSource(ValueSerializer* serializer,
Handle<JSFunction> function);
void SerializeFunctionInfo(ValueSerializer* serializer,
Handle<JSFunction> function);
void SerializeString(Handle<String> string, uint32_t& id);
void SerializeMap(Handle<Map> map, uint32_t& id);
void SerializeFunction(Handle<JSFunction> function, uint32_t& id);
void SerializeClass(Handle<JSFunction> function, uint32_t& id);
void SerializeContext(Handle<Context> context, uint32_t& id);
void SerializeArray(Handle<JSArray> array, uint32_t& id);
void SerializePendingArray(Handle<JSArray> array);
......@@ -145,6 +174,7 @@ class V8_EXPORT WebSnapshotSerializer
ValueSerializer map_serializer_;
ValueSerializer context_serializer_;
ValueSerializer function_serializer_;
ValueSerializer class_serializer_;
ValueSerializer array_serializer_;
ValueSerializer object_serializer_;
ValueSerializer export_serializer_;
......@@ -153,6 +183,7 @@ class V8_EXPORT WebSnapshotSerializer
ObjectCacheIndexMap map_ids_;
ObjectCacheIndexMap context_ids_;
ObjectCacheIndexMap function_ids_;
ObjectCacheIndexMap class_ids_;
ObjectCacheIndexMap array_ids_;
ObjectCacheIndexMap object_ids_;
uint32_t export_count_ = 0;
......@@ -173,6 +204,7 @@ class V8_EXPORT WebSnapshotDeserializer
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_; }
......@@ -184,8 +216,14 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<String> ReadString(bool internalize = false);
void DeserializeMaps();
void DeserializeContexts();
Handle<ScopeInfo> CreateScopeInfo(uint32_t variable_count, bool has_parent);
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 flags,
uint32_t context_id);
void DeserializeFunctionData(uint32_t count, uint32_t current_count);
void DeserializeFunctions();
void DeserializeClasses();
void DeserializeArrays();
void DeserializeObjects();
void DeserializeExports();
......@@ -205,15 +243,22 @@ class V8_EXPORT WebSnapshotDeserializer
Handle<FixedArray> maps_;
Handle<FixedArray> contexts_;
Handle<FixedArray> functions_;
Handle<FixedArray> classes_;
Handle<FixedArray> arrays_;
Handle<FixedArray> objects_;
Handle<ArrayList> deferred_references_;
Handle<WeakFixedArray> shared_function_infos_;
Handle<ObjectHashTable> shared_function_info_table_;
Handle<Script> script_;
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;
......
......@@ -366,6 +366,62 @@ TEST(SFIDeduplication) {
}
}
TEST(SFIDeduplicationClasses) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
WebSnapshotData snapshot_data;
{
v8::Local<v8::Context> new_context = CcTest::NewContext();
v8::Context::Scope context_scope(new_context);
const char* snapshot_source =
"let foo = {};\n"
"foo.create = function(a) {\n"
" return class {\n"
" constructor(x) {this.x = x;};\n"
" }\n"
"}\n"
"foo.class = foo.create('hi');";
CompileRun(snapshot_source);
v8::Local<v8::PrimitiveArray> exports = v8::PrimitiveArray::New(isolate, 1);
v8::Local<v8::String> str =
v8::String::NewFromUtf8(isolate, "foo").ToLocalChecked();
exports->Set(isolate, 0, str);
WebSnapshotSerializer serializer(isolate);
CHECK(serializer.TakeSnapshot(new_context, exports, snapshot_data));
CHECK(!serializer.has_error());
CHECK_NOT_NULL(snapshot_data.buffer);
}
{
v8::Local<v8::Context> new_context = CcTest::NewContext();
v8::Context::Scope context_scope(new_context);
WebSnapshotDeserializer deserializer(isolate);
CHECK(deserializer.UseWebSnapshot(snapshot_data.buffer,
snapshot_data.buffer_size));
CHECK(!deserializer.has_error());
const char* get_class = "foo.class";
const char* create_new_class = "foo.create()";
// Verify that foo.inner and the JSFunction which is the result of calling
// foo.outer() after deserialization share the SFI.
v8::Local<v8::Function> v8_class1 =
CompileRun(get_class).As<v8::Function>();
v8::Local<v8::Function> v8_class2 =
CompileRun(create_new_class).As<v8::Function>();
Handle<JSFunction> class1 =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_class1));
Handle<JSFunction> class2 =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_class2));
CHECK_EQ(class1->shared(), class2->shared());
}
}
TEST(SFIDeduplicationAfterBytecodeFlushing) {
FLAG_stress_flush_bytecode = true;
CcTest::InitializeVM();
......@@ -451,6 +507,91 @@ TEST(SFIDeduplicationAfterBytecodeFlushing) {
}
}
TEST(SFIDeduplicationAfterBytecodeFlushingClasses) {
FLAG_stress_flush_bytecode = true;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
WebSnapshotData snapshot_data;
{
v8::HandleScope scope(isolate);
v8::Local<v8::Context> new_context = CcTest::NewContext();
v8::Context::Scope context_scope(new_context);
const char* snapshot_source =
"let foo = {};\n"
"foo.create = function(a) {\n"
" return class {\n"
" constructor(x) {this.x = x;};\n"
" }\n"
"}\n"
"foo.class = foo.create('hi');";
CompileRun(snapshot_source);
v8::Local<v8::PrimitiveArray> exports = v8::PrimitiveArray::New(isolate, 1);
v8::Local<v8::String> str =
v8::String::NewFromUtf8(isolate, "foo").ToLocalChecked();
exports->Set(isolate, 0, str);
WebSnapshotSerializer serializer(isolate);
CHECK(serializer.TakeSnapshot(new_context, exports, snapshot_data));
CHECK(!serializer.has_error());
CHECK_NOT_NULL(snapshot_data.buffer);
}
CcTest::CollectAllGarbage();
CcTest::CollectAllGarbage();
{
v8::HandleScope scope(isolate);
v8::Local<v8::Context> new_context = CcTest::NewContext();
v8::Context::Scope context_scope(new_context);
WebSnapshotDeserializer deserializer(isolate);
CHECK(deserializer.UseWebSnapshot(snapshot_data.buffer,
snapshot_data.buffer_size));
CHECK(!deserializer.has_error());
const char* get_create = "foo.create";
const char* get_class = "foo.class";
const char* create_new_class = "foo.create()";
v8::Local<v8::Function> v8_create =
CompileRun(get_create).As<v8::Function>();
Handle<JSFunction> create =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_create));
CHECK(!create->shared().is_compiled());
v8::Local<v8::Function> v8_class1 =
CompileRun(get_class).As<v8::Function>();
v8::Local<v8::Function> v8_class2 =
CompileRun(create_new_class).As<v8::Function>();
Handle<JSFunction> class1 =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_class1));
Handle<JSFunction> class2 =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_class2));
CHECK(create->shared().is_compiled());
CHECK_EQ(class1->shared(), class2->shared());
// Force bytecode flushing of "foo.outer".
CcTest::CollectAllGarbage();
CcTest::CollectAllGarbage();
CHECK(!create->shared().is_compiled());
// Create another inner function.
v8::Local<v8::Function> v8_class3 =
CompileRun(create_new_class).As<v8::Function>();
Handle<JSFunction> class3 =
Handle<JSFunction>::cast(Utils::OpenHandle(*v8_class3));
// Check that it shares the SFI with the original inner function which is in
// the snapshot.
CHECK_EQ(class1->shared(), class3->shared());
}
}
TEST(SFIDeduplicationOfFunctionsNotInSnapshot) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
......
......@@ -13,13 +13,13 @@ function use(exports) {
function takeAndUseWebSnapshot(createObjects, exports) {
// Take a snapshot in Realm r1.
const r1 = Realm.create();
Realm.eval(r1, createObjects, {type: 'function'});
Realm.eval(r1, createObjects, { type: 'function' });
const snapshot = Realm.takeWebSnapshot(r1, exports);
// Use the snapshot in Realm r2.
const r2 = Realm.create();
const success = Realm.useWebSnapshot(r2, snapshot);
assertTrue(success);
return Realm.eval(r2, use, {type: 'function', arguments: [exports]});
return Realm.eval(r2, use, { type: 'function', arguments: [exports] });
}
(function TestMinimal() {
......@@ -191,7 +191,7 @@ function takeAndUseWebSnapshot(createObjects, exports) {
(function TestObjectReferencingObject() {
function createObjects() {
globalThis.foo = {
bar: {baz: 11525}
bar: { baz: 11525 }
};
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
......@@ -201,7 +201,7 @@ function takeAndUseWebSnapshot(createObjects, exports) {
(function TestContextReferencingObject() {
function createObjects() {
function outer() {
let o = {value: 11525};
let o = { value: 11525 };
function inner() { return o; }
return inner;
}
......@@ -259,7 +259,7 @@ function takeAndUseWebSnapshot(createObjects, exports) {
(function TestArrayContainingObject() {
function createObjects() {
globalThis.foo = {
array: [{a: 1}, {b: 2}]
array: [{ a: 1 }, { b: 2 }]
};
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
......@@ -270,14 +270,13 @@ function takeAndUseWebSnapshot(createObjects, exports) {
(function TestArrayContainingFunction() {
function createObjects() {
globalThis.foo = {
array: [function() { return 5; }]
array: [function () { return 5; }]
};
}
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(5, foo.array[0]());
})();
(function TestContextReferencingArray() {
function createObjects() {
function outer() {
......@@ -292,3 +291,46 @@ function takeAndUseWebSnapshot(createObjects, exports) {
const { foo } = takeAndUseWebSnapshot(createObjects, ['foo']);
assertEquals(11525, foo.func()[0]);
})();
(function TestEmptyClass() {
function createObjects() {
globalThis.Foo = class Foo { };
}
const { Foo } = takeAndUseWebSnapshot(createObjects, ['Foo']);
const x = new Foo();
})();
(function TestClassWithConstructor() {
function createObjects() {
globalThis.Foo = class {
constructor() {
this.n = 42;
}
};
}
const { Foo } = takeAndUseWebSnapshot(createObjects, ['Foo']);
const x = new Foo(2);
assertEquals(42, x.n);
})();
(function TestClassWithMethods() {
function createObjects() {
globalThis.Foo = class {
f() { return 7; };
};
}
const { Foo } = takeAndUseWebSnapshot(createObjects, ['Foo']);
const x = new Foo();
assertEquals(7, x.f());
})();
(async function TestClassWithAsyncMethods() {
function createObjects() {
globalThis.Foo = class {
async g() { return 6; };
};
}
const { Foo } = takeAndUseWebSnapshot(createObjects, ['Foo']);
const x = new Foo();
assertEquals(6, await x.g());
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment