blob: c79c793ae69a632da62f46be9ab990539af1a57f [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/compiler/heap-refs.h"
#ifdef ENABLE_SLOW_DCHECKS
#include <algorithm>
#endif
#include "src/api/api-inl.h"
#include "src/ast/modules.h"
#include "src/codegen/code-factory.h"
#include "src/compiler/access-info.h"
#include "src/compiler/bytecode-analysis.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/per-isolate-compiler-cache.h"
#include "src/compiler/vector-slot-pair.h"
#include "src/init/bootstrapper.h"
#include "src/objects/allocation-site-inl.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/cell-inl.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/instance-type-inl.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/template-objects-inl.h"
#include "src/objects/templates.h"
#include "src/utils/boxed-float.h"
#include "src/utils/utils.h"
namespace v8 {
namespace internal {
namespace compiler {
#define TRACE(broker, x) TRACE_BROKER(broker, x)
#define TRACE_MISSING(broker, x) TRACE_BROKER_MISSING(broker, x)
#define FORWARD_DECL(Name) class Name##Data;
HEAP_BROKER_OBJECT_LIST(FORWARD_DECL)
#undef FORWARD_DECL
// There are three kinds of ObjectData values.
//
// kSmi: The underlying V8 object is a Smi and the data is an instance of the
// base class (ObjectData), i.e. it's basically just the handle. Because the
// object is a Smi, it's safe to access the handle in order to extract the
// number value, and AsSmi() does exactly that.
//
// kSerializedHeapObject: The underlying V8 object is a HeapObject and the
// data is an instance of the corresponding (most-specific) subclass, e.g.
// JSFunctionData, which provides serialized information about the object.
//
// kUnserializedHeapObject: The underlying V8 object is a HeapObject and the
// data is an instance of the base class (ObjectData), i.e. it basically
// carries no information other than the handle.
//
enum ObjectDataKind { kSmi, kSerializedHeapObject, kUnserializedHeapObject };
class ObjectData : public ZoneObject {
public:
ObjectData(JSHeapBroker* broker, ObjectData** storage, Handle<Object> object,
ObjectDataKind kind)
: object_(object), kind_(kind) {
// This assignment ensures we don't end up inserting the same object
// in an endless recursion.
*storage = this;
TRACE(broker, "Creating data " << this << " for handle " << object.address()
<< " (" << Brief(*object) << ")");
CHECK_NOT_NULL(broker->isolate()->handle_scope_data()->canonical_scope);
}
#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
Handle<Object> object() const { return object_; }
ObjectDataKind kind() const { return kind_; }
bool is_smi() const { return kind_ == kSmi; }
private:
Handle<Object> const object_;
ObjectDataKind const kind_;
};
class HeapObjectData : public ObjectData {
public:
HeapObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<HeapObject> object);
bool boolean_value() const { return boolean_value_; }
MapData* map() const { return map_; }
static HeapObjectData* Serialize(JSHeapBroker* broker,
Handle<HeapObject> object);
private:
bool const boolean_value_;
MapData* const map_;
};
class PropertyCellData : public HeapObjectData {
public:
PropertyCellData(JSHeapBroker* broker, ObjectData** storage,
Handle<PropertyCell> object);
PropertyDetails property_details() const { return property_details_; }
void Serialize(JSHeapBroker* broker);
ObjectData* value() const { return value_; }
private:
PropertyDetails const property_details_;
bool serialized_ = false;
ObjectData* value_ = nullptr;
};
// TODO(mslekova): Once we have real-world usage data, we might want to
// reimplement this as sorted vector instead, to reduce the memory overhead.
typedef ZoneMap<MapData*, HolderLookupResult> KnownReceiversMap;
class FunctionTemplateInfoData : public HeapObjectData {
public:
FunctionTemplateInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<FunctionTemplateInfo> object);
bool is_signature_undefined() const { return is_signature_undefined_; }
bool accept_any_receiver() const { return accept_any_receiver_; }
bool has_call_code() const { return has_call_code_; }
void SerializeCallCode(JSHeapBroker* broker);
CallHandlerInfoData* call_code() const { return call_code_; }
KnownReceiversMap& known_receivers() { return known_receivers_; }
private:
bool serialized_call_code_ = false;
CallHandlerInfoData* call_code_ = nullptr;
bool is_signature_undefined_ = false;
bool accept_any_receiver_ = false;
bool has_call_code_ = false;
KnownReceiversMap known_receivers_;
};
class CallHandlerInfoData : public HeapObjectData {
public:
CallHandlerInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<CallHandlerInfo> object);
Address callback() const { return callback_; }
void Serialize(JSHeapBroker* broker);
ObjectData* data() const { return data_; }
private:
Address const callback_;
bool serialized_ = false;
ObjectData* data_ = nullptr;
};
FunctionTemplateInfoData::FunctionTemplateInfoData(
JSHeapBroker* broker, ObjectData** storage,
Handle<FunctionTemplateInfo> object)
: HeapObjectData(broker, storage, object),
known_receivers_(broker->zone()) {
auto function_template_info = Handle<FunctionTemplateInfo>::cast(object);
is_signature_undefined_ =
function_template_info->signature().IsUndefined(broker->isolate());
accept_any_receiver_ = function_template_info->accept_any_receiver();
CallOptimization call_optimization(broker->isolate(), object);
has_call_code_ = call_optimization.is_simple_api_call();
}
CallHandlerInfoData::CallHandlerInfoData(JSHeapBroker* broker,
ObjectData** storage,
Handle<CallHandlerInfo> object)
: HeapObjectData(broker, storage, object),
callback_(v8::ToCData<Address>(object->callback())) {}
void JSHeapBroker::IncrementTracingIndentation() { ++trace_indentation_; }
void JSHeapBroker::DecrementTracingIndentation() { --trace_indentation_; }
PropertyCellData::PropertyCellData(JSHeapBroker* broker, ObjectData** storage,
Handle<PropertyCell> object)
: HeapObjectData(broker, storage, object),
property_details_(object->property_details()) {}
void PropertyCellData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "PropertyCellData::Serialize");
auto cell = Handle<PropertyCell>::cast(object());
DCHECK_NULL(value_);
value_ = broker->GetOrCreateData(cell->value());
}
void FunctionTemplateInfoData::SerializeCallCode(JSHeapBroker* broker) {
if (serialized_call_code_) return;
serialized_call_code_ = true;
TraceScope tracer(broker, this,
"FunctionTemplateInfoData::SerializeCallCode");
auto function_template_info = Handle<FunctionTemplateInfo>::cast(object());
DCHECK_NULL(call_code_);
call_code_ = broker->GetOrCreateData(function_template_info->call_code())
->AsCallHandlerInfo();
call_code_->Serialize(broker);
}
void CallHandlerInfoData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "CallHandlerInfoData::Serialize");
auto call_handler_info = Handle<CallHandlerInfo>::cast(object());
DCHECK_NULL(data_);
data_ = broker->GetOrCreateData(call_handler_info->data());
}
class JSObjectField {
public:
bool IsDouble() const { return object_ == nullptr; }
uint64_t AsBitsOfDouble() const {
CHECK(IsDouble());
return number_bits_;
}
double AsDouble() const {
CHECK(IsDouble());
return bit_cast<double>(number_bits_);
}
bool IsObject() const { return object_ != nullptr; }
ObjectData* AsObject() const {
CHECK(IsObject());
return object_;
}
explicit JSObjectField(uint64_t value_bits) : number_bits_(value_bits) {}
explicit JSObjectField(ObjectData* value) : object_(value) {}
private:
ObjectData* object_ = nullptr;
uint64_t number_bits_ = 0;
};
struct FieldIndexHasher {
size_t operator()(FieldIndex field_index) const {
return field_index.index();
}
};
class JSObjectData : public HeapObjectData {
public:
JSObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSObject> object);
// Recursive serialization of all reachable JSObjects.
void SerializeAsBoilerplate(JSHeapBroker* broker);
const JSObjectField& GetInobjectField(int property_index) const;
// Shallow serialization of {elements}.
void SerializeElements(JSHeapBroker* broker);
bool serialized_elements() const { return serialized_elements_; }
FixedArrayBaseData* elements() const;
void SerializeObjectCreateMap(JSHeapBroker* broker);
MapData* object_create_map() const { // Can be nullptr.
CHECK(serialized_object_create_map_);
return object_create_map_;
}
ObjectData* GetOwnConstantElement(JSHeapBroker* broker, uint32_t index,
bool serialize);
ObjectData* GetOwnProperty(JSHeapBroker* broker,
Representation representation,
FieldIndex field_index, bool serialize);
// This method is only used to assert our invariants.
bool cow_or_empty_elements_tenured() const;
private:
void SerializeRecursiveAsBoilerplate(JSHeapBroker* broker, 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;
// Elements (indexed properties) that either
// (1) are known to exist directly on the object as non-writable and
// non-configurable, or (2) are known not to (possibly they don't exist at
// all). In case (2), the second pair component is nullptr.
ZoneVector<std::pair<uint32_t, ObjectData*>> own_constant_elements_;
// Properties that either:
// (1) are known to exist directly on the object, or
// (2) are known not to (possibly they don't exist at all).
// In case (2), the second pair component is nullptr.
// For simplicity, this may in theory overlap with inobject_fields_.
ZoneUnorderedMap<FieldIndex, ObjectData*, FieldIndexHasher> own_properties_;
};
void JSObjectData::SerializeObjectCreateMap(JSHeapBroker* broker) {
if (serialized_object_create_map_) return;
serialized_object_create_map_ = true;
TraceScope tracer(broker, this, "JSObjectData::SerializeObjectCreateMap");
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();
}
}
}
}
namespace {
base::Optional<ObjectRef> GetOwnElementFromHeap(JSHeapBroker* broker,
Handle<Object> receiver,
uint32_t index,
bool constant_only) {
LookupIterator it(broker->isolate(), receiver, index, LookupIterator::OWN);
if (it.state() == LookupIterator::DATA &&
(!constant_only || (it.IsReadOnly() && !it.IsConfigurable()))) {
return ObjectRef(broker, it.GetDataValue());
}
return base::nullopt;
}
ObjectRef GetOwnPropertyFromHeap(JSHeapBroker* broker,
Handle<JSObject> receiver,
Representation representation,
FieldIndex field_index) {
Handle<Object> constant =
JSObject::FastPropertyAt(receiver, representation, field_index);
return ObjectRef(broker, constant);
}
} // namespace
ObjectData* JSObjectData::GetOwnConstantElement(JSHeapBroker* broker,
uint32_t index,
bool serialize) {
for (auto const& p : own_constant_elements_) {
if (p.first == index) return p.second;
}
if (!serialize) {
TRACE_MISSING(broker, "knowledge about index " << index << " on " << this);
return nullptr;
}
base::Optional<ObjectRef> element =
GetOwnElementFromHeap(broker, object(), index, true);
ObjectData* result = element.has_value() ? element->data() : nullptr;
own_constant_elements_.push_back({index, result});
return result;
}
ObjectData* JSObjectData::GetOwnProperty(JSHeapBroker* broker,
Representation representation,
FieldIndex field_index,
bool serialize) {
auto p = own_properties_.find(field_index);
if (p != own_properties_.end()) return p->second;
if (!serialize) {
TRACE_MISSING(broker, "knowledge about property with index "
<< field_index.property_index() << " on "
<< this);
return nullptr;
}
ObjectRef property = GetOwnPropertyFromHeap(
broker, Handle<JSObject>::cast(object()), representation, field_index);
ObjectData* result(property.data());
own_properties_.insert(std::make_pair(field_index, result));
return result;
}
class JSTypedArrayData : public JSObjectData {
public:
JSTypedArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSTypedArray> object);
bool is_on_heap() const { return is_on_heap_; }
size_t length() const { return length_; }
void* external_pointer() const { return external_pointer_; }
void Serialize(JSHeapBroker* broker);
bool serialized() const { return serialized_; }
HeapObjectData* buffer() const { return buffer_; }
private:
bool const is_on_heap_;
size_t const length_;
void* const external_pointer_;
bool serialized_ = false;
HeapObjectData* buffer_ = nullptr;
};
JSTypedArrayData::JSTypedArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSTypedArray> object)
: JSObjectData(broker, storage, object),
is_on_heap_(object->is_on_heap()),
length_(object->length()),
external_pointer_(object->external_pointer()) {}
void JSTypedArrayData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "JSTypedArrayData::Serialize");
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object());
if (!is_on_heap()) {
DCHECK_NULL(buffer_);
buffer_ = broker->GetOrCreateData(typed_array->buffer())->AsHeapObject();
}
}
class JSDataViewData : public JSObjectData {
public:
JSDataViewData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSDataView> object);
size_t byte_length() const { return byte_length_; }
size_t byte_offset() const { return byte_offset_; }
private:
size_t const byte_length_;
size_t const byte_offset_;
};
class JSBoundFunctionData : public JSObjectData {
public:
JSBoundFunctionData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSBoundFunction> object);
void Serialize(JSHeapBroker* broker);
ObjectData* bound_target_function() const { return bound_target_function_; }
ObjectData* bound_this() const { return bound_this_; }
FixedArrayData* bound_arguments() const { return bound_arguments_; }
private:
bool serialized_ = false;
ObjectData* bound_target_function_ = nullptr;
ObjectData* bound_this_ = nullptr;
FixedArrayData* bound_arguments_ = nullptr;
};
class JSFunctionData : public JSObjectData {
public:
JSFunctionData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSFunction> object);
bool has_feedback_vector() const { return has_feedback_vector_; }
bool has_initial_map() const { return has_initial_map_; }
bool has_prototype() const { return has_prototype_; }
bool PrototypeRequiresRuntimeLookup() const {
return PrototypeRequiresRuntimeLookup_;
}
void Serialize(JSHeapBroker* broker);
bool serialized() const { return serialized_; }
ContextData* context() const { return context_; }
NativeContextData* native_context() const { return native_context_; }
MapData* initial_map() const { return initial_map_; }
ObjectData* prototype() const { return prototype_; }
SharedFunctionInfoData* shared() const { return shared_; }
FeedbackVectorData* feedback_vector() const { return feedback_vector_; }
int initial_map_instance_size_with_min_slack() const {
CHECK(serialized_);
return initial_map_instance_size_with_min_slack_;
}
private:
bool has_feedback_vector_;
bool has_initial_map_;
bool has_prototype_;
bool PrototypeRequiresRuntimeLookup_;
bool serialized_ = false;
ContextData* context_ = nullptr;
NativeContextData* native_context_ = nullptr;
MapData* initial_map_ = nullptr;
ObjectData* prototype_ = nullptr;
SharedFunctionInfoData* shared_ = nullptr;
FeedbackVectorData* feedback_vector_ = nullptr;
int initial_map_instance_size_with_min_slack_;
};
class JSRegExpData : public JSObjectData {
public:
JSRegExpData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSRegExp> object)
: JSObjectData(broker, storage, object) {}
void SerializeAsRegExpBoilerplate(JSHeapBroker* broker);
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, ObjectData** storage,
Handle<HeapNumber> object)
: HeapObjectData(broker, storage, object), value_(object->value()) {}
double value() const { return value_; }
private:
double const value_;
};
class MutableHeapNumberData : public HeapObjectData {
public:
MutableHeapNumberData(JSHeapBroker* broker, ObjectData** storage,
Handle<MutableHeapNumber> object)
: HeapObjectData(broker, storage, object), value_(object->value()) {}
double value() const { return value_; }
private:
double const value_;
};
class ContextData : public HeapObjectData {
public:
ContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<Context> object);
// {previous} will return the closest valid context possible to desired
// {depth}, decrementing {depth} for each previous link successfully followed.
// If {serialize} is true, it will serialize contexts along the way.
ContextData* previous(JSHeapBroker* broker, size_t* depth, bool serialize);
// Returns nullptr if the slot index isn't valid or wasn't serialized
// (unless {serialize} is true).
ObjectData* GetSlot(JSHeapBroker* broker, int index, bool serialize);
private:
ZoneMap<int, ObjectData*> slots_;
ContextData* previous_ = nullptr;
};
ContextData::ContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<Context> object)
: HeapObjectData(broker, storage, object), slots_(broker->zone()) {}
ContextData* ContextData::previous(JSHeapBroker* broker, size_t* depth,
bool serialize) {
if (*depth == 0) return this;
if (serialize && previous_ == nullptr) {
TraceScope tracer(broker, this, "ContextData::previous");
Handle<Context> context = Handle<Context>::cast(object());
Object prev = context->unchecked_previous();
if (prev.IsContext()) {
previous_ = broker->GetOrCreateData(prev)->AsContext();
}
}
if (previous_ != nullptr) {
*depth = *depth - 1;
return previous_->previous(broker, depth, serialize);
}
return this;
}
ObjectData* ContextData::GetSlot(JSHeapBroker* broker, int index,
bool serialize) {
CHECK_GE(index, 0);
auto search = slots_.find(index);
if (search != slots_.end()) {
return search->second;
}
if (serialize) {
Handle<Context> context = Handle<Context>::cast(object());
if (index < context->length()) {
TraceScope tracer(broker, this, "ContextData::GetSlot");
TRACE(broker, "Serializing context slot " << index);
ObjectData* odata = broker->GetOrCreateData(context->get(index));
slots_.insert(std::make_pair(index, odata));
return odata;
}
}
return nullptr;
}
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_;
}
ScopeInfoData* scope_info() const {
CHECK(serialized_);
return scope_info_;
}
NativeContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<NativeContext> object);
void Serialize(JSHeapBroker* broker);
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_;
ScopeInfoData* scope_info_ = nullptr;
};
class NameData : public HeapObjectData {
public:
NameData(JSHeapBroker* broker, ObjectData** storage, Handle<Name> object)
: HeapObjectData(broker, storage, object) {}
};
class StringData : public NameData {
public:
StringData(JSHeapBroker* broker, ObjectData** storage, Handle<String> object);
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_; }
StringData* GetCharAsString(JSHeapBroker* broker, uint32_t index,
bool serialize);
private:
int const length_;
uint16_t const first_char_;
base::Optional<double> to_number_;
bool const is_external_string_;
bool const is_seq_string_;
// Known individual characters as strings, corresponding to the semantics of
// element access (s[i]). The first pair component is always less than
// {length_}. The second component is never nullptr.
ZoneVector<std::pair<uint32_t, StringData*>> chars_as_strings_;
static constexpr int kMaxLengthForDoubleConversion = 23;
};
class SymbolData : public NameData {
public:
SymbolData(JSHeapBroker* broker, ObjectData** storage, Handle<Symbol> object)
: NameData(broker, storage, object) {}
};
StringData::StringData(JSHeapBroker* broker, ObjectData** storage,
Handle<String> object)
: NameData(broker, storage, object),
length_(object->length()),
first_char_(length_ > 0 ? object->Get(0) : 0),
is_external_string_(object->IsExternalString()),
is_seq_string_(object->IsSeqString()),
chars_as_strings_(broker->zone()) {
int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY;
if (length_ <= kMaxLengthForDoubleConversion) {
to_number_ = StringToDouble(broker->isolate(), object, flags);
}
}
class InternalizedStringData : public StringData {
public:
InternalizedStringData(JSHeapBroker* broker, ObjectData** storage,
Handle<InternalizedString> object);
uint32_t array_index() const { return array_index_; }
private:
uint32_t array_index_;
};
StringData* StringData::GetCharAsString(JSHeapBroker* broker, uint32_t index,
bool serialize) {
if (index >= static_cast<uint32_t>(length())) return nullptr;
for (auto const& p : chars_as_strings_) {
if (p.first == index) return p.second;
}
if (!serialize) {
TRACE_MISSING(broker, "knowledge about index " << index << " on " << this);
return nullptr;
}
base::Optional<ObjectRef> element =
GetOwnElementFromHeap(broker, object(), index, true);
StringData* result =
element.has_value() ? element->data()->AsString() : nullptr;
chars_as_strings_.push_back({index, result});
return result;
}
InternalizedStringData::InternalizedStringData(
JSHeapBroker* broker, ObjectData** storage,
Handle<InternalizedString> object)
: StringData(broker, storage, object) {}
namespace {
bool IsFastLiteralHelper(Handle<JSObject> boilerplate, int max_depth,
int* max_properties) {
DCHECK_GE(max_depth, 0);
DCHECK_GE(*max_properties, 0);
Isolate* const isolate = boilerplate->GetIsolate();
// Make sure the boilerplate map is not deprecated.
if (!JSObject::TryMigrateInstance(isolate, boilerplate)) return false;
// Check for too deep nesting.
if (max_depth == 0) return false;
// Check the elements.
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, ObjectData** storage,
Handle<AllocationSite> object);
void SerializeBoilerplate(JSHeapBroker* broker);
bool PointsToLiteral() const { return PointsToLiteral_; }
AllocationType GetAllocationType() const { return GetAllocationType_; }
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_;
AllocationType const GetAllocationType_;
ObjectData* nested_site_ = nullptr;
bool IsFastLiteral_ = false;
JSObjectData* boilerplate_ = nullptr;
ElementsKind GetElementsKind_ = NO_ELEMENTS;
bool CanInlineCall_ = false;
bool serialized_boilerplate_ = false;
};
class BigIntData : public HeapObjectData {
public:
BigIntData(JSHeapBroker* broker, ObjectData** storage, Handle<BigInt> object)
: HeapObjectData(broker, storage, object),
as_uint64_(object->AsUint64(nullptr)) {}
uint64_t AsUint64() const { return as_uint64_; }
private:
const uint64_t as_uint64_;
};
// Only used in JSNativeContextSpecialization.
class ScriptContextTableData : public HeapObjectData {
public:
ScriptContextTableData(JSHeapBroker* broker, ObjectData** storage,
Handle<ScriptContextTable> object)
: HeapObjectData(broker, storage, object) {}
};
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, ObjectData** storage, Handle<Map> object);
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_;
}
int constructor_function_index() const { return constructor_function_index_; }
int NextFreePropertyIndex() const { return next_free_property_index_; }
int UnusedPropertyFields() const { return unused_property_fields_; }
bool supports_fast_array_iteration() const {
return supports_fast_array_iteration_;
}
bool supports_fast_array_resize() const {
return supports_fast_array_resize_;
}
bool IsMapOfCurrentGlobalProxy() const {
return is_map_of_current_global_proxy_;
}
// Extra information.
void SerializeElementsKindGeneralizations(JSHeapBroker* broker);
const ZoneVector<MapData*>& elements_kind_generalizations() const {
CHECK(serialized_elements_kind_generalizations_);
return elements_kind_generalizations_;
}
// Serialize a single (or all) own slot(s) of the descriptor array and recurse
// on field owner(s).
void SerializeOwnDescriptor(JSHeapBroker* broker, int descriptor_index);
void SerializeOwnDescriptors(JSHeapBroker* broker);
DescriptorArrayData* instance_descriptors() const {
return instance_descriptors_;
}
void SerializeConstructor(JSHeapBroker* broker);
ObjectData* GetConstructor() const {
CHECK(serialized_constructor_);
return constructor_;
}
void SerializeBackPointer(JSHeapBroker* broker);
HeapObjectData* GetBackPointer() const {
CHECK(serialized_backpointer_);
return backpointer_;
}
void SerializePrototype(JSHeapBroker* broker);
bool serialized_prototype() const { return serialized_prototype_; }
ObjectData* prototype() const {
CHECK(serialized_prototype_);
return prototype_;
}
void SerializeForElementLoad(JSHeapBroker* broker);
void SerializeForElementStore(JSHeapBroker* broker);
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_;
int const constructor_function_index_;
int const next_free_property_index_;
int const unused_property_fields_;
bool const supports_fast_array_iteration_;
bool const supports_fast_array_resize_;
bool const is_map_of_current_global_proxy_;
bool serialized_elements_kind_generalizations_ = false;
ZoneVector<MapData*> elements_kind_generalizations_;
bool serialized_own_descriptors_ = false;
DescriptorArrayData* instance_descriptors_ = nullptr;
bool serialized_constructor_ = false;
ObjectData* constructor_ = nullptr;
bool serialized_backpointer_ = false;
HeapObjectData* backpointer_ = nullptr;
bool serialized_prototype_ = false;
ObjectData* prototype_ = nullptr;
bool serialized_for_element_load_ = false;
bool serialized_for_element_store_ = false;
};
AllocationSiteData::AllocationSiteData(JSHeapBroker* broker,
ObjectData** storage,
Handle<AllocationSite> object)
: HeapObjectData(broker, storage, object),
PointsToLiteral_(object->PointsToLiteral()),
GetAllocationType_(object->GetAllocationType()) {
if (PointsToLiteral_) {
IsFastLiteral_ = IsInlinableFastLiteral(
handle(object->boilerplate(), broker->isolate()));
} else {
GetElementsKind_ = object->GetElementsKind();
CanInlineCall_ = object->CanInlineCall();
}
}
void AllocationSiteData::SerializeBoilerplate(JSHeapBroker* broker) {
if (serialized_boilerplate_) return;
serialized_boilerplate_ = true;
TraceScope tracer(broker, this, "AllocationSiteData::SerializeBoilerplate");
Handle<AllocationSite> site = Handle<AllocationSite>::cast(object());
CHECK(IsFastLiteral_);
DCHECK_NULL(boilerplate_);
boilerplate_ = broker->GetOrCreateData(site->boilerplate())->AsJSObject();
boilerplate_->SerializeAsBoilerplate(broker);
DCHECK_NULL(nested_site_);
nested_site_ = broker->GetOrCreateData(site->nested_site());
if (nested_site_->IsAllocationSite()) {
nested_site_->AsAllocationSite()->SerializeBoilerplate(broker);
}
}
HeapObjectData::HeapObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<HeapObject> object)
: ObjectData(broker, storage, object, kSerializedHeapObject),
boolean_value_(object->BooleanValue(broker->isolate())),
// We have to use a raw cast below instead of AsMap() because of
// recursion. AsMap() would call IsMap(), which accesses the
// instance_type_ member. In the case of constructing the MapData for the
// meta map (whose map is itself), this member has not yet been
// initialized.
map_(static_cast<MapData*>(broker->GetOrCreateData(object->map()))) {
CHECK(broker->SerializingAllowed());
}
namespace {
bool IsReadOnlyLengthDescriptor(Isolate* isolate, Handle<Map> jsarray_map) {
DCHECK(!jsarray_map->is_dictionary_map());
Handle<Name> length_string = isolate->factory()->length_string();
DescriptorArray descriptors = jsarray_map->instance_descriptors();
int number = descriptors.Search(*length_string, *jsarray_map);
DCHECK_NE(DescriptorArray::kNotFound, number);
return descriptors.GetDetails(number).IsReadOnly();
}
bool SupportsFastArrayIteration(Isolate* isolate, Handle<Map> map) {
return map->instance_type() == JS_ARRAY_TYPE &&
IsFastElementsKind(map->elements_kind()) &&
map->prototype().IsJSArray() &&
isolate->IsAnyInitialArrayPrototype(
handle(JSArray::cast(map->prototype()), isolate)) &&
isolate->IsNoElementsProtectorIntact();
}
bool SupportsFastArrayResize(Isolate* isolate, Handle<Map> map) {
return SupportsFastArrayIteration(isolate, map) && map->is_extensible() &&
!map->is_dictionary_map() && !IsReadOnlyLengthDescriptor(isolate, map);
}
} // namespace
MapData::MapData(JSHeapBroker* broker, ObjectData** storage, Handle<Map> object)
: HeapObjectData(broker, storage, object),
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),
constructor_function_index_(object->IsPrimitiveMap()
? object->GetConstructorFunctionIndex()
: Map::kNoConstructorFunctionIndex),
next_free_property_index_(object->NextFreePropertyIndex()),
unused_property_fields_(object->UnusedPropertyFields()),
supports_fast_array_iteration_(
SupportsFastArrayIteration(broker->isolate(), object)),
supports_fast_array_resize_(
SupportsFastArrayResize(broker->isolate(), object)),
is_map_of_current_global_proxy_(
object->IsMapOfGlobalProxy(broker->isolate()->native_context())),
elements_kind_generalizations_(broker->zone()) {}
JSFunctionData::JSFunctionData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSFunction> object)
: JSObjectData(broker, storage, object),
has_feedback_vector_(object->has_feedback_vector()),
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(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "JSFunctionData::Serialize");
Handle<JSFunction> function = Handle<JSFunction>::cast(object());
DCHECK_NULL(context_);
DCHECK_NULL(native_context_);
DCHECK_NULL(initial_map_);
DCHECK_NULL(prototype_);
DCHECK_NULL(shared_);
DCHECK_NULL(feedback_vector_);
context_ = broker->GetOrCreateData(function->context())->AsContext();
native_context_ =
broker->GetOrCreateData(function->native_context())->AsNativeContext();
shared_ = broker->GetOrCreateData(function->shared())->AsSharedFunctionInfo();
feedback_vector_ = has_feedback_vector()
? broker->GetOrCreateData(function->feedback_vector())
->AsFeedbackVector()
: nullptr;
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(broker);
}
initial_map_->SerializeConstructor(broker);
// 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(broker);
}
}
void MapData::SerializeElementsKindGeneralizations(JSHeapBroker* broker) {
if (serialized_elements_kind_generalizations_) return;
serialized_elements_kind_generalizations_ = true;
TraceScope tracer(broker, this,
"MapData::SerializeElementsKindGeneralizations");
DCHECK_EQ(instance_type(), JS_ARRAY_TYPE);
MapRef self(broker, 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(), to_kind);
elements_kind_generalizations_.push_back(
broker->GetOrCreateData(target)->AsMap());
}
}
}
class DescriptorArrayData : public HeapObjectData {
public:
DescriptorArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<DescriptorArray> object)
: HeapObjectData(broker, storage, object), contents_(broker->zone()) {}
ZoneMap<int, PropertyDescriptor>& contents() { return contents_; }
private:
ZoneMap<int, PropertyDescriptor> contents_;
};
class FeedbackCellData : public HeapObjectData {
public:
FeedbackCellData(JSHeapBroker* broker, ObjectData** storage,
Handle<FeedbackCell> object);
HeapObjectData* value() const { return value_; }
private:
HeapObjectData* const value_;
};
FeedbackCellData::FeedbackCellData(JSHeapBroker* broker, ObjectData** storage,
Handle<FeedbackCell> object)
: HeapObjectData(broker, storage, object),
value_(broker->GetOrCreateData(object->value())->AsHeapObject()) {}
class FeedbackVectorData : public HeapObjectData {
public:
const ZoneVector<ObjectData*>& feedback() { return feedback_; }
FeedbackVectorData(JSHeapBroker* broker, ObjectData** storage,
Handle<FeedbackVector> object);
void SerializeSlots(JSHeapBroker* broker);
private:
bool serialized_ = false;
ZoneVector<ObjectData*> feedback_;
};
FeedbackVectorData::FeedbackVectorData(JSHeapBroker* broker,
ObjectData** storage,
Handle<FeedbackVector> object)
: HeapObjectData(broker, storage, object), feedback_(broker->zone()) {}
void FeedbackVectorData::SerializeSlots(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "FeedbackVectorData::SerializeSlots");
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(broker);
} else if (slot_value->IsJSRegExp()) {
slot_value->AsJSRegExp()->SerializeAsRegExpBoilerplate(broker);
}
}
DCHECK_EQ(vector->length(), feedback_.size());
TRACE(broker, "Copied " << feedback_.size() << " slots");
}
class FixedArrayBaseData : public HeapObjectData {
public:
FixedArrayBaseData(JSHeapBroker* broker, ObjectData** storage,
Handle<FixedArrayBase> object)
: HeapObjectData(broker, storage, object), length_(object->length()) {}
int length() const { return length_; }
private:
int const length_;
};
class FixedArrayData : public FixedArrayBaseData {
public:
FixedArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<FixedArray> object);
// Creates all elements of the fixed array.
void SerializeContents(JSHeapBroker* broker);
ObjectData* Get(int i) const;
private:
bool serialized_contents_ = false;
ZoneVector<ObjectData*> contents_;
};
JSDataViewData::JSDataViewData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSDataView> object)
: JSObjectData(broker, storage, object),
byte_length_(object->byte_length()),
byte_offset_(object->byte_offset()) {}
JSBoundFunctionData::JSBoundFunctionData(JSHeapBroker* broker,
ObjectData** storage,
Handle<JSBoundFunction> object)
: JSObjectData(broker, storage, object) {}
void JSBoundFunctionData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "JSBoundFunctionData::Serialize");
Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(object());
DCHECK_NULL(bound_target_function_);
DCHECK_NULL(bound_this_);
DCHECK_NULL(bound_arguments_);
bound_target_function_ =
broker->GetOrCreateData(function->bound_target_function());
bound_this_ = broker->GetOrCreateData(function->bound_this());
bound_arguments_ =
broker->GetOrCreateData(function->bound_arguments())->AsFixedArray();
bound_arguments_->SerializeContents(broker);
}
JSObjectData::JSObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSObject> object)
: HeapObjectData(broker, storage, object),
inobject_fields_(broker->zone()),
own_constant_elements_(broker->zone()),
own_properties_(broker->zone()) {}
FixedArrayData::FixedArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<FixedArray> object)
: FixedArrayBaseData(broker, storage, object), contents_(broker->zone()) {}
void FixedArrayData::SerializeContents(JSHeapBroker* broker) {
if (serialized_contents_) return;
serialized_contents_ = true;
TraceScope tracer(broker, this, "FixedArrayData::SerializeContents");
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));
}
TRACE(broker, "Copied " << contents_.size() << " elements");
}
class FixedDoubleArrayData : public FixedArrayBaseData {
public:
FixedDoubleArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<FixedDoubleArray> object);
// Serializes all elements of the fixed array.
void SerializeContents(JSHeapBroker* broker);
Float64 Get(int i) const;
private:
bool serialized_contents_ = false;
ZoneVector<Float64> contents_;
};
FixedDoubleArrayData::FixedDoubleArrayData(JSHeapBroker* broker,
ObjectData** storage,
Handle<FixedDoubleArray> object)
: FixedArrayBaseData(broker, storage, object), contents_(broker->zone()) {}
void FixedDoubleArrayData::SerializeContents(JSHeapBroker* broker) {
if (serialized_contents_) return;
serialized_contents_ = true;
TraceScope tracer(broker, this, "FixedDoubleArrayData::SerializeContents");
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)));
}
TRACE(broker, "Copied " << contents_.size() << " elements");
}
class BytecodeArrayData : public FixedArrayBaseData {
public:
int register_count() const { return register_count_; }
int parameter_count() const { return parameter_count_; }
interpreter::Register incoming_new_target_or_generator_register() const {
return incoming_new_target_or_generator_register_;
}
uint8_t get(int index) const {
DCHECK(is_serialized_for_compilation_);
return bytecodes_[index];
}
Address GetFirstBytecodeAddress() const {
return reinterpret_cast<Address>(bytecodes_.data());
}
Handle<Object> GetConstantAtIndex(int index, Isolate* isolate) const {
return constant_pool_[index]->object();
}
bool IsConstantAtIndexSmi(int index) const {
return constant_pool_[index]->is_smi();
}
Smi GetConstantAtIndexAsSmi(int index) const {
return *(Handle<Smi>::cast(constant_pool_[index]->object()));
}
bool IsSerializedForCompilation() const {
return is_serialized_for_compilation_;
}
void SerializeForCompilation(JSHeapBroker* broker) {
if (is_serialized_for_compilation_) return;
Handle<BytecodeArray> bytecode_array =
Handle<BytecodeArray>::cast(object());
DCHECK(bytecodes_.empty());
bytecodes_.reserve(bytecode_array->length());
for (int i = 0; i < bytecode_array->length(); i++) {
bytecodes_.push_back(bytecode_array->get(i));
}
DCHECK(constant_pool_.empty());
Handle<FixedArray> constant_pool(bytecode_array->constant_pool(),
broker->isolate());
constant_pool_.reserve(constant_pool->length());
for (int i = 0; i < constant_pool->length(); i++) {
constant_pool_.push_back(broker->GetOrCreateData(constant_pool->get(i)));
}
Handle<ByteArray> source_position_table(
bytecode_array->SourcePositionTableIfCollected(), broker->isolate());
source_positions_.reserve(source_position_table->length());
for (int i = 0; i < source_position_table->length(); i++) {
source_positions_.push_back(source_position_table->get(i));
}
Handle<ByteArray> handlers(bytecode_array->handler_table(),
broker->isolate());
handler_table_.reserve(handlers->length());
for (int i = 0; i < handlers->length(); i++) {
handler_table_.push_back(handlers->get(i));
}
is_serialized_for_compilation_ = true;
}
const byte* source_positions_address() const {
return source_positions_.data();
}
size_t source_positions_size() const { return source_positions_.size(); }
Address handler_table_address() const {
CHECK(is_serialized_for_compilation_);
return reinterpret_cast<Address>(handler_table_.data());
}
int handler_table_size() const {
CHECK(is_serialized_for_compilation_);
return static_cast<int>(handler_table_.size());
}
BytecodeArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<BytecodeArray> object)
: FixedArrayBaseData(broker, storage, object),
register_count_(object->register_count()),
parameter_count_(object->parameter_count()),
incoming_new_target_or_generator_register_(
object->incoming_new_target_or_generator_register()),
bytecodes_(broker->zone()),
source_positions_(broker->zone()),
handler_table_(broker->zone()),
constant_pool_(broker->zone()) {}
private:
int const register_count_;
int const parameter_count_;
interpreter::Register const incoming_new_target_or_generator_register_;
bool is_serialized_for_compilation_ = false;
ZoneVector<uint8_t> bytecodes_;
ZoneVector<uint8_t> source_positions_;
ZoneVector<uint8_t> handler_table_;
ZoneVector<ObjectData*> constant_pool_;
};
class JSArrayData : public JSObjectData {
public:
JSArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSArray> object);
void Serialize(JSHeapBroker* broker);
ObjectData* length() const { return length_; }
ObjectData* GetOwnElement(JSHeapBroker* broker, uint32_t index,
bool serialize);
private:
bool serialized_ = false;
ObjectData* length_ = nullptr;
// Elements (indexed properties) that either
// (1) are known to exist directly on the object, or
// (2) are known not to (possibly they don't exist at all).
// In case (2), the second pair component is nullptr.
ZoneVector<std::pair<uint32_t, ObjectData*>> own_elements_;
};
JSArrayData::JSArrayData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSArray> object)
: JSObjectData(broker, storage, object), own_elements_(broker->zone()) {}
void JSArrayData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "JSArrayData::Serialize");
Handle<JSArray> jsarray = Handle<JSArray>::cast(object());
DCHECK_NULL(length_);
length_ = broker->GetOrCreateData(jsarray->length());
}
ObjectData* JSArrayData::GetOwnElement(JSHeapBroker* broker, uint32_t index,
bool serialize) {
for (auto const& p : own_elements_) {
if (p.first == index) return p.second;
}
if (!serialize) {
TRACE_MISSING(broker, "knowledge about index " << index << " on " << this);
return nullptr;
}
base::Optional<ObjectRef> element =
GetOwnElementFromHeap(broker, object(), index, false);
ObjectData* result = element.has_value() ? element->data() : nullptr;
own_elements_.push_back({index, result});
return result;
}
class ScopeInfoData : public HeapObjectData {
public:
ScopeInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<ScopeInfo> object);
int context_length() const { return context_length_; }
private:
int const context_length_;
};
ScopeInfoData::ScopeInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<ScopeInfo> object)
: HeapObjectData(broker, storage, object),
context_length_(object->ContextLength()) {}
class SharedFunctionInfoData : public HeapObjectData {
public:
SharedFunctionInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<SharedFunctionInfo> object);
int builtin_id() const { return builtin_id_; }
BytecodeArrayData* GetBytecodeArray() const { return GetBytecodeArray_; }
void SetSerializedForCompilation(JSHeapBroker* broker,
FeedbackVectorRef feedback);
bool IsSerializedForCompilation(FeedbackVectorRef feedback) const;
void SerializeFunctionTemplateInfo(JSHeapBroker* broker);
FunctionTemplateInfoData* function_template_info() const {
return function_template_info_;
}
JSArrayData* GetTemplateObject(FeedbackSlot slot) const {
auto lookup_it = template_objects_.find(slot.ToInt());
if (lookup_it != template_objects_.cend()) {
return lookup_it->second;
}
return nullptr;
}
void SetTemplateObject(FeedbackSlot slot, JSArrayData* object) {
CHECK(
template_objects_.insert(std::make_pair(slot.ToInt(), object)).second);
}
#define DECL_ACCESSOR(type, name) \
type name() const { return name##_; }
BROKER_SFI_FIELDS(DECL_ACCESSOR)
#undef DECL_ACCESSOR
private:
int const builtin_id_;
BytecodeArrayData* const GetBytecodeArray_;
ZoneUnorderedSet<Handle<FeedbackVector>, Handle<FeedbackVector>::hash,
Handle<FeedbackVector>::equal_to>
serialized_for_compilation_;
#define DECL_MEMBER(type, name) type const name##_;
BROKER_SFI_FIELDS(DECL_MEMBER)
#undef DECL_MEMBER
FunctionTemplateInfoData* function_template_info_;
ZoneMap<int, JSArrayData*> template_objects_;
};
SharedFunctionInfoData::SharedFunctionInfoData(
JSHeapBroker* broker, ObjectData** storage,
Handle<SharedFunctionInfo> object)
: HeapObjectData(broker, storage, object),
builtin_id_(object->HasBuiltinId() ? object->builtin_id()
: Builtins::kNoBuiltinId),
GetBytecodeArray_(
object->HasBytecodeArray()
? broker->GetOrCreateData(object->GetBytecodeArray())
->AsBytecodeArray()
: nullptr),
serialized_for_compilation_(broker->zone())
#define INIT_MEMBER(type, name) , name##_(object->name())
BROKER_SFI_FIELDS(INIT_MEMBER)
#undef INIT_MEMBER
,
function_template_info_(nullptr),
template_objects_(broker->zone()) {
DCHECK_EQ(HasBuiltinId_, builtin_id_ != Builtins::kNoBuiltinId);
DCHECK_EQ(HasBytecodeArray_, GetBytecodeArray_ != nullptr);
}
void SharedFunctionInfoData::SetSerializedForCompilation(
JSHeapBroker* broker, FeedbackVectorRef feedback) {
CHECK(serialized_for_compilation_.insert(feedback.object()).second);
TRACE(broker, "Set function " << this << " with " << feedback
<< " as serialized for compilation");
}
void SharedFunctionInfoData::SerializeFunctionTemplateInfo(
JSHeapBroker* broker) {
if (function_template_info_) return;
function_template_info_ =
broker
->GetOrCreateData(handle(
Handle<SharedFunctionInfo>::cast(object())->function_data(),
broker->isolate()))
->AsFunctionTemplateInfo();
}
bool SharedFunctionInfoData::IsSerializedForCompilation(
FeedbackVectorRef feedback) const {
return serialized_for_compilation_.find(feedback.object()) !=
serialized_for_compilation_.end();
}
class SourceTextModuleData : public HeapObjectData {
public:
SourceTextModuleData(JSHeapBroker* broker, ObjectData** storage,
Handle<SourceTextModule> object);
void Serialize(JSHeapBroker* broker);
CellData* GetCell(int cell_index) const;
private:
bool serialized_ = false;
ZoneVector<CellData*> imports_;
ZoneVector<CellData*> exports_;
};
SourceTextModuleData::SourceTextModuleData(JSHeapBroker* broker,
ObjectData** storage,
Handle<SourceTextModule> object)
: HeapObjectData(broker, storage, object),
imports_(broker->zone()),
exports_(broker->zone()) {}
CellData* SourceTextModuleData::GetCell(int cell_index) const {
CHECK(serialized_);
CellData* cell;
switch (SourceTextModuleDescriptor::GetCellIndexKind(cell_index)) {
case SourceTextModuleDescriptor::kImport:
cell = imports_.at(SourceTextModule::ImportIndex(cell_index));
break;
case SourceTextModuleDescriptor::kExport:
cell = exports_.at(SourceTextModule::ExportIndex(cell_index));
break;
case SourceTextModuleDescriptor::kInvalid:
UNREACHABLE();
}
CHECK_NOT_NULL(cell);
return cell;
}
void SourceTextModuleData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "SourceTextModuleData::Serialize");
Handle<SourceTextModule> module = Handle<SourceTextModule>::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());
}
TRACE(broker, "Copied " << imports_.size() << " imports");
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());
}
TRACE(broker, "Copied " << exports_.size() << " exports");
}
class CellData : public HeapObjectData {
public:
CellData(JSHeapBroker* broker, ObjectData** storage, Handle<Cell> object);
void Serialize(JSHeapBroker* broker);
ObjectData* value() { return value_; }
private:
bool serialized_ = false;
ObjectData* value_ = nullptr;
};
CellData::CellData(JSHeapBroker* broker, ObjectData** storage,
Handle<Cell> object)
: HeapObjectData(broker, storage, object) {}
void CellData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "CellData::Serialize");
auto cell = Handle<Cell>::cast(object());
DCHECK_NULL(value_);
value_ = broker->GetOrCreateData(cell->value());
}
class JSGlobalProxyData : public JSObjectData {
public:
JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalProxy> object);
PropertyCellData* GetPropertyCell(JSHeapBroker* broker, NameData* name,
bool serialize);
private:
// Properties that either
// (1) are known to exist as property cells on the global object, or
// (2) are known not to (possibly they don't exist at all).
// In case (2), the second pair component is nullptr.
ZoneVector<std::pair<NameData*, PropertyCellData*>> properties_;
};
JSGlobalProxyData::JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalProxy> object)
: JSObjectData(broker, storage, object), properties_(broker->zone()) {}
namespace {
base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker,
Handle<Name> name) {
LookupIterator it(broker->isolate(),
handle(broker->native_context().object()->global_object(),
broker->isolate()),
name, LookupIterator::OWN);
it.TryLookupCachedProperty();
if (it.state() == LookupIterator::DATA &&
it.GetHolder<JSObject>()->IsJSGlobalObject()) {
return PropertyCellRef(broker, it.GetPropertyCell());
}
return base::nullopt;
}
} // namespace
PropertyCellData* JSGlobalProxyData::GetPropertyCell(JSHeapBroker* broker,
NameData* name,
bool serialize) {
CHECK_NOT_NULL(name);
for (auto const& p : properties_) {
if (p.first == name) return p.second;
}
if (!serialize) {
TRACE_MISSING(broker, "knowledge about global property " << name);
return nullptr;
}
PropertyCellData* result = nullptr;
base::Optional<PropertyCellRef> cell =
GetPropertyCellFromHeap(broker, Handle<Name>::cast(name->object()));
if (cell.has_value()) {
cell->Serialize();
result = cell->data()->AsPropertyCell();
}
properties_.push_back({name, result});
return result;
}
class CodeData : public HeapObjectData {
public:
CodeData(JSHeapBroker* broker, ObjectData** storage, Handle<Code> object)
: HeapObjectData(broker, storage, object) {}
};
#define DEFINE_IS_AND_AS(Name) \
bool ObjectData::Is##Name() const { \
if (kind() == kUnserializedHeapObject) { \
AllowHandleDereference allow_handle_dereference; \
return object()->Is##Name(); \
} \
if (is_smi()) return false; \
InstanceType instance_type = \
static_cast<const HeapObjectData*>(this)->map()->instance_type(); \
return InstanceTypeChecker::Is##Name(instance_type); \
} \
Name##Data* ObjectData::As##Name() { \
CHECK_EQ(kind(), kSerializedHeapObject); \
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(JSHeapBroker* broker) {
SerializeRecursiveAsBoilerplate(broker, kMaxFastLiteralDepth);
}
void JSObjectData::SerializeElements(JSHeapBroker* broker) {
if (serialized_elements_) return;
serialized_elements_ = true;
TraceScope tracer(broker, this, "JSObjectData::SerializeElements");
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::SerializeConstructor(JSHeapBroker* broker) {
if (serialized_constructor_) return;
serialized_constructor_ = true;
TraceScope tracer(broker, this, "MapData::SerializeConstructor");
Handle<Map> map = Handle<Map>::cast(object());
DCHECK_NULL(constructor_);
constructor_ = broker->GetOrCreateData(map->GetConstructor());
}
void MapData::SerializeBackPointer(JSHeapBroker* broker) {
if (serialized_backpointer_) return;
serialized_backpointer_ = true;
TraceScope tracer(broker, this, "MapData::SerializeBackPointer");
Handle<Map> map = Handle<Map>::cast(object());
DCHECK_NULL(backpointer_);
backpointer_ = broker->GetOrCreateData(map->GetBackPointer())->AsHeapObject();
}
void MapData::SerializePrototype(JSHeapBroker* broker) {
if (serialized_prototype_) return;
serialized_prototype_ = true;
TraceScope tracer(broker, this, "MapData::SerializePrototype");
Handle<Map> map = Handle<Map>::cast(object());
DCHECK_NULL(prototype_);
prototype_ = broker->GetOrCreateData(map->prototype());
}
void MapData::SerializeOwnDescriptors(JSHeapBroker* broker) {
if (serialized_own_descriptors_) return;
serialized_own_descriptors_ = true;
TraceScope tracer(broker, this, "MapData::SerializeOwnDescriptors");
Handle<Map> map = Handle<Map>::cast(object());
int const number_of_own = map->NumberOfOwnDescriptors();
for (int i = 0; i < number_of_own; ++i) {
SerializeOwnDescriptor(broker, i);
}
}
void MapData::SerializeOwnDescriptor(JSHeapBroker* broker,
int descriptor_index) {
TraceScope tracer(broker, this, "MapData::SerializeOwnDescriptor");
Handle<Map> map = Handle<Map>::cast(object());
if (instance_descriptors_ == nullptr) {
instance_descriptors_ = broker->GetOrCreateData(map->instance_descriptors())
->AsDescriptorArray();
}
ZoneMap<int, PropertyDescriptor>& contents =
instance_descriptors_->contents();
CHECK_LT(descriptor_index, map->NumberOfOwnDescriptors());
if (contents.find(descriptor_index) != contents.end()) return;
Isolate* const isolate = broker->isolate();
auto descriptors =
Handle<DescriptorArray>::cast(instance_descriptors_->object());
CHECK_EQ(*descriptors, map->instance_descriptors());
PropertyDescriptor d;
d.key =
broker->GetOrCreateData(descriptors->GetKey(descriptor_index))->AsName();
d.details = descriptors->GetDetails(descriptor_index);
if (d.details.location() == kField) {
d.field_index = FieldIndex::ForDescriptor(*map, descriptor_index);
d.field_owner =
broker->GetOrCreateData(map->FindFieldOwner(isolate, descriptor_index))
->AsMap();
d.field_type =
broker->GetOrCreateData(descriptors->GetFieldType(descriptor_index));
d.is_unboxed_double_field = map->IsUnboxedDoubleField(d.field_index);
}
contents[descriptor_index] = d;
if (d.details.location() == kField) {
// Recurse on the owner map.
d.field_owner->SerializeOwnDescriptor(broker, descriptor_index);
}
TRACE(broker, "Copied descriptor " << descriptor_index << " into "
<< instance_descriptors_ << " ("
<< contents.size() << " total)");
}
void JSObjectData::SerializeRecursiveAsBoilerplate(JSHeapBroker* broker,
int depth) {
if (serialized_as_boilerplate_) return;
serialized_as_boilerplate_ = true;
TraceScope tracer(broker, this,
"JSObjectData::SerializeRecursiveAsBoilerplate");
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 (ObjectInYoungGeneration(*elements_object)) {
elements_object = 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(broker);
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()->SerializeRecursiveAsBoilerplate(broker,
depth - 1);
}
}
} else {
CHECK(boilerplate->HasDoubleElements());
CHECK_LE(elements_object->Size(), kMaxRegularHeapObjectSize);
elements_->AsFixedDoubleArray()->SerializeContents(broker);
}
// 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)) {
uint64_t value_bits =
boilerplate->RawFastDoublePropertyAsBitsAt(field_index);
inobject_fields_.push_back(JSObjectField{value_bits});
} else {
Handle<Object> value(boilerplate->RawFastPropertyAt(field_index),
isolate);
// In case of unboxed double fields we use a sentinel NaN value to mark
// uninitialized fields. A boilerplate value with such a field may migrate
// from its unboxed double to a tagged representation. In the process the
// raw double is converted to a heap number. The sentinel value carries no
// special meaning when it occurs in a heap number, so we would like to
// recover the uninitialized value.
// We check for the sentinel here, specifically, since migrations might
// have been triggered as part of boilerplate serialization.
if (value->IsHeapNumber() &&
HeapNumber::cast(*value).value_as_bits() == kHoleNanInt64) {
value = isolate->factory()->uninitialized_value();
}
ObjectData* value_data = broker->GetOrCreateData(value);
if (value->IsJSObject()) {
value_data->AsJSObject()->SerializeRecursiveAsBoilerplate(broker,
depth - 1);
}
inobject_fields_.push_back(JSObjectField{value_data});
}
}
TRACE(broker, "Copied " << inobject_fields_.size() << " in-object fields");
map()->SerializeOwnDescriptors(broker);
if (IsJSArray()) AsJSArray()->Serialize(broker);
}
void JSRegExpData::SerializeAsRegExpBoilerplate(JSHeapBroker* broker) {
if (serialized_as_reg_exp_boilerplate_) return;
serialized_as_reg_exp_boilerplate_ = true;
TraceScope tracer(broker, this, "JSRegExpData::SerializeAsRegExpBoilerplate");
Handle<JSRegExp> boilerplate = Handle<JSRegExp>::cast(object());
SerializeElements(broker);
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());
}
bool ObjectRef::equals(const ObjectRef& other) const {
return data_ == other.data_;
}
Isolate* ObjectRef::isolate() const { return broker()->isolate(); }
ContextRef ContextRef::previous(size_t* depth, bool serialize) const {
DCHECK_NOT_NULL(depth);
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
Context current = *object();
while (*depth != 0 && current.unchecked_previous().IsContext()) {
current = Context::cast(current.unchecked_previous());
(*depth)--;
}
return ContextRef(broker(), handle(current, broker()->isolate()));
}
ContextData* current = this->data()->AsContext();
return ContextRef(broker(), current->previous(broker(), depth, serialize));
}
base::Optional<ObjectRef> ContextRef::get(int index, bool serialize) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
Handle<Object> value(object()->get(index), broker()->isolate());
return ObjectRef(broker(), value);
}
ObjectData* optional_slot =
data()->AsContext()->GetSlot(broker(), index, serialize);
if (optional_slot != nullptr) {
return ObjectRef(broker(), optional_slot);
}
return base::nullopt;
}
JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone,
bool tracing_enabled)
: isolate_(isolate),
broker_zone_(broker_zone),
current_zone_(broker_zone),
refs_(new (zone())
RefsMap(kMinimalRefsBucketCount, AddressMatcher(), zone())),
array_and_object_prototypes_(zone()),
tracing_enabled_(tracing_enabled),
feedback_(zone()),
bytecode_analyses_(zone()),
ais_for_loading_then_(zone()),
ais_for_loading_exec_(zone()) {
// Note that this initialization of the refs_ pointer with the minimal
// initial capacity is redundant in the normal use case (concurrent
// compilation enabled, standard objects to be serialized), as the map
// is going to be replaced immediatelly with a larger capacity one.
// It doesn't seem to affect the performance in a noticeable way though.
TRACE(this, "Constructing heap broker");
}
std::ostream& JSHeapBroker::Trace() {
return trace_out_ << "[" << this << "] "
<< std::string(trace_indentation_ * 2, ' ');
}
void JSHeapBroker::StartSerializing() {
CHECK_EQ(mode_, kDisabled);
TRACE(this, "Starting serialization");
mode_ = kSerializing;
refs_->Clear();
}
void JSHeapBroker::StopSerializing() {
CHECK_EQ(mode_, kSerializing);
TRACE(this, "Stopping serialization");
mode_ = kSerialized;
}
void JSHeapBroker::Retire() {
CHECK_EQ(mode_, kSerialized);
TRACE(this, "Retiring");
mode_ = kRetired;
}
bool JSHeapBroker::SerializingAllowed() const { return mode() == kSerializing; }
void JSHeapBroker::SetNativeContextRef() {
native_context_ = NativeContextRef(this, isolate()->native_context());
}
bool IsShareable(Handle<Object> object, Isolate* isolate) {
Builtins* const b = isolate->builtins();
int index;
RootIndex root_index;
return (object->IsHeapObject() &&
b->IsBuiltinHandle(Handle<HeapObject>::cast(object), &index)) ||
isolate->roots_table().IsRootHandle(object, &root_index);
}
void JSHeapBroker::SerializeShareableObjects() {
PerIsolateCompilerCache::Setup(isolate());
compiler_cache_ = isolate()->compiler_cache();
if (compiler_cache_->HasSnapshot()) {
RefsMap* snapshot = compiler_cache_->GetSnapshot();
refs_ = new (zone()) RefsMap(snapshot, zone());
return;
}
TraceScope tracer(
this, "JSHeapBroker::SerializeShareableObjects (building snapshot)");
refs_ =
new (zone()) RefsMap(kInitialRefsBucketCount, AddressMatcher(), zone());
current_zone_ = compiler_cache_->zone();
Builtins* const b = isolate()->builtins();
{
Builtins::Name builtins[] = {
Builtins::kAllocateInYoungGeneration,
Builtins::kAllocateRegularInYoungGeneration,
Builtins::kAllocateInOldGeneration,
Builtins::kAllocateRegularInOldGeneration,
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));
}
}
for (RefsMap::Entry* p = refs_->Start(); p != nullptr; p = refs_->Next(p)) {
CHECK(IsShareable(p->value->object(), isolate()));
}
// TODO(mslekova):
// Serialize root objects (from factory).
compiler_cache()->SetSnapshot(refs_);
current_zone_ = broker_zone_;
}
void JSHeapBroker::CollectArrayAndObjectPrototypes() {
DisallowHeapAllocation no_gc;
CHECK_EQ(mode(), kSerializing);
CHECK(array_and_object_prototypes_.empty());
Object maybe_context = isolate()->heap()->native_contexts_list();
while (!maybe_context.IsUndefined(isolate())) {
Context context = Context::cast(maybe_context);
Object array_prot = context.get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX);
Object object_prot = context.get(Context::INITIAL_OBJECT_PROTOTYPE_INDEX);
array_and_object_prototypes_.emplace(JSObject::cast(array_prot), isolate());
array_and_object_prototypes_.emplace(JSObject::cast(object_prot),
isolate());
maybe_context = context.next_context_link();
}
CHECK(!array_and_object_prototypes_.empty());
}
bool JSHeapBroker::IsArrayOrObjectPrototype(const JSObjectRef& object) const {
if (mode() == kDisabled) {
return isolate()->IsInAnyContext(*object.object(),
Context::INITIAL_ARRAY_PROTOTYPE_INDEX) ||
isolate()->IsInAnyContext(*object.object(),
Context::INITIAL_OBJECT_PROTOTYPE_INDEX);
}
CHECK(!array_and_object_prototypes_.empty());
return array_and_object_prototypes_.find(object.object()) !=
array_and_object_prototypes_.end();
}
void JSHeapBroker::SerializeStandardObjects() {
if (mode() == kDisabled) return;
CHECK_EQ(mode(), kSerializing);
SerializeShareableObjects();
TraceScope tracer(this, "JSHeapBroker::SerializeStandardObjects");
CollectArrayAndObjectPrototypes();
SetNativeContextRef();
native_context().Serialize();
Factory* const f = isolate()->factory();
// Maps, strings, oddballs
GetOrCreateData(f->arguments_marker_map());
GetOrCreateData(f->bigint_string());
GetOrCreateData(f->block_context_map());
GetOrCreateData(f->boolean_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->NaN_string());
GetOrCreateData(f->null_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->optimized_out_map());
GetOrCreateData(f->property_array_map());
GetOrCreateData(f->sloppy_arguments_elements_map());
GetOrCreateData(f->stale_register());
GetOrCreateData(f->stale_register_map());
GetOrCreateData(f->string_string());
GetOrCreateData(f->symbol_string());
GetOrCreateData(f->termination_exception_map());
GetOrCreateData(f->the_hole_map());
GetOrCreateData(f->the_hole_value());
GetOrCreateData(f->true_string());
GetOrCreateData(f->true_value());
GetOrCreateData(f->undefined_map());
GetOrCreateData(f->undefined_string());
GetOrCreateData(f->undefined_value());
GetOrCreateData(f->uninitialized_map());
GetOrCreateData(f->with_context_map());
GetOrCreateData(f->zero_string());
// Protector cells
GetOrCreateData(f->array_buffer_detaching_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->array_constructor_protector())->AsCell()->Serialize(this);
GetOrCreateData(f->array_iterator_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->array_species_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->no_elements_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->promise_hook_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->promise_species_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->promise_then_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->string_length_protector())->AsCell()->Serialize(this);
// CEntry stub
GetOrCreateData(
CodeFactory::CEntry(isolate(), 1, kDontSaveFPRegs, kArgvOnStack, true));
TRACE(this, "Finished serializing standard objects");
}
ObjectData* JSHeapBroker::GetData(Handle<Object> object) const {
RefsMap::Entry* entry = refs_->Lookup(object.address());
return entry ? entry->value : nullptr;
}
// clang-format off
ObjectData* JSHeapBroker::GetOrCreateData(Handle<Object> object) {
CHECK(SerializingAllowed());
RefsMap::Entry* entry = refs_->LookupOrInsert(object.address(), zone());
ObjectData** data_storage = &(entry->value);
if (*data_storage == nullptr) {
// TODO(neis): Remove these Allow* once we serialize everything upfront.
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
if (object->IsSmi()) {
new (zone()) ObjectData(this, data_storage, object, kSmi);
#define CREATE_DATA_IF_MATCH(name) \
} else if (object->Is##name()) { \
new (zone()) name##Data(this, data_storage, Handle<name>::cast(object));
HEAP_BROKER_OBJECT_LIST(CREATE_DATA_IF_MATCH)
#undef CREATE_DATA_IF_MATCH
} else {
UNREACHABLE();
}
}
CHECK_NOT_NULL(*data_storage);
return (*data_storage);
}
// clang-format on
ObjectData* JSHeapBroker::GetOrCreateData(Object object) {
return GetOrCreateData(handle(object, isolate()));
}
#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(broker(), 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 Handle<Smi>::cast(object())->value();
}
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())
.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(broker(), map_data)
: base::Optional<MapRef>();
}
#define DEF_TESTER(Type, ...) \
bool MapRef::Is##Type##Map() const { \
return InstanceTypeChecker::Is##Type(instance_type()); \
}
INSTANCE_TYPE_CHECKERS(DEF_TESTER)
#undef DEF_TESTER
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(), kind));
}
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(broker(), data);
if (map.elements_kind() == kind) return map;
}
return base::Optional<MapRef>();
}
void MapRef::SerializeForElementLoad() {
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeForElementLoad(broker());
}
void MapRef::SerializeForElementStore() {
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeForElementStore(broker());
}
namespace {
// This helper function has two modes. If {prototype_maps} is nullptr, the
// prototype chain is serialized as necessary to determine the result.
// Otherwise, the heap is untouched and the encountered prototypes are pushed
// onto {prototype_maps}.
bool HasOnlyStablePrototypesWithFastElementsHelper(
JSHeapBroker* broker, MapRef const& map,
ZoneVector<MapRef>* prototype_maps) {
for (MapRef prototype_map = map;;) {
if (prototype_maps == nullptr) prototype_map.SerializePrototype();
prototype_map = prototype_map