blob: baddcc02fafa617723cc7f4fc2200afc2cf6d2f6 [file] [log] [blame]
// Copyright 2018 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/compiler/js-heap-broker.h"
#include "src/ast/modules.h"
#include "src/boxed-float.h"
#include "src/code-factory.h"
#include "src/compiler/graph-reducer.h"
#include "src/objects-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/module-inl.h"
#include "src/utils.h"
namespace v8 {
namespace internal {
namespace compiler {
#define FORWARD_DECL(Name) class Name##Data;
HEAP_BROKER_OBJECT_LIST(FORWARD_DECL)
#undef FORWARD_DECL
// TODO(neis): It would be nice to share the serialized data for read-only
// objects.
class ObjectData : public ZoneObject {
public:
static ObjectData* Serialize(JSHeapBroker* broker, Handle<Object> object);
ObjectData(JSHeapBroker* broker, Handle<Object> object, bool is_smi)
: broker_(broker), object_(object), is_smi_(is_smi) {
broker->AddData(object, this);
}
#define DECLARE_IS_AND_AS(Name) \
bool Is##Name() const; \
Name##Data* As##Name();
HEAP_BROKER_OBJECT_LIST(DECLARE_IS_AND_AS)
#undef DECLARE_IS_AND_AS
JSHeapBroker* broker() const { return broker_; }
Handle<Object> object() const { return object_; }
bool is_smi() const { return is_smi_; }
private:
JSHeapBroker* const broker_;
Handle<Object> const object_;
bool const is_smi_;
};
class HeapObjectData : public ObjectData {
public:
static HeapObjectData* Serialize(JSHeapBroker* broker,
Handle<HeapObject> object);
HeapObjectType type() const { return type_; }
MapData* map() const { return map_; }
HeapObjectData(JSHeapBroker* broker, Handle<HeapObject> object,
HeapObjectType type);
private:
HeapObjectType const type_;
MapData* const map_;
};
class PropertyCellData : public HeapObjectData {
public:
PropertyCellData(JSHeapBroker* broker, Handle<PropertyCell> object,
HeapObjectType type);
PropertyDetails property_details() const { return property_details_; }
void Serialize();
ObjectData* value() { return value_; }
private:
PropertyDetails const property_details_;
bool serialized_ = false;
ObjectData* value_ = nullptr;
};
PropertyCellData::PropertyCellData(JSHeapBroker* broker,
Handle<PropertyCell> object,
HeapObjectType type)
: HeapObjectData(broker, object, type),
property_details_(object->property_details()) {}
void PropertyCellData::Serialize() {
if (serialized_) return;
serialized_ = true;
auto cell = Handle<PropertyCell>::cast(object());
DCHECK_NULL(value_);
value_ = broker()->GetOrCreateData(cell->value());
}
class JSObjectField {
public:
bool IsDouble() const { return object_ == nullptr; }
double AsDouble() const {
CHECK(IsDouble());
return number_;
}
bool IsObject() const { return object_ != nullptr; }
ObjectData* AsObject() const {
CHECK(IsObject());
return object_;
}
explicit JSObjectField(double value) : number_(value) {}
explicit JSObjectField(ObjectData* value) : object_(value) {}
private:
ObjectData* object_ = nullptr;
double number_ = 0;
};
class JSObjectData : public HeapObjectData {
public:
JSObjectData(JSHeapBroker* broker, Handle<JSObject> object,
HeapObjectType type);
// Recursively serializes all reachable JSObjects.
void SerializeAsBoilerplate();
// Shallow serialization of {elements}.
void SerializeElements();
const JSObjectField& GetInobjectField(int property_index) const;
FixedArrayBaseData* elements() const;
// This method is only used to assert our invariants.
bool cow_or_empty_elements_tenured() const;
void SerializeObjectCreateMap();
MapData* object_create_map() const { // Can be nullptr.
CHECK(serialized_object_create_map_);
return object_create_map_;
}
private:
void SerializeRecursive(int max_depths);
FixedArrayBaseData* elements_ = nullptr;
bool cow_or_empty_elements_tenured_ = false;
// The {serialized_as_boilerplate} flag is set when all recursively
// reachable JSObjects are serialized.
bool serialized_as_boilerplate_ = false;
bool serialized_elements_ = false;
ZoneVector<JSObjectField> inobject_fields_;
bool serialized_object_create_map_ = false;
MapData* object_create_map_ = nullptr;
};
void JSObjectData::SerializeObjectCreateMap() {
if (serialized_object_create_map_) return;
serialized_object_create_map_ = true;
Handle<JSObject> jsobject = Handle<JSObject>::cast(object());
if (jsobject->map()->is_prototype_map()) {
Handle<Object> maybe_proto_info(jsobject->map()->prototype_info(),
broker()->isolate());
if (maybe_proto_info->IsPrototypeInfo()) {
auto proto_info = Handle<PrototypeInfo>::cast(maybe_proto_info);
if (proto_info->HasObjectCreateMap()) {
DCHECK_NULL(object_create_map_);
object_create_map_ =
broker()->GetOrCreateData(proto_info->ObjectCreateMap())->AsMap();
}
}
}
}
class JSFunctionData : public JSObjectData {
public:
JSFunctionData(JSHeapBroker* broker, Handle<JSFunction> object,
HeapObjectType type);
bool has_initial_map() const { return has_initial_map_; }
bool has_prototype() const { return has_prototype_; }
bool PrototypeRequiresRuntimeLookup() const {
return PrototypeRequiresRuntimeLookup_;
}
void Serialize();
JSGlobalProxyData* global_proxy() const { return global_proxy_; }
MapData* initial_map() const { return initial_map_; }
ObjectData* prototype() const { return prototype_; }
SharedFunctionInfoData* shared() const { return shared_; }
int initial_map_instance_size_with_min_slack() const {
CHECK(serialized_);
return initial_map_instance_size_with_min_slack_;
}
private:
bool has_initial_map_;
bool has_prototype_;
bool PrototypeRequiresRuntimeLookup_;
bool serialized_ = false;
JSGlobalProxyData* global_proxy_ = nullptr;
MapData* initial_map_ = nullptr;
ObjectData* prototype_ = nullptr;
SharedFunctionInfoData* shared_ = nullptr;
int initial_map_instance_size_with_min_slack_;
};
class JSRegExpData : public JSObjectData {
public:
JSRegExpData(JSHeapBroker* broker, Handle<JSRegExp> object,
HeapObjectType type)
: JSObjectData(broker, object, type) {}
void SerializeAsRegExpBoilerplate();
ObjectData* raw_properties_or_hash() const { return raw_properties_or_hash_; }
ObjectData* data() const { return data_; }
ObjectData* source() const { return source_; }
ObjectData* flags() const { return flags_; }
ObjectData* last_index() const { return last_index_; }
private:
bool serialized_as_reg_exp_boilerplate_ = false;
ObjectData* raw_properties_or_hash_ = nullptr;
ObjectData* data_ = nullptr;
ObjectData* source_ = nullptr;
ObjectData* flags_ = nullptr;
ObjectData* last_index_ = nullptr;
};
class HeapNumberData : public HeapObjectData {
public:
HeapNumberData(JSHeapBroker* broker, Handle<HeapNumber> object,
HeapObjectType type)
: HeapObjectData(broker, object, type), value_(object->value()) {}
double value() const { return value_; }
private:
double const value_;
};
class MutableHeapNumberData : public HeapObjectData {
public:
MutableHeapNumberData(JSHeapBroker* broker, Handle<MutableHeapNumber> object,
HeapObjectType type)
: HeapObjectData(broker, object, type), value_(object->value()) {}
double value() const { return value_; }
private:
double const value_;
};
class ContextData : public HeapObjectData {
public:
ContextData(JSHeapBroker* broker, Handle<Context> object,
HeapObjectType type);
void Serialize();
ContextData* previous() const {
CHECK(serialized_);
return previous_;
}
private:
bool serialized_ = false;
ContextData* previous_ = nullptr;
};
ContextData::ContextData(JSHeapBroker* broker, Handle<Context> object,
HeapObjectType type)
: HeapObjectData(broker, object, type) {}
void ContextData::Serialize() {
if (serialized_) return;
serialized_ = true;
Handle<Context> context = Handle<Context>::cast(object());
DCHECK_NULL(previous_);
// Context::previous DCHECK-fails when called on the native context.
if (!context->IsNativeContext()) {
previous_ = broker()->GetOrCreateData(context->previous())->AsContext();
previous_->Serialize();
}
}
class NativeContextData : public ContextData {
public:
#define DECL_ACCESSOR(type, name) \
type##Data* name() const { return name##_; }
BROKER_NATIVE_CONTEXT_FIELDS(DECL_ACCESSOR)
#undef DECL_ACCESSOR
const ZoneVector<MapData*>& function_maps() const {
CHECK(serialized_);
return function_maps_;
}
NativeContextData(JSHeapBroker* broker, Handle<NativeContext> object,
HeapObjectType type);
void Serialize();
private:
bool serialized_ = false;
#define DECL_MEMBER(type, name) type##Data* name##_ = nullptr;
BROKER_NATIVE_CONTEXT_FIELDS(DECL_MEMBER)
#undef DECL_MEMBER
ZoneVector<MapData*> function_maps_;
};
class NameData : public HeapObjectData {
public:
NameData(JSHeapBroker* broker, Handle<Name> object, HeapObjectType type)
: HeapObjectData(broker, object, type) {}
};
class StringData : public NameData {
public:
StringData(JSHeapBroker* broker, Handle<String> object, HeapObjectType type);
int length() const { return length_; }
uint16_t first_char() const { return first_char_; }
base::Optional<double> to_number() const { return to_number_; }
bool is_external_string() const { return is_external_string_; }
bool is_seq_string() const { return is_seq_string_; }
private:
int const length_;
uint16_t const first_char_;
base::Optional<double> to_number_;
bool const is_external_string_;
bool const is_seq_string_;
static constexpr int kMaxLengthForDoubleConversion = 23;
};
StringData::StringData(JSHeapBroker* broker, Handle<String> object,
HeapObjectType type)
: NameData(broker, object, type),
length_(object->length()),
first_char_(length_ > 0 ? object->Get(0) : 0),
is_external_string_(object->IsExternalString()),
is_seq_string_(object->IsSeqString()) {
int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY;
if (length_ <= kMaxLengthForDoubleConversion) {
to_number_ = StringToDouble(
broker->isolate(), broker->isolate()->unicode_cache(), object, flags);
}
}
class InternalizedStringData : public StringData {
public:
InternalizedStringData(JSHeapBroker* broker,
Handle<InternalizedString> object, HeapObjectType type)
: StringData(broker, object, type) {}
};
namespace {
bool IsFastLiteralHelper(Handle<JSObject> boilerplate, int max_depth,
int* max_properties) {
DCHECK_GE(max_depth, 0);
DCHECK_GE(*max_properties, 0);
// Make sure the boilerplate map is not deprecated.
if (!JSObject::TryMigrateInstance(boilerplate)) return false;
// Check for too deep nesting.
if (max_depth == 0) return false;
// Check the elements.
Isolate* const isolate = boilerplate->GetIsolate();
Handle<FixedArrayBase> elements(boilerplate->elements(), isolate);
if (elements->length() > 0 &&
elements->map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) {
if (boilerplate->HasSmiOrObjectElements()) {
Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
int length = elements->length();
for (int i = 0; i < length; i++) {
if ((*max_properties)-- == 0) return false;
Handle<Object> value(fast_elements->get(i), isolate);
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
if (!IsFastLiteralHelper(value_object, max_depth - 1,
max_properties)) {
return false;
}
}
}
} else if (boilerplate->HasDoubleElements()) {
if (elements->Size() > kMaxRegularHeapObjectSize) return false;
} else {
return false;
}
}
// TODO(turbofan): Do we want to support out-of-object properties?
if (!(boilerplate->HasFastProperties() &&
boilerplate->property_array()->length() == 0)) {
return false;
}
// Check the in-object properties.
Handle<DescriptorArray> descriptors(
boilerplate->map()->instance_descriptors(), isolate);
int limit = boilerplate->map()->NumberOfOwnDescriptors();
for (int i = 0; i < limit; i++) {
PropertyDetails details = descriptors->GetDetails(i);
if (details.location() != kField) continue;
DCHECK_EQ(kData, details.kind());
if ((*max_properties)-- == 0) return false;
FieldIndex field_index = FieldIndex::ForDescriptor(boilerplate->map(), i);
if (boilerplate->IsUnboxedDoubleField(field_index)) continue;
Handle<Object> value(boilerplate->RawFastPropertyAt(field_index), isolate);
if (value->IsJSObject()) {
Handle<JSObject> value_object = Handle<JSObject>::cast(value);
if (!IsFastLiteralHelper(value_object, max_depth - 1, max_properties)) {
return false;
}
}
}
return true;
}
// Maximum depth and total number of elements and properties for literal
// graphs to be considered for fast deep-copying. The limit is chosen to
// match the maximum number of inobject properties, to ensure that the
// performance of using object literals is not worse than using constructor
// functions, see crbug.com/v8/6211 for details.
const int kMaxFastLiteralDepth = 3;
const int kMaxFastLiteralProperties = JSObject::kMaxInObjectProperties;
// Determines whether the given array or object literal boilerplate satisfies
// all limits to be considered for fast deep-copying and computes the total
// size of all objects that are part of the graph.
bool IsInlinableFastLiteral(Handle<JSObject> boilerplate) {
int max_properties = kMaxFastLiteralProperties;
return IsFastLiteralHelper(boilerplate, kMaxFastLiteralDepth,
&max_properties);
}
} // namespace
class AllocationSiteData : public HeapObjectData {
public:
AllocationSiteData(JSHeapBroker* broker, Handle<AllocationSite> object,
HeapObjectType type);
void SerializeBoilerplate();
bool PointsToLiteral() const { return PointsToLiteral_; }
PretenureFlag GetPretenureMode() const { return GetPretenureMode_; }
ObjectData* nested_site() const { return nested_site_; }
bool IsFastLiteral() const { return IsFastLiteral_; }
JSObjectData* boilerplate() const { return boilerplate_; }
// These are only valid if PointsToLiteral is false.
ElementsKind GetElementsKind() const { return GetElementsKind_; }
bool CanInlineCall() const { return CanInlineCall_; }
private:
bool const PointsToLiteral_;
PretenureFlag const GetPretenureMode_;
ObjectData* nested_site_ = nullptr;
bool IsFastLiteral_ = false;
JSObjectData* boilerplate_ = nullptr;
ElementsKind GetElementsKind_ = NO_ELEMENTS;
bool CanInlineCall_ = false;
bool serialized_boilerplate_ = false;
};
// Only used in JSNativeContextSpecialization.
class ScriptContextTableData : public HeapObjectData {
public:
ScriptContextTableData(JSHeapBroker* broker,
Handle<ScriptContextTable> object, HeapObjectType type)
: HeapObjectData(broker, object, type) {}
};
struct PropertyDescriptor {
NameData* key = nullptr;
PropertyDetails details = PropertyDetails::Empty();
FieldIndex field_index;
MapData* field_owner = nullptr;
ObjectData* field_type = nullptr;
bool is_unboxed_double_field = false;
};
class MapData : public HeapObjectData {
public:
MapData(JSHeapBroker* broker, Handle<Map> object, HeapObjectType type);
InstanceType instance_type() const { return instance_type_; }
int instance_size() const { return instance_size_; }
byte bit_field() const { return bit_field_; }
byte bit_field2() const { return bit_field2_; }
uint32_t bit_field3() const { return bit_field3_; }
bool can_be_deprecated() const { return can_be_deprecated_; }
bool can_transition() const { return can_transition_; }
int in_object_properties_start_in_words() const {
CHECK(InstanceTypeChecker::IsJSObject(instance_type()));
return in_object_properties_start_in_words_;
}
int in_object_properties() const {
CHECK(InstanceTypeChecker::IsJSObject(instance_type()));
return in_object_properties_;
}
// Extra information.
void SerializeElementsKindGeneralizations();
const ZoneVector<MapData*>& elements_kind_generalizations() const {
CHECK(serialized_elements_kind_generalizations_);
return elements_kind_generalizations_;
}
// Serialize the descriptor array and, recursively, that of any field owner.
void SerializeDescriptors();
const ZoneVector<PropertyDescriptor>& descriptors() const {
CHECK(serialized_descriptors_);
return descriptors_;
}
void SerializeConstructorOrBackpointer();
ObjectData* constructor_or_backpointer() const {
CHECK(serialized_constructor_or_backpointer_);
return constructor_or_backpointer_;
}
void SerializePrototype();
ObjectData* prototype() const {
CHECK(serialized_prototype_);
return prototype_;
}
private:
InstanceType const instance_type_;
int const instance_size_;
byte const bit_field_;
byte const bit_field2_;
uint32_t const bit_field3_;
bool const can_be_deprecated_;
bool const can_transition_;
int const in_object_properties_start_in_words_;
int const in_object_properties_;
bool serialized_elements_kind_generalizations_ = false;
ZoneVector<MapData*> elements_kind_generalizations_;
bool serialized_descriptors_ = false;
ZoneVector<PropertyDescriptor> descriptors_;
bool serialized_constructor_or_backpointer_ = false;
ObjectData* constructor_or_backpointer_ = nullptr;
bool serialized_prototype_ = false;
ObjectData* prototype_ = nullptr;
};
AllocationSiteData::AllocationSiteData(JSHeapBroker* broker,
Handle<AllocationSite> object,
HeapObjectType type)
: HeapObjectData(broker, object, type),
PointsToLiteral_(object->PointsToLiteral()),
GetPretenureMode_(object->GetPretenureMode()) {
if (PointsToLiteral_) {
IsFastLiteral_ = IsInlinableFastLiteral(
handle(object->boilerplate(), broker->isolate()));
} else {
GetElementsKind_ = object->GetElementsKind();
CanInlineCall_ = object->CanInlineCall();
}
}
void AllocationSiteData::SerializeBoilerplate() {
if (serialized_boilerplate_) return;
serialized_boilerplate_ = true;
Handle<AllocationSite> site = Handle<AllocationSite>::cast(object());
CHECK(IsFastLiteral_);
DCHECK_NULL(boilerplate_);
boilerplate_ = broker()->GetOrCreateData(site->boilerplate())->AsJSObject();
boilerplate_->SerializeAsBoilerplate();
DCHECK_NULL(nested_site_);
nested_site_ = broker()->GetOrCreateData(site->nested_site());
if (nested_site_->IsAllocationSite()) {
nested_site_->AsAllocationSite()->SerializeBoilerplate();
}
}
HeapObjectData::HeapObjectData(JSHeapBroker* broker, Handle<HeapObject> object,
HeapObjectType type)
: ObjectData(broker, object, false),
type_(type),
map_(broker->GetOrCreateData(object->map())->AsMap()) {
CHECK(broker->SerializingAllowed());
}
MapData::MapData(JSHeapBroker* broker, Handle<Map> object, HeapObjectType type)
: HeapObjectData(broker, object, type),
instance_type_(object->instance_type()),
instance_size_(object->instance_size()),
bit_field_(object->bit_field()),
bit_field2_(object->bit_field2()),
bit_field3_(object->bit_field3()),
can_be_deprecated_(object->NumberOfOwnDescriptors() > 0
? object->CanBeDeprecated()
: false),
can_transition_(object->CanTransition()),
in_object_properties_start_in_words_(
object->IsJSObjectMap() ? object->GetInObjectPropertiesStartInWords()
: 0),
in_object_properties_(
object->IsJSObjectMap() ? object->GetInObjectProperties() : 0),
elements_kind_generalizations_(broker->zone()),
descriptors_(broker->zone()) {}
JSFunctionData::JSFunctionData(JSHeapBroker* broker, Handle<JSFunction> object,
HeapObjectType type)
: JSObjectData(broker, object, type),
has_initial_map_(object->has_prototype_slot() &&
object->has_initial_map()),
has_prototype_(object->has_prototype_slot() && object->has_prototype()),
PrototypeRequiresRuntimeLookup_(
object->PrototypeRequiresRuntimeLookup()) {}
void JSFunctionData::Serialize() {
if (serialized_) return;
serialized_ = true;
Handle<JSFunction> function = Handle<JSFunction>::cast(object());
DCHECK_NULL(global_proxy_);
DCHECK_NULL(initial_map_);
DCHECK_NULL(prototype_);
DCHECK_NULL(shared_);
global_proxy_ =
broker()->GetOrCreateData(function->global_proxy())->AsJSGlobalProxy();
shared_ =
broker()->GetOrCreateData(function->shared())->AsSharedFunctionInfo();
initial_map_ =
has_initial_map()
? broker()->GetOrCreateData(function->initial_map())->AsMap()
: nullptr;
prototype_ = has_prototype()
? broker()->GetOrCreateData(function->prototype())
: nullptr;
if (initial_map_ != nullptr) {
initial_map_instance_size_with_min_slack_ =
function->ComputeInstanceSizeWithMinSlack(broker()->isolate());
if (initial_map_->instance_type() == JS_ARRAY_TYPE) {
initial_map_->SerializeElementsKindGeneralizations();
}
initial_map_->SerializeConstructorOrBackpointer();
// TODO(neis): This is currently only needed for native_context's
// object_function, as used by GetObjectCreateMap. If no further use sites
// show up, we should move this into NativeContextData::Serialize.
initial_map_->SerializePrototype();
}
}
void MapData::SerializeElementsKindGeneralizations() {
if (serialized_elements_kind_generalizations_) return;
serialized_elements_kind_generalizations_ = true;
broker()->Trace("Computing ElementsKind generalizations of %p.\n", *object());
DCHECK_EQ(instance_type(), JS_ARRAY_TYPE);
MapRef self(this);
ElementsKind from_kind = self.elements_kind();
DCHECK(elements_kind_generalizations_.empty());
for (int i = FIRST_FAST_ELEMENTS_KIND; i <= LAST_FAST_ELEMENTS_KIND; i++) {
ElementsKind to_kind = static_cast<ElementsKind>(i);
if (IsMoreGeneralElementsKindTransition(from_kind, to_kind)) {
Handle<Map> target =
Map::AsElementsKind(broker()->isolate(), self.object<Map>(), to_kind);
elements_kind_generalizations_.push_back(
broker()->GetOrCreateData(target)->AsMap());
}
}
}
class FeedbackVectorData : public HeapObjectData {
public:
const ZoneVector<ObjectData*>& feedback() { return feedback_; }
FeedbackVectorData(JSHeapBroker* broker, Handle<FeedbackVector> object,
HeapObjectType type);
void SerializeSlots();
private:
bool serialized_ = false;
ZoneVector<ObjectData*> feedback_;
};
FeedbackVectorData::FeedbackVectorData(JSHeapBroker* broker,
Handle<FeedbackVector> object,
HeapObjectType type)
: HeapObjectData(broker, object, type), feedback_(broker->zone()) {}
void FeedbackVectorData::SerializeSlots() {
if (serialized_) return;
serialized_ = true;
Handle<FeedbackVector> vector = Handle<FeedbackVector>::cast(object());
DCHECK(feedback_.empty());
feedback_.reserve(vector->length());
for (int i = 0; i < vector->length(); ++i) {
MaybeObject* value = vector->get(i);
ObjectData* slot_value =
value->IsObject() ? broker()->GetOrCreateData(value->cast<Object>())
: nullptr;
feedback_.push_back(slot_value);
if (slot_value == nullptr) continue;
if (slot_value->IsAllocationSite() &&
slot_value->AsAllocationSite()->IsFastLiteral()) {
slot_value->AsAllocationSite()->SerializeBoilerplate();
} else if (slot_value->IsJSRegExp()) {
slot_value->AsJSRegExp()->SerializeAsRegExpBoilerplate();
}
}
DCHECK_EQ(vector->length(), feedback_.size());
}
class FixedArrayBaseData : public HeapObjectData {
public:
FixedArrayBaseData(JSHeapBroker* broker, Handle<FixedArrayBase> object,
HeapObjectType type)
: HeapObjectData(broker, object, type), length_(object->length()) {}
int length() const { return length_; }
private:
int const length_;
};
JSObjectData::JSObjectData(JSHeapBroker* broker, Handle<JSObject> object,
HeapObjectType type)
: HeapObjectData(broker, object, type), inobject_fields_(broker->zone()) {}
class FixedArrayData : public FixedArrayBaseData {
public:
FixedArrayData(JSHeapBroker* broker, Handle<FixedArray> object,
HeapObjectType type);
// Creates all elements of the fixed array.
void SerializeContents();
ObjectData* Get(int i) const;
private:
bool serialized_contents_ = false;
ZoneVector<ObjectData*> contents_;
};
void FixedArrayData::SerializeContents() {
if (serialized_contents_) return;
serialized_contents_ = true;
Handle<FixedArray> array = Handle<FixedArray>::cast(object());
CHECK_EQ(array->length(), length());
CHECK(contents_.empty());
contents_.reserve(static_cast<size_t>(length()));
for (int i = 0; i < length(); i++) {
Handle<Object> value(array->get(i), broker()->isolate());
contents_.push_back(broker()->GetOrCreateData(value));
}
}
FixedArrayData::FixedArrayData(JSHeapBroker* broker, Handle<FixedArray> object,
HeapObjectType type)
: FixedArrayBaseData(broker, object, type), contents_(broker->zone()) {}
class FixedDoubleArrayData : public FixedArrayBaseData {
public:
FixedDoubleArrayData(JSHeapBroker* broker, Handle<FixedDoubleArray> object,
HeapObjectType type);
// Serializes all elements of the fixed array.
void SerializeContents();
Float64 Get(int i) const;
private:
bool serialized_contents_ = false;
ZoneVector<Float64> contents_;
};
FixedDoubleArrayData::FixedDoubleArrayData(JSHeapBroker* broker,
Handle<FixedDoubleArray> object,
HeapObjectType type)
: FixedArrayBaseData(broker, object, type), contents_(broker->zone()) {}
void FixedDoubleArrayData::SerializeContents() {
if (serialized_contents_) return;
serialized_contents_ = true;
Handle<FixedDoubleArray> self = Handle<FixedDoubleArray>::cast(object());
CHECK_EQ(self->length(), length());
CHECK(contents_.empty());
contents_.reserve(static_cast<size_t>(length()));
for (int i = 0; i < length(); i++) {
contents_.push_back(Float64::FromBits(self->get_representation(i)));
}
}
class BytecodeArrayData : public FixedArrayBaseData {
public:
int register_count() const { return register_count_; }
BytecodeArrayData(JSHeapBroker* broker, Handle<BytecodeArray> object,
HeapObjectType type)
: FixedArrayBaseData(broker, object, type),
register_count_(object->register_count()) {}
private:
int const register_count_;
};
class JSArrayData : public JSObjectData {
public:
JSArrayData(JSHeapBroker* broker, Handle<JSArray> object,
HeapObjectType type);
void Serialize();
ObjectData* length() const { return length_; }
private:
bool serialized_ = false;
ObjectData* length_ = nullptr;
};
JSArrayData::JSArrayData(JSHeapBroker* broker, Handle<JSArray> object,
HeapObjectType type)
: JSObjectData(broker, object, type) {}
void JSArrayData::Serialize() {
if (serialized_) return;
serialized_ = true;
Handle<JSArray> jsarray = Handle<JSArray>::cast(object());
DCHECK_NULL(length_);
length_ = broker()->GetOrCreateData(jsarray->length());
}
class ScopeInfoData : public HeapObjectData {
public:
ScopeInfoData(JSHeapBroker* broker, Handle<ScopeInfo> object,
HeapObjectType type);
int context_length() const { return context_length_; }
private:
int const context_length_;
};
ScopeInfoData::ScopeInfoData(JSHeapBroker* broker, Handle<ScopeInfo> object,
HeapObjectType type)
: HeapObjectData(broker, object, type),
context_length_(object->ContextLength()) {}
class SharedFunctionInfoData : public HeapObjectData {
public:
int builtin_id() const { return builtin_id_; }
BytecodeArrayData* GetBytecodeArray() const { return GetBytecodeArray_; }
#define DECL_ACCESSOR(type, name) \
type name() const { return name##_; }
BROKER_SFI_FIELDS(DECL_ACCESSOR)
#undef DECL_ACCESSOR
SharedFunctionInfoData(JSHeapBroker* broker,
Handle<SharedFunctionInfo> object, HeapObjectType type)
: HeapObjectData(broker, object, type),
builtin_id_(object->HasBuiltinId() ? object->builtin_id()
: Builtins::kNoBuiltinId),
GetBytecodeArray_(
object->HasBytecodeArray()
? broker->GetOrCreateData(object->GetBytecodeArray())
->AsBytecodeArray()
: nullptr)
#define INIT_MEMBER(type, name) , name##_(object->name())
BROKER_SFI_FIELDS(INIT_MEMBER)
#undef INIT_MEMBER
{
DCHECK_EQ(HasBuiltinId_, builtin_id_ != Builtins::kNoBuiltinId);
DCHECK_EQ(HasBytecodeArray_, GetBytecodeArray_ != nullptr);
}
private:
int const builtin_id_;
BytecodeArrayData* const GetBytecodeArray_;
#define DECL_MEMBER(type, name) type const name##_;
BROKER_SFI_FIELDS(DECL_MEMBER)
#undef DECL_MEMBER
};
class ModuleData : public HeapObjectData {
public:
ModuleData(JSHeapBroker* broker, Handle<Module> object, HeapObjectType type);
void Serialize();
CellData* GetCell(int cell_index) const;
private:
bool serialized_ = false;
ZoneVector<CellData*> imports_;
ZoneVector<CellData*> exports_;
};
ModuleData::ModuleData(JSHeapBroker* broker, Handle<Module> object,
HeapObjectType type)
: HeapObjectData(broker, object, type),
imports_(broker->zone()),
exports_(broker->zone()) {}
CellData* ModuleData::GetCell(int cell_index) const {
CHECK(serialized_);
CellData* cell;
switch (ModuleDescriptor::GetCellIndexKind(cell_index)) {
case ModuleDescriptor::kImport:
cell = imports_.at(Module::ImportIndex(cell_index));
break;
case ModuleDescriptor::kExport:
cell = exports_.at(Module::ExportIndex(cell_index));
break;
case ModuleDescriptor::kInvalid:
UNREACHABLE();
break;
}
CHECK_NOT_NULL(cell);
return cell;
}
void ModuleData::Serialize() {
if (serialized_) return;
serialized_ = true;
Handle<Module> module = Handle<Module>::cast(object());
// TODO(neis): We could be smarter and only serialize the cells we care about.
// TODO(neis): Define a helper for serializing a FixedArray into a ZoneVector.
DCHECK(imports_.empty());
Handle<FixedArray> imports(module->regular_imports(), broker()->isolate());
int const imports_length = imports->length();
imports_.reserve(imports_length);
for (int i = 0; i < imports_length; ++i) {
imports_.push_back(broker()->GetOrCreateData(imports->get(i))->AsCell());
}
DCHECK(exports_.empty());
Handle<FixedArray> exports(module->regular_exports(), broker()->isolate());
int const exports_length = exports->length();
exports_.reserve(exports_length);
for (int i = 0; i < exports_length; ++i) {
exports_.push_back(broker()->GetOrCreateData(exports->get(i))->AsCell());
}
}
class CellData : public HeapObjectData {
public:
CellData(JSHeapBroker* broker, Handle<Cell> object, HeapObjectType type)
: HeapObjectData(broker, object, type) {}
};
class JSGlobalProxyData : public JSObjectData {
public:
JSGlobalProxyData(JSHeapBroker* broker, Handle<JSGlobalProxy> object,
HeapObjectType type)
: JSObjectData(broker, object, type) {}
};
class CodeData : public HeapObjectData {
public:
CodeData(JSHeapBroker* broker, Handle<Code> object, HeapObjectType type)
: HeapObjectData(broker, object, type) {}
};
#define DEFINE_IS_AND_AS(Name) \
bool ObjectData::Is##Name() const { \
if (broker()->mode() == JSHeapBroker::kDisabled) { \
AllowHandleDereference allow_handle_dereference; \
return object()->Is##Name(); \
} \
if (is_smi()) return false; \
InstanceType instance_type = \
static_cast<const HeapObjectData*>(this)->type().instance_type(); \
return InstanceTypeChecker::Is##Name(instance_type); \
} \
Name##Data* ObjectData::As##Name() { \
CHECK_NE(broker()->mode(), JSHeapBroker::kDisabled); \
CHECK(Is##Name()); \
return static_cast<Name##Data*>(this); \
}
HEAP_BROKER_OBJECT_LIST(DEFINE_IS_AND_AS)
#undef DEFINE_IS_AND_AS
const JSObjectField& JSObjectData::GetInobjectField(int property_index) const {
CHECK_LT(static_cast<size_t>(property_index), inobject_fields_.size());
return inobject_fields_[property_index];
}
bool JSObjectData::cow_or_empty_elements_tenured() const {
return cow_or_empty_elements_tenured_;
}
FixedArrayBaseData* JSObjectData::elements() const { return elements_; }
void JSObjectData::SerializeAsBoilerplate() {
SerializeRecursive(kMaxFastLiteralDepth);
}
void JSObjectData::SerializeElements() {
if (serialized_elements_) return;
serialized_elements_ = true;
Handle<JSObject> boilerplate = Handle<JSObject>::cast(object());
Handle<FixedArrayBase> elements_object(boilerplate->elements(),
broker()->isolate());
DCHECK_NULL(elements_);
elements_ = broker()->GetOrCreateData(elements_object)->AsFixedArrayBase();
}
void MapData::SerializeConstructorOrBackpointer() {
if (serialized_constructor_or_backpointer_) return;
serialized_constructor_or_backpointer_ = true;
Handle<Map> map = Handle<Map>::cast(object());
DCHECK_NULL(constructor_or_backpointer_);
constructor_or_backpointer_ =
broker()->GetOrCreateData(map->constructor_or_backpointer());
}
void MapData::SerializePrototype() {
if (serialized_prototype_) return;
serialized_prototype_ = true;
Handle<Map> map = Handle<Map>::cast(object());
DCHECK_NULL(prototype_);
prototype_ = broker()->GetOrCreateData(map->prototype());
}
void MapData::SerializeDescriptors() {
if (serialized_descriptors_) return;
serialized_descriptors_ = true;
Handle<Map> map = Handle<Map>::cast(object());
Isolate* const isolate = broker()->isolate();
Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate);
// We copy all descriptors (not only the own) in order to support
// FindFieldOwner, which is used by the FieldType compilation dependency.
int const number_of_descriptors = descriptors->number_of_descriptors();
DCHECK(descriptors_.empty());
descriptors_.reserve(number_of_descriptors);
for (int i = 0; i < number_of_descriptors; ++i) {
PropertyDescriptor d;
d.key = broker()->GetOrCreateData(descriptors->GetKey(i))->AsName();
d.details = descriptors->GetDetails(i);
if (d.details.location() == kField) {
d.field_index = FieldIndex::ForDescriptor(*map, i);
d.field_owner =
broker()->GetOrCreateData(map->FindFieldOwner(isolate, i))->AsMap();
d.field_type = broker()->GetOrCreateData(descriptors->GetFieldType(i));
d.is_unboxed_double_field = map->IsUnboxedDoubleField(d.field_index);
// Recurse.
d.field_owner->SerializeDescriptors();
}
descriptors_.push_back(d);
}
}
void JSObjectData::SerializeRecursive(int depth) {
if (serialized_as_boilerplate_) return;
serialized_as_boilerplate_ = true;
Handle<JSObject> boilerplate = Handle<JSObject>::cast(object());
// We only serialize boilerplates that pass the IsInlinableFastLiteral
// check, so we only do a sanity check on the depth here.
CHECK_GT(depth, 0);
CHECK(!boilerplate->map()->is_deprecated());
// Serialize the elements.
Isolate* const isolate = broker()->isolate();
Handle<FixedArrayBase> elements_object(boilerplate->elements(), isolate);
// Boilerplates need special serialization - we need to make sure COW arrays
// are tenured. Boilerplate objects should only be reachable from their
// allocation site, so it is safe to assume that the elements have not been
// serialized yet.
bool const empty_or_cow =
elements_object->length() == 0 ||
elements_object->map() == ReadOnlyRoots(isolate).fixed_cow_array_map();
if (empty_or_cow) {
// We need to make sure copy-on-write elements are tenured.
if (Heap::InNewSpace(*elements_object)) {
elements_object =
broker()->isolate()->factory()->CopyAndTenureFixedCOWArray(
Handle<FixedArray>::cast(elements_object));
boilerplate->set_elements(*elements_object);
}
cow_or_empty_elements_tenured_ = true;
}
DCHECK_NULL(elements_);
elements_ = broker()->GetOrCreateData(elements_object)->AsFixedArrayBase();
if (empty_or_cow) {
// No need to do anything here. Empty or copy-on-write elements
// do not need to be serialized because we only need to store the elements
// reference to the allocated object.
} else if (boilerplate->HasSmiOrObjectElements()) {
elements_->AsFixedArray()->SerializeContents();
Handle<FixedArray> fast_elements =
Handle<FixedArray>::cast(elements_object);
int length = elements_object->length();
for (int i = 0; i < length; i++) {
Handle<Object> value(fast_elements->get(i), isolate);
if (value->IsJSObject()) {
ObjectData* value_data = broker()->GetOrCreateData(value);
value_data->AsJSObject()->SerializeRecursive(depth - 1);
}
}
} else {
CHECK(boilerplate->HasDoubleElements());
CHECK_LE(elements_object->Size(), kMaxRegularHeapObjectSize);
elements_->AsFixedDoubleArray()->SerializeContents();
}
// TODO(turbofan): Do we want to support out-of-object properties?
CHECK(boilerplate->HasFastProperties() &&
boilerplate->property_array()->length() == 0);
CHECK_EQ(inobject_fields_.size(), 0u);
// Check the in-object properties.
Handle<DescriptorArray> descriptors(
boilerplate->map()->instance_descriptors(), isolate);
int const limit = boilerplate->map()->NumberOfOwnDescriptors();
for (int i = 0; i < limit; i++) {
PropertyDetails details = descriptors->GetDetails(i);
if (details.location() != kField) continue;
DCHECK_EQ(kData, details.kind());
FieldIndex field_index = FieldIndex::ForDescriptor(boilerplate->map(), i);
// Make sure {field_index} agrees with {inobject_properties} on the index of
// this field.
DCHECK_EQ(field_index.property_index(),
static_cast<int>(inobject_fields_.size()));
if (boilerplate->IsUnboxedDoubleField(field_index)) {
double value = boilerplate->RawFastDoublePropertyAt(field_index);
inobject_fields_.push_back(JSObjectField{value});
} else {
Handle<Object> value(boilerplate->RawFastPropertyAt(field_index),
isolate);
ObjectData* value_data = broker()->GetOrCreateData(value);
if (value->IsJSObject()) {
value_data->AsJSObject()->SerializeRecursive(depth - 1);
}
inobject_fields_.push_back(JSObjectField{value_data});
}
}
map()->SerializeDescriptors();
if (IsJSArray()) AsJSArray()->Serialize();
}
void JSRegExpData::SerializeAsRegExpBoilerplate() {
if (serialized_as_reg_exp_boilerplate_) return;
serialized_as_reg_exp_boilerplate_ = true;
SerializeElements();
Handle<JSRegExp> boilerplate = Handle<JSRegExp>::cast(object());
raw_properties_or_hash_ =
broker()->GetOrCreateData(boilerplate->raw_properties_or_hash());
data_ = broker()->GetOrCreateData(boilerplate->data());
source_ = broker()->GetOrCreateData(boilerplate->source());
flags_ = broker()->GetOrCreateData(boilerplate->flags());
last_index_ = broker()->GetOrCreateData(boilerplate->last_index());
}
ObjectData* ObjectData::Serialize(JSHeapBroker* broker, Handle<Object> object) {
CHECK(broker->SerializingAllowed());
return object->IsSmi() ? new (broker->zone()) ObjectData(broker, object, true)
: HeapObjectData::Serialize(
broker, Handle<HeapObject>::cast(object));
}
HeapObjectData* HeapObjectData::Serialize(JSHeapBroker* broker,
Handle<HeapObject> object) {
CHECK(broker->SerializingAllowed());
Handle<Map> map(object->map(), broker->isolate());
HeapObjectType type = broker->HeapObjectTypeFromMap(map);
#define RETURN_CREATE_DATA_IF_MATCH(name) \
if (object->Is##name()) { \
return new (broker->zone()) \
name##Data(broker, Handle<name>::cast(object), type); \
}
HEAP_BROKER_OBJECT_LIST(RETURN_CREATE_DATA_IF_MATCH)
#undef RETURN_CREATE_DATA_IF_MATCH
UNREACHABLE();
}
bool ObjectRef::equals(const ObjectRef& other) const {
return data_ == other.data_;
}
Isolate* ObjectRef::isolate() const { return broker()->isolate(); }
ContextRef ContextRef::previous() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
return ContextRef(
broker(), handle(object<Context>()->previous(), broker()->isolate()));
}
return ContextRef(data()->AsContext()->previous());
}
// Not needed for TypedLowering.
ObjectRef ContextRef::get(int index) const {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
Handle<Object> value(object<Context>()->get(index), broker()->isolate());
return ObjectRef(broker(), value);
}
JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* zone)
: isolate_(isolate),
zone_(zone),
refs_(zone),
mode_(FLAG_concurrent_compiler_frontend ? kSerializing : kDisabled) {
Trace("%s", "Constructing heap broker.\n");
}
void JSHeapBroker::Trace(const char* format, ...) const {
if (FLAG_trace_heap_broker) {
PrintF("[%p] ", this);
va_list arguments;
va_start(arguments, format);
base::OS::VPrint(format, arguments);
va_end(arguments);
}
}
bool JSHeapBroker::SerializingAllowed() const {
return mode() == kSerializing ||
(!FLAG_strict_heap_broker && mode() == kSerialized);
}
void JSHeapBroker::SerializeStandardObjects() {
if (mode() == kDisabled) return;
Trace("Serializing standard objects.\n");
Builtins* const b = isolate()->builtins();
Factory* const f = isolate()->factory();
// Maps, strings, oddballs
GetOrCreateData(f->NaN_string());
GetOrCreateData(f->bigint_string());
GetOrCreateData(f->block_context_map());
GetOrCreateData(f->boolean_string());
GetOrCreateData(f->catch_context_map());
GetOrCreateData(f->empty_fixed_array());
GetOrCreateData(f->empty_string());
GetOrCreateData(f->eval_context_map());
GetOrCreateData(f->false_string());
GetOrCreateData(f->false_value());
GetOrCreateData(f->fixed_array_map());
GetOrCreateData(f->fixed_cow_array_map());
GetOrCreateData(f->fixed_double_array_map());
GetOrCreateData(f->function_context_map());
GetOrCreateData(f->function_string());
GetOrCreateData(f->heap_number_map());
GetOrCreateData(f->length_string());
GetOrCreateData(f->many_closures_cell_map());
GetOrCreateData(f->minus_zero_value());
GetOrCreateData(f->mutable_heap_number_map());
GetOrCreateData(f->name_dictionary_map());
GetOrCreateData(f->null_string());
GetOrCreateData(f->null_value());
GetOrCreateData(f->number_string());
GetOrCreateData(f->object_string());
GetOrCreateData(f->one_pointer_filler_map());
GetOrCreateData(f->optimized_out());
GetOrCreateData(f->property_array_map());
GetOrCreateData(f->sloppy_arguments_elements_map());
GetOrCreateData(f->stale_register());
GetOrCreateData(f->string_string());
GetOrCreateData(f->symbol_string());
GetOrCreateData(f->the_hole_value());
GetOrCreateData(f->true_string());
GetOrCreateData(f->true_value());
GetOrCreateData(f->undefined_string());
GetOrCreateData(f->undefined_value());
GetOrCreateData(f->with_context_map());
// Property cells
GetOrCreateData(f->array_buffer_neutering_protector())
->AsPropertyCell()
->Serialize();
GetOrCreateData(f->array_iterator_protector())->AsPropertyCell()->Serialize();
GetOrCreateData(f->array_species_protector())->AsPropertyCell()->Serialize();
GetOrCreateData(f->no_elements_protector())->AsPropertyCell()->Serialize();
GetOrCreateData(f->promise_hook_protector())->AsPropertyCell()->Serialize();
GetOrCreateData(f->promise_species_protector())
->AsPropertyCell()
->Serialize();
GetOrCreateData(f->promise_then_protector())->AsPropertyCell()->Serialize();
// Builtins
{
Builtins::Name builtins[] = {
Builtins::kAllocateInNewSpace,
Builtins::kAllocateInOldSpace,
Builtins::kArgumentsAdaptorTrampoline,
Builtins::kArrayConstructorImpl,
Builtins::kCallFunctionForwardVarargs,
Builtins::kCallFunction_ReceiverIsAny,
Builtins::kCallFunction_ReceiverIsNotNullOrUndefined,
Builtins::kCallFunction_ReceiverIsNullOrUndefined,
Builtins::kConstructFunctionForwardVarargs,
Builtins::kForInFilter,
Builtins::kJSBuiltinsConstructStub,
Builtins::kJSConstructStubGeneric,
Builtins::kStringAdd_CheckNone,
Builtins::kStringAdd_ConvertLeft,
Builtins::kStringAdd_ConvertRight,
Builtins::kToNumber,
Builtins::kToObject,
};
for (auto id : builtins) {
GetOrCreateData(b->builtin_handle(id));
}
}
for (int32_t id = 0; id < Builtins::builtin_count; ++id) {
if (Builtins::KindOf(id) == Builtins::TFJ) {
GetOrCreateData(b->builtin_handle(id));
}
}
// Stubs
GetOrCreateData(
CodeFactory::CEntry(isolate(), 1, kDontSaveFPRegs, kArgvOnStack, true));
{
ElementsKind kinds[] = {HOLEY_SMI_ELEMENTS, HOLEY_DOUBLE_ELEMENTS,
HOLEY_ELEMENTS};
for (auto kind : kinds) {
GetOrCreateData(CodeFactory::ArrayNoArgumentConstructor(isolate(), kind,
DONT_OVERRIDE)
.code());
GetOrCreateData(CodeFactory::ArrayNoArgumentConstructor(
isolate(), kind, DISABLE_ALLOCATION_SITES)
.code());
GetOrCreateData(CodeFactory::ArraySingleArgumentConstructor(
isolate(), kind, DONT_OVERRIDE)
.code());
GetOrCreateData(CodeFactory::ArraySingleArgumentConstructor(
isolate(), kind, DISABLE_ALLOCATION_SITES)
.code());
}
}
GetOrCreateData(isolate()->native_context())->AsNativeContext()->Serialize();
Trace("Finished serializing standard objects.\n");
}
HeapObjectType JSHeapBroker::HeapObjectTypeFromMap(Map* map) const {
AllowHandleDereference allow_handle_dereference;
OddballType oddball_type = OddballType::kNone;
if (map->instance_type() == ODDBALL_TYPE) {
ReadOnlyRoots roots(isolate_);
if (map == roots.undefined_map()) {
oddball_type = OddballType::kUndefined;
} else if (map == roots.null_map()) {
oddball_type = OddballType::kNull;
} else if (map == roots.boolean_map()) {
oddball_type = OddballType::kBoolean;
} else if (map == roots.the_hole_map()) {
oddball_type = OddballType::kHole;
} else if (map == roots.uninitialized_map()) {
oddball_type = OddballType::kUninitialized;
} else {
oddball_type = OddballType::kOther;
DCHECK(map == roots.termination_exception_map() ||
map == roots.arguments_marker_map() ||
map == roots.optimized_out_map() ||
map == roots.stale_register_map());
}
}
HeapObjectType::Flags flags(0);
if (map->is_undetectable()) flags |= HeapObjectType::kUndetectable;
if (map->is_callable()) flags |= HeapObjectType::kCallable;
return HeapObjectType(map->instance_type(), flags, oddball_type);
}
ObjectData* JSHeapBroker::GetData(Handle<Object> object) const {
auto it = refs_.find(object.address());
return it != refs_.end() ? it->second : nullptr;
}
ObjectData* JSHeapBroker::GetOrCreateData(Handle<Object> object) {
CHECK(SerializingAllowed());
ObjectData* data = GetData(object);
if (data == nullptr) {
// TODO(neis): Remove these Allow* once we serialize everything upfront.
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
// TODO(neis): Inline Serialize here, now that we have subclass-specific
// Serialize methods.
data = ObjectData::Serialize(this, object);
}
CHECK_NOT_NULL(data);
return data;
}
ObjectData* JSHeapBroker::GetOrCreateData(Object* object) {
return GetOrCreateData(handle(object, isolate()));
}
void JSHeapBroker::AddData(Handle<Object> object, ObjectData* data) {
Trace("Creating data %p for handle %" V8PRIuPTR " (", data, object.address());
if (FLAG_trace_heap_broker) {
object->ShortPrint();
PrintF(")\n");
}
CHECK_NOT_NULL(isolate()->handle_scope_data()->canonical_scope);
CHECK(refs_.insert({object.address(), data}).second);
}
#define DEFINE_IS_AND_AS(Name) \
bool ObjectRef::Is##Name() const { return data()->Is##Name(); } \
Name##Ref ObjectRef::As##Name() const { \
DCHECK(Is##Name()); \
return Name##Ref(data()); \
}
HEAP_BROKER_OBJECT_LIST(DEFINE_IS_AND_AS)
#undef DEFINE_IS_AND_AS
bool ObjectRef::IsSmi() const { return data()->is_smi(); }
int ObjectRef::AsSmi() const {
DCHECK(IsSmi());
// Handle-dereference is always allowed for Handle<Smi>.
return object<Smi>()->value();
}
HeapObjectType HeapObjectRef::type() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return broker()->HeapObjectTypeFromMap(object<HeapObject>()->map());
}
return data()->AsHeapObject()->type();
}
NativeContextRef JSHeapBroker::native_context() {
AllowHandleAllocation handle_allocation;
return NativeContextRef(this, isolate()->native_context());
}
base::Optional<MapRef> JSObjectRef::GetObjectCreateMap() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
AllowHeapAllocation heap_allocation;
Handle<Map> instance_map;
if (Map::TryGetObjectCreateMap(broker()->isolate(), object<HeapObject>())
.ToHandle(&instance_map)) {
return MapRef(broker(), instance_map);
} else {
return base::Optional<MapRef>();
}
}
MapData* map_data = data()->AsJSObject()->object_create_map();
return map_data != nullptr ? MapRef(map_data) : base::Optional<MapRef>();
}
base::Optional<MapRef> MapRef::AsElementsKind(ElementsKind kind) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHeapAllocation heap_allocation;
AllowHandleDereference allow_handle_dereference;
return MapRef(broker(), Map::AsElementsKind(broker()->isolate(),
object<Map>(), kind));
} else {
if (kind == elements_kind()) return *this;
const ZoneVector<MapData*>& elements_kind_generalizations =
data()->AsMap()->elements_kind_generalizations();
for (auto data : elements_kind_generalizations) {
MapRef map(data);
if (map.elements_kind() == kind) return map;
}
return base::Optional<MapRef>();
}
}
int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation handle_allocation;
return object<JSFunction>()->ComputeInstanceSizeWithMinSlack(
broker()->isolate());
}
return data()->AsJSFunction()->initial_map_instance_size_with_min_slack();
}
// Not needed for TypedLowering.
base::Optional<ScriptContextTableRef::LookupResult>
ScriptContextTableRef::lookup(const NameRef& name) const {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
if (!name.IsString()) return {};
ScriptContextTable::LookupResult lookup_result;
auto table = object<ScriptContextTable>();
if (!ScriptContextTable::Lookup(broker()->isolate(), table,
name.object<String>(), &lookup_result)) {
return {};
}
Handle<Context> script_context = ScriptContextTable::GetContext(
broker()->isolate(), table, lookup_result.context_index);
LookupResult result{ContextRef(broker(), script_context),
lookup_result.mode == VariableMode::kConst,
lookup_result.slot_index};
return result;
}
OddballType ObjectRef::oddball_type() const {
return IsSmi() ? OddballType::kNone : AsHeapObject().type().oddball_type();
}
ObjectRef FeedbackVectorRef::get(FeedbackSlot slot) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
Handle<Object> value(object<FeedbackVector>()->Get(slot)->cast<Object>(),
broker()->isolate());
return ObjectRef(broker(), value);
}
int i = FeedbackVector::GetIndex(slot);
return ObjectRef(data()->AsFeedbackVector()->feedback().at(i));
}
double JSObjectRef::RawFastDoublePropertyAt(FieldIndex index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference handle_dereference;
return object<JSObject>()->RawFastDoublePropertyAt(index);
}
JSObjectData* object_data = data()->AsJSObject();
CHECK(index.is_inobject());
return object_data->GetInobjectField(index.property_index()).AsDouble();
}
ObjectRef JSObjectRef::RawFastPropertyAt(FieldIndex index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
return ObjectRef(broker(),
handle(object<JSObject>()->RawFastPropertyAt(index),
broker()->isolate()));
}
JSObjectData* object_data = data()->AsJSObject();
CHECK(index.is_inobject());
return ObjectRef(
object_data->GetInobjectField(index.property_index()).AsObject());
}
bool AllocationSiteRef::IsFastLiteral() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHeapAllocation allow_heap_allocation; // For TryMigrateInstance.
AllowHandleAllocation allow_handle_allocation;
AllowHandleDereference allow_handle_dereference;
return IsInlinableFastLiteral(
handle(object<AllocationSite>()->boilerplate(), broker()->isolate()));
}
return data()->AsAllocationSite()->IsFastLiteral();
}
void JSObjectRef::EnsureElementsTenured() {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation allow_handle_allocation;
AllowHandleDereference allow_handle_dereference;
AllowHeapAllocation allow_heap_allocation;
Handle<FixedArrayBase> object_elements =
elements().object<FixedArrayBase>();
if (Heap::InNewSpace(*object_elements)) {
// If we would like to pretenure a fixed cow array, we must ensure that
// the array is already in old space, otherwise we'll create too many
// old-to-new-space pointers (overflowing the store buffer).
object_elements =
broker()->isolate()->factory()->CopyAndTenureFixedCOWArray(
Handle<FixedArray>::cast(object_elements));
object<JSObject>()->set_elements(*object_elements);
}
return;
}
CHECK(data()->AsJSObject()->cow_or_empty_elements_tenured());
}
FieldIndex MapRef::GetFieldIndexFor(int descriptor_index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return FieldIndex::ForDescriptor(*object<Map>(), descriptor_index);
}
return data()->AsMap()->descriptors().at(descriptor_index).field_index;
}
int MapRef::GetInObjectPropertyOffset(int i) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return object<Map>()->GetInObjectPropertyOffset(i);
}
return (GetInObjectPropertiesStartInWords() + i) * kPointerSize;
}
PropertyDetails MapRef::GetPropertyDetails(int descriptor_index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return object<Map>()->instance_descriptors()->GetDetails(descriptor_index);
}
return data()->AsMap()->descriptors().at(descriptor_index).details;
}
NameRef MapRef::GetPropertyKey(int descriptor_index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
return NameRef(
broker(),
handle(object<Map>()->instance_descriptors()->GetKey(descriptor_index),
broker()->isolate()));
}
return NameRef(data()->AsMap()->descriptors().at(descriptor_index).key);
}
bool MapRef::IsFixedCowArrayMap() const {
Handle<Map> fixed_cow_array_map =
ReadOnlyRoots(broker()->isolate()).fixed_cow_array_map_handle();
return equals(MapRef(broker(), fixed_cow_array_map));
}
MapRef MapRef::FindFieldOwner(int descriptor_index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
Handle<Map> owner(
object<Map>()->FindFieldOwner(broker()->isolate(), descriptor_index),
broker()->isolate());
return MapRef(broker(), owner);
}
return MapRef(
data()->AsMap()->descriptors().at(descriptor_index).field_owner);
}
ObjectRef MapRef::GetFieldType(int descriptor_index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
Handle<FieldType> field_type(
object<Map>()->instance_descriptors()->GetFieldType(descriptor_index),
broker()->isolate());
return ObjectRef(broker(), field_type);
}
return ObjectRef(
data()->AsMap()->descriptors().at(descriptor_index).field_type);
}
bool MapRef::IsUnboxedDoubleField(int descriptor_index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return object<Map>()->IsUnboxedDoubleField(
FieldIndex::ForDescriptor(*object<Map>(), descriptor_index));
}
return data()
->AsMap()
->descriptors()
.at(descriptor_index)
.is_unboxed_double_field;
}
uint16_t StringRef::GetFirstChar() {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return object<String>()->Get(0);
}
return data()->AsString()->first_char();
}
base::Optional<double> StringRef::ToNumber() {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation allow_handle_allocation;
AllowHeapAllocation allow_heap_allocation;
int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY;
return StringToDouble(broker()->isolate(),
broker()->isolate()->unicode_cache(),
object<String>(), flags);
}
return data()->AsString()->to_number();
}
ObjectRef FixedArrayRef::get(int i) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
return ObjectRef(broker(),
handle(object<FixedArray>()->get(i), broker()->isolate()));
}
return ObjectRef(data()->AsFixedArray()->Get(i));
}
bool FixedDoubleArrayRef::is_the_hole(int i) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return object<FixedDoubleArray>()->is_the_hole(i);
}
return data()->AsFixedDoubleArray()->Get(i).is_hole_nan();
}
double FixedDoubleArrayRef::get_scalar(int i) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
return object<FixedDoubleArray>()->get_scalar(i);
}
CHECK(!data()->AsFixedDoubleArray()->Get(i).is_hole_nan());
return data()->AsFixedDoubleArray()->Get(i).get_scalar();
}
#define IF_BROKER_DISABLED_ACCESS_HANDLE_C(holder, name) \
if (broker()->mode() == JSHeapBroker::kDisabled) { \
AllowHandleAllocation handle_allocation; \
AllowHandleDereference allow_handle_dereference; \
return object<holder>()->name(); \
}
#define IF_BROKER_DISABLED_ACCESS_HANDLE(holder, result, name) \
if (broker()->mode() == JSHeapBroker::kDisabled) { \
AllowHandleAllocation handle_allocation; \
AllowHandleDereference allow_handle_dereference; \
return result##Ref(broker(), \
handle(object<holder>()->name(), broker()->isolate())); \
}
// Macros for definining a const getter that, depending on the broker mode,
// either looks into the handle or into the serialized data.
#define BIMODAL_ACCESSOR(holder, result, name) \
result##Ref holder##Ref::name() const { \
IF_BROKER_DISABLED_ACCESS_HANDLE(holder, result, name); \
return result##Ref(ObjectRef::data()->As##holder()->name()); \
}
// Like above except that the result type is not an XYZRef.
#define BIMODAL_ACCESSOR_C(holder, result, name) \
result holder##Ref::name() const { \
IF_BROKER_DISABLED_ACCESS_HANDLE_C(holder, name); \
return ObjectRef::data()->As##holder()->name(); \
}
// Like above but for BitFields.
#define BIMODAL_ACCESSOR_B(holder, field, name, BitField) \
typename BitField::FieldType holder##Ref::name() const { \
IF_BROKER_DISABLED_ACCESS_HANDLE_C(holder, name); \
return BitField::decode(ObjectRef::data()->As##holder()->field()); \
}
BIMODAL_ACCESSOR(AllocationSite, Object, nested_site)
BIMODAL_ACCESSOR_C(AllocationSite, bool, CanInlineCall)
BIMODAL_ACCESSOR_C(AllocationSite, bool, PointsToLiteral)
BIMODAL_ACCESSOR_C(AllocationSite, ElementsKind, GetElementsKind)
BIMODAL_ACCESSOR_C(AllocationSite, PretenureFlag, GetPretenureMode)
BIMODAL_ACCESSOR_C(BytecodeArray, int, register_count)
BIMODAL_ACCESSOR(HeapObject, Map, map)
BIMODAL_ACCESSOR(JSArray, Object, length)
BIMODAL_ACCESSOR_C(JSFunction, bool, has_prototype)
BIMODAL_ACCESSOR_C(JSFunction, bool, has_initial_map)
BIMODAL_ACCESSOR_C(JSFunction, bool, PrototypeRequiresRuntimeLookup)
BIMODAL_ACCESSOR(JSFunction, JSGlobalProxy, global_proxy)
BIMODAL_ACCESSOR(JSFunction, Map, initial_map)
BIMODAL_ACCESSOR(JSFunction, Object, prototype)
BIMODAL_ACCESSOR(JSFunction, SharedFunctionInfo, shared)
BIMODAL_ACCESSOR_B(Map, bit_field2, elements_kind, Map::ElementsKindBits)
BIMODAL_ACCESSOR_B(Map, bit_field3, is_deprecated, Map::IsDeprecatedBit)
BIMODAL_ACCESSOR_B(Map, bit_field3, is_dictionary_map, Map::IsDictionaryMapBit)
BIMODAL_ACCESSOR_B(Map, bit_field3, NumberOfOwnDescriptors,
Map::NumberOfOwnDescriptorsBits)
BIMODAL_ACCESSOR_B(Map, bit_field, has_prototype_slot, Map::HasPrototypeSlotBit)
BIMODAL_ACCESSOR_B(Map, bit_field, is_constructor, Map::IsConstructorBit)
BIMODAL_ACCESSOR_C(Map, int, instance_size)
BIMODAL_ACCESSOR(Map, Object, prototype)
BIMODAL_ACCESSOR_C(Map, InstanceType, instance_type)
BIMODAL_ACCESSOR(Map, Object, constructor_or_backpointer)
#define DEF_NATIVE_CONTEXT_ACCESSOR(type, name) \
BIMODAL_ACCESSOR(NativeContext, type, name)
BROKER_NATIVE_CONTEXT_FIELDS(DEF_NATIVE_CONTEXT_ACCESSOR)
#undef DEF_NATIVE_CONTEXT_ACCESSOR
BIMODAL_ACCESSOR(PropertyCell, Object, value)
BIMODAL_ACCESSOR_C(PropertyCell, PropertyDetails, property_details)
BIMODAL_ACCESSOR_C(SharedFunctionInfo, int, builtin_id)
BIMODAL_ACCESSOR(SharedFunctionInfo, BytecodeArray, GetBytecodeArray)
#define DEF_SFI_ACCESSOR(type, name) \
BIMODAL_ACCESSOR_C(SharedFunctionInfo, type, name)
BROKER_SFI_FIELDS(DEF_SFI_ACCESSOR)
#undef DEF_SFI_ACCESSOR
BIMODAL_ACCESSOR_C(String, int, length)
bool MapRef::IsInobjectSlackTrackingInProgress() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(Map, IsInobjectSlackTrackingInProgress);
return Map::ConstructionCounterBits::decode(data()->AsMap()->bit_field3()) !=
Map::kNoSlackTracking;
}
bool MapRef::is_stable() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(Map, is_stable);
return !Map::IsUnstableBit::decode(data()->AsMap()->bit_field3());
}
bool MapRef::CanBeDeprecated() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(Map, CanBeDeprecated);
CHECK_GT(NumberOfOwnDescriptors(), 0);
return data()->AsMap()->can_be_deprecated();
}
bool MapRef::CanTransition() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(Map, CanTransition);
return data()->AsMap()->can_transition();
}
int MapRef::GetInObjectPropertiesStartInWords() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(Map, GetInObjectPropertiesStartInWords);
return data()->AsMap()->in_object_properties_start_in_words();
}
int MapRef::GetInObjectProperties() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(Map, GetInObjectProperties);
return data()->AsMap()->in_object_properties();
}
int ScopeInfoRef::ContextLength() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(ScopeInfo, ContextLength);
return data()->AsScopeInfo()->context_length();
}
bool StringRef::IsExternalString() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(String, IsExternalString);
return data()->AsString()->is_external_string();
}
bool StringRef::IsSeqString() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(String, IsSeqString);
return data()->AsString()->is_seq_string();
}
MapRef NativeContextRef::GetFunctionMapFromIndex(int index) const {
DCHECK_GE(index, Context::FIRST_FUNCTION_MAP_INDEX);
DCHECK_LE(index, Context::LAST_FUNCTION_MAP_INDEX);
if (broker()->mode() == JSHeapBroker::kDisabled) {
return get(index).AsMap();
}
return MapRef(data()->AsNativeContext()->function_maps().at(
index - Context::FIRST_FUNCTION_MAP_INDEX));
}
MapRef NativeContextRef::GetInitialJSArrayMap(ElementsKind kind) const {
switch (kind) {
case PACKED_SMI_ELEMENTS:
return js_array_packed_smi_elements_map();
case HOLEY_SMI_ELEMENTS:
return js_array_holey_smi_elements_map();
case PACKED_DOUBLE_ELEMENTS:
return js_array_packed_double_elements_map();
case HOLEY_DOUBLE_ELEMENTS:
return js_array_holey_double_elements_map();
case PACKED_ELEMENTS:
return js_array_packed_elements_map();
case HOLEY_ELEMENTS:
return js_array_holey_elements_map();
default:
UNREACHABLE();
}
}
bool ObjectRef::BooleanValue() {
AllowHandleDereference allow_handle_dereference;
return object<Object>()->BooleanValue(broker()->isolate());
}
double ObjectRef::OddballToNumber() const {
OddballType type = oddball_type();
switch (type) {
case OddballType::kBoolean: {
ObjectRef true_ref(broker(),
broker()->isolate()->factory()->true_value());
return this->equals(true_ref) ? 1 : 0;
break;
}
case OddballType::kUndefined: {
return std::numeric_limits<double>::quiet_NaN();
break;
}
case OddballType::kNull: {
return 0;
break;
}
default: {
UNREACHABLE();
break;
}
}
}
double HeapNumberRef::value() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(HeapNumber, value);
return data()->AsHeapNumber()->value();
}
double MutableHeapNumberRef::value() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(MutableHeapNumber, value);
return data()->AsMutableHeapNumber()->value();
}
CellRef ModuleRef::GetCell(int cell_index) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
return CellRef(broker(), handle(object<Module>()->GetCell(cell_index),
broker()->isolate()));
}
return CellRef(data()->AsModule()->GetCell(cell_index));
}
ObjectRef::ObjectRef(JSHeapBroker* broker, Handle<Object> object) {
switch (broker->mode()) {
case JSHeapBroker::kSerialized:
data_ = FLAG_strict_heap_broker ? broker->GetData(object)
: broker->GetOrCreateData(object);
break;
case JSHeapBroker::kSerializing:
data_ = broker->GetOrCreateData(object);
break;
case JSHeapBroker::kDisabled:
data_ = broker->GetData(object);
if (data_ == nullptr) {
AllowHandleDereference handle_dereference;
data_ =
new (broker->zone()) ObjectData(broker, object, object->IsSmi());
}
break;
}
CHECK_NOT_NULL(data_);
}
base::Optional<JSObjectRef> AllocationSiteRef::boilerplate() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
return JSObjectRef(broker(), handle(object<AllocationSite>()->boilerplate(),
broker()->isolate()));
}
JSObjectData* boilerplate = data()->AsAllocationSite()->boilerplate();
if (boilerplate) {
return JSObjectRef(boilerplate);
} else {
return base::nullopt;
}
}
ElementsKind JSObjectRef::GetElementsKind() const {
return map().elements_kind();
}
FixedArrayBaseRef JSObjectRef::elements() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference allow_handle_dereference;
return FixedArrayBaseRef(
broker(), handle(object<JSObject>()->elements(), broker()->isolate()));
}
return FixedArrayBaseRef(data()->AsJSObject()->elements());
}
int FixedArrayBaseRef::length() const {
IF_BROKER_DISABLED_ACCESS_HANDLE_C(FixedArrayBase, length);
return data()->AsFixedArrayBase()->length();
}
ObjectData* FixedArrayData::Get(int i) const {
CHECK_LT(i, static_cast<int>(contents_.size()));
CHECK_NOT_NULL(contents_[i]);
return contents_[i];
}
Float64 FixedDoubleArrayData::Get(int i) const {
CHECK_LT(i, static_cast<int>(contents_.size()));
return contents_[i];
}
void FeedbackVectorRef::SerializeSlots() {
data()->AsFeedbackVector()->SerializeSlots();
}
ObjectRef JSRegExpRef::data() const {
IF_BROKER_DISABLED_ACCESS_HANDLE(JSRegExp, Object, data);
return ObjectRef(ObjectRef::data()->AsJSRegExp()->data());
}
ObjectRef JSRegExpRef::flags() const {
IF_BROKER_DISABLED_ACCESS_HANDLE(JSRegExp, Object, flags);
return ObjectRef(ObjectRef::data()->AsJSRegExp()->flags());
}
ObjectRef JSRegExpRef::last_index() const {
IF_BROKER_DISABLED_ACCESS_HANDLE(JSRegExp, Object, last_index);
return ObjectRef(ObjectRef::data()->AsJSRegExp()->last_index());
}
ObjectRef JSRegExpRef::raw_properties_or_hash() const {
IF_BROKER_DISABLED_ACCESS_HANDLE(JSRegExp, Object, raw_properties_or_hash);
return ObjectRef(ObjectRef::data()->AsJSRegExp()->raw_properties_or_hash());
}
ObjectRef JSRegExpRef::source() const {
IF_BROKER_DISABLED_ACCESS_HANDLE(JSRegExp, Object, source);
return ObjectRef(ObjectRef::data()->AsJSRegExp()->source());
}
Handle<Object> ObjectRef::object() const { return data()->object(); }
JSHeapBroker* ObjectRef::broker() const { return data()->broker(); }
ObjectData* ObjectRef::data() const { return data_; }
Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker,
const char* function, int line) {
if (FLAG_trace_heap_broker) {
PrintF("[%p] Skipping optimization in %s at line %d due to missing data\n",
broker, function, line);
}
return AdvancedReducer::NoChange();
}
NativeContextData::NativeContextData(JSHeapBroker* broker,
Handle<NativeContext> object,
HeapObjectType type)
: ContextData(broker, object, type), function_maps_(broker->zone()) {}
void NativeContextData::Serialize() {
if (serialized_) return;
serialized_ = true;
Handle<NativeContext> context = Handle<NativeContext>::cast(object());
#define SERIALIZE_MEMBER(type, name) \
DCHECK_NULL(name##_); \
name##_ = broker()->GetOrCreateData(context->name())->As##type(); \
if (name##_->IsJSFunction()) name##_->AsJSFunction()->Serialize();
BROKER_NATIVE_CONTEXT_FIELDS(SERIALIZE_MEMBER)
#undef SERIALIZE_MEMBER
DCHECK(function_maps_.empty());
int const first = Context::FIRST_FUNCTION_MAP_INDEX;
int const last = Context::LAST_FUNCTION_MAP_INDEX;
function_maps_.reserve(last + 1 - first);
for (int i = first; i <= last; ++i) {
function_maps_.push_back(
broker()->GetOrCreateData(context->get(i))->AsMap());
}
}
void JSFunctionRef::Serialize() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsJSFunction()->Serialize();
}
void JSObjectRef::SerializeObjectCreateMap() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsJSObject()->SerializeObjectCreateMap();
}
void MapRef::SerializeDescriptors() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeDescriptors();
}
void ModuleRef::Serialize() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsModule()->Serialize();
}
void ContextRef::Serialize() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsContext()->Serialize();
}
#undef BIMODAL_ACCESSOR
#undef BIMODAL_ACCESSOR_B
#undef BIMODAL_ACCESSOR_C
#undef IF_BROKER_DISABLED_ACCESS_HANDLE
#undef IF_BROKER_DISABLED_ACCESS_HANDLE_C
} // namespace compiler
} // namespace internal
} // namespace v8