blob: 01413f9a7dbb025fc4d6c17afb398f2225e05f1c [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/common/globals.h"
#include "src/compiler/heap-refs.h"
#ifdef ENABLE_SLOW_DCHECKS
#include <algorithm>
#endif
#include "include/v8-fast-api-calls.h"
#include "src/api/api-inl.h"
#include "src/ast/modules.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/optimized-compilation-info.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/execution/protectors-inl.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/literal-objects-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/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_SERIALIZED_OBJECT_LIST(FORWARD_DECL)
// TODO(solanes, v8:10866): Remove once FLAG_turbo_direct_heap_access is
// removed.
HEAP_BROKER_NEVER_SERIALIZED_OBJECT_LIST(FORWARD_DECL)
#undef FORWARD_DECL
// There are five 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.
//
// kNeverSerializedHeapObject: The underlying V8 object is a (potentially
// mutable) HeapObject and the data is an instance of ObjectData. Its handle
// must be persistent so that the GC can update it at a safepoint. Via this
// handle, the object can be accessed concurrently to the main thread. To be
// used the flag --turbo-direct-heap-access must be on.
//
// kUnserializedReadOnlyHeapObject: The underlying V8 object is a read-only
// HeapObject and the data is an instance of ObjectData. For
// ReadOnlyHeapObjects, it is OK to access heap even from off-thread, so
// these objects need not be serialized.
enum ObjectDataKind {
kSmi,
kSerializedHeapObject,
kUnserializedHeapObject,
kNeverSerializedHeapObject,
kUnserializedReadOnlyHeapObject
};
class AllowHandleAllocationIfNeeded {
public:
explicit AllowHandleAllocationIfNeeded(ObjectDataKind kind,
JSHeapBroker::BrokerMode mode,
bool direct_heap_access = false) {
DCHECK_IMPLIES(mode == JSHeapBroker::BrokerMode::kSerialized,
kind == kUnserializedReadOnlyHeapObject ||
kind == kNeverSerializedHeapObject ||
(direct_heap_access && kind == kSerializedHeapObject));
if (kind == kUnserializedHeapObject) maybe_allow_handle_.emplace();
}
private:
base::Optional<AllowHandleAllocation> maybe_allow_handle_;
};
class AllowHandleDereferenceIfNeeded {
public:
explicit AllowHandleDereferenceIfNeeded(ObjectDataKind kind,
JSHeapBroker::BrokerMode mode,
bool direct_heap_access = false)
: AllowHandleDereferenceIfNeeded(kind) {
DCHECK_IMPLIES(mode == JSHeapBroker::BrokerMode::kSerialized,
kind == kUnserializedReadOnlyHeapObject ||
kind == kNeverSerializedHeapObject ||
(direct_heap_access && kind == kSerializedHeapObject));
}
explicit AllowHandleDereferenceIfNeeded(ObjectDataKind kind) {
if (kind == kUnserializedHeapObject ||
kind == kUnserializedReadOnlyHeapObject) {
maybe_allow_handle_.emplace();
}
}
private:
base::Optional<AllowHandleDereference> maybe_allow_handle_;
};
class AllowHeapAllocationIfNeeded {
public:
explicit AllowHeapAllocationIfNeeded(ObjectDataKind kind,
JSHeapBroker::BrokerMode mode) {
DCHECK_IMPLIES(mode == JSHeapBroker::BrokerMode::kSerialized,
kind == kUnserializedReadOnlyHeapObject);
if (kind == kUnserializedHeapObject) maybe_allow_handle_.emplace();
}
private:
base::Optional<AllowHeapAllocation> maybe_allow_handle_;
};
namespace {
bool IsReadOnlyHeapObject(Object object) {
DisallowHeapAllocation no_gc;
return (object.IsCode() && Code::cast(object).is_builtin()) ||
(object.IsHeapObject() &&
ReadOnlyHeap::Contains(HeapObject::cast(object)));
}
} // namespace
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) << ")");
// It is safe to access read only heap objects and builtins from a
// background thread. When we read fileds of these objects, we may create
// ObjectData on the background thread even without a canonical handle
// scope. This is safe too since we don't create handles but just get
// handles from read only root table or builtins table which is what
// canonical scope uses as well. For all other objects we should have
// created ObjectData in canonical handle scope on the main thread.
CHECK_IMPLIES(
broker->mode() == JSHeapBroker::kDisabled ||
broker->mode() == JSHeapBroker::kSerializing,
broker->isolate()->handle_scope_data()->canonical_scope != nullptr);
CHECK_IMPLIES(broker->mode() == JSHeapBroker::kSerialized,
(kind == kUnserializedReadOnlyHeapObject &&
IsReadOnlyHeapObject(*object)) ||
kind == kNeverSerializedHeapObject);
}
#define DECLARE_IS(Name) bool Is##Name() const;
HEAP_BROKER_SERIALIZED_OBJECT_LIST(DECLARE_IS)
HEAP_BROKER_NEVER_SERIALIZED_OBJECT_LIST(DECLARE_IS)
#undef DECLARE_IS
#define DECLARE_AS(Name) Name##Data* As##Name();
HEAP_BROKER_SERIALIZED_OBJECT_LIST(DECLARE_AS)
// TODO(solanes, v8:10866): Remove once FLAG_turbo_direct_heap_access is
// removed.
HEAP_BROKER_NEVER_SERIALIZED_OBJECT_LIST(DECLARE_AS)
#undef DECLARE_AS
Handle<Object> object() const { return object_; }
ObjectDataKind kind() const { return kind_; }
bool is_smi() const { return kind_ == kSmi; }
bool should_access_heap() const {
return kind_ == kUnserializedHeapObject ||
kind_ == kNeverSerializedHeapObject ||
kind_ == kUnserializedReadOnlyHeapObject;
}
#ifdef DEBUG
enum class Usage{kUnused, kOnlyIdentityUsed, kDataUsed};
mutable Usage used_status = Usage::kUnused;
#endif // DEBUG
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_; }
ObjectData* map() const { return map_; }
InstanceType GetMapInstanceType() const;
static HeapObjectData* Serialize(JSHeapBroker* broker,
Handle<HeapObject> object);
private:
bool const boolean_value_;
ObjectData* 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_;
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<ObjectData*, 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);
ObjectData* call_code() const { return call_code_; }
Address c_function() const { return c_function_; }
const CFunctionInfo* c_signature() const { return c_signature_; }
KnownReceiversMap& known_receivers() { return known_receivers_; }
private:
bool is_signature_undefined_ = false;
bool accept_any_receiver_ = false;
bool has_call_code_ = false;
ObjectData* call_code_ = nullptr;
const Address c_function_;
const CFunctionInfo* const c_signature_;
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_;
ObjectData* data_ = nullptr;
};
FunctionTemplateInfoData::FunctionTemplateInfoData(
JSHeapBroker* broker, ObjectData** storage,
Handle<FunctionTemplateInfo> object)
: HeapObjectData(broker, storage, object),
c_function_(v8::ToCData<Address>(object->GetCFunction())),
c_signature_(v8::ToCData<CFunctionInfo*>(object->GetCSignature())),
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())) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
// These definitions are here in order to please the linker, which in debug mode
// sometimes requires static constants to be defined in .cc files.
const uint32_t JSHeapBroker::kMinimalRefsBucketCount;
const uint32_t JSHeapBroker::kInitialRefsBucketCount;
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 (value_ != nullptr) return;
TraceScope tracer(broker, this, "PropertyCellData::Serialize");
auto cell = Handle<PropertyCell>::cast(object());
value_ = broker->GetOrCreateData(cell->value());
}
void FunctionTemplateInfoData::SerializeCallCode(JSHeapBroker* broker) {
if (call_code_ != nullptr) return;
TraceScope tracer(broker, this,
"FunctionTemplateInfoData::SerializeCallCode");
auto function_template_info = Handle<FunctionTemplateInfo>::cast(object());
call_code_ =
broker->GetOrCreateData(function_template_info->call_code(kAcquireLoad));
if (call_code_->should_access_heap()) {
// TODO(mvstanton): When ObjectRef is in the never serialized list, this
// code can be removed.
broker->GetOrCreateData(
Handle<CallHandlerInfo>::cast(call_code_->object())->data());
} else {
call_code_->AsCallHandlerInfo()->Serialize(broker);
}
}
void CallHandlerInfoData::Serialize(JSHeapBroker* broker) {
if (data_ != nullptr) return;
TraceScope tracer(broker, this, "CallHandlerInfoData::Serialize");
auto call_handler_info = Handle<CallHandlerInfo>::cast(object());
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;
};
class JSReceiverData : public HeapObjectData {
public:
JSReceiverData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSReceiver> object)
: HeapObjectData(broker, storage, object) {}
};
class JSObjectData : public JSReceiverData {
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_; }
ObjectData* elements() const;
void SerializeObjectCreateMap(JSHeapBroker* broker);
ObjectData* object_create_map(
JSHeapBroker* broker) const { // Can be nullptr.
if (!serialized_object_create_map_) {
DCHECK_NULL(object_create_map_);
TRACE_MISSING(broker, "object_create_map on " << this);
}
return object_create_map_;
}
ObjectData* GetOwnConstantElement(
JSHeapBroker* broker, uint32_t index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
ObjectData* GetOwnDataProperty(
JSHeapBroker* broker, Representation representation,
FieldIndex field_index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
// This method is only used to assert our invariants.
bool cow_or_empty_elements_tenured() const;
private:
void SerializeRecursiveAsBoilerplate(JSHeapBroker* broker, int max_depths);
ObjectData* 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;
ObjectData* 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_.
// The keys of the map are the property_index() values of the
// respective property FieldIndex'es.
ZoneUnorderedMap<int, ObjectData*> 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());
}
}
}
}
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,
broker->CanonicalPersistentHandle(it.GetDataValue()));
}
return base::nullopt;
}
ObjectRef GetOwnDataPropertyFromHeap(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,
SerializationPolicy policy) {
for (auto const& p : own_constant_elements_) {
if (p.first == index) return p.second;
}
if (policy == SerializationPolicy::kAssumeSerialized) {
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::GetOwnDataProperty(JSHeapBroker* broker,
Representation representation,
FieldIndex field_index,
SerializationPolicy policy) {
auto p = own_properties_.find(field_index.property_index());
if (p != own_properties_.end()) return p->second;
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_MISSING(broker, "knowledge about property with index "
<< field_index.property_index() << " on "
<< this);
return nullptr;
}
ObjectRef property = GetOwnDataPropertyFromHeap(
broker, Handle<JSObject>::cast(object()), representation, field_index);
ObjectData* result(property.data());
own_properties_.insert(std::make_pair(field_index.property_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* data_ptr() const { return data_ptr_; }
void Serialize(JSHeapBroker* broker);
bool serialized() const { return serialized_; }
ObjectData* buffer() const { return buffer_; }
private:
bool const is_on_heap_;
size_t const length_;
void* const data_ptr_;
bool serialized_ = false;
ObjectData* 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()),
data_ptr_(object->DataPtr()) {}
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());
}
}
class ArrayBoilerplateDescriptionData : public HeapObjectData {
public:
ArrayBoilerplateDescriptionData(JSHeapBroker* broker, ObjectData** storage,
Handle<ArrayBoilerplateDescription> object)
: HeapObjectData(broker, storage, object),
constants_elements_length_(object->constant_elements().length()) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
int constants_elements_length() const { return constants_elements_length_; }
private:
int const constants_elements_length_;
};
class ObjectBoilerplateDescriptionData : public HeapObjectData {
public:
ObjectBoilerplateDescriptionData(JSHeapBroker* broker, ObjectData** storage,
Handle<ObjectBoilerplateDescription> object)
: HeapObjectData(broker, storage, object), size_(object->size()) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
int size() const { return size_; }
private:
int const size_;
};
class JSDataViewData : public JSObjectData {
public:
JSDataViewData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSDataView> object);
size_t byte_length() const { return byte_length_; }
private:
size_t const byte_length_;
};
class JSBoundFunctionData : public JSObjectData {
public:
JSBoundFunctionData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSBoundFunction> object);
bool Serialize(JSHeapBroker* broker);
bool serialized() const { return serialized_; }
ObjectData* bound_target_function() const { return bound_target_function_; }
ObjectData* bound_this() const { return bound_this_; }
ObjectData* bound_arguments() const { return bound_arguments_; }
private:
bool serialized_ = false;
ObjectData* bound_target_function_ = nullptr;
ObjectData* bound_this_ = nullptr;
ObjectData* 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 HasAttachedOptimizedCode() const { return has_attached_optimized_code_; }
bool PrototypeRequiresRuntimeLookup() const {
return PrototypeRequiresRuntimeLookup_;
}
void Serialize(JSHeapBroker* broker);
bool serialized() const { return serialized_; }
ObjectData* context() const { return context_; }
ObjectData* native_context() const { return native_context_; }
ObjectData* initial_map() const { return initial_map_; }
ObjectData* prototype() const { return prototype_; }
ObjectData* shared() const { return shared_; }
ObjectData* raw_feedback_cell() const { return feedback_cell_; }
ObjectData* feedback_vector() const { return feedback_vector_; }
ObjectData* code() const { return code_; }
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 has_attached_optimized_code_;
bool PrototypeRequiresRuntimeLookup_;
bool serialized_ = false;
ObjectData* context_ = nullptr;
ObjectData* native_context_ = nullptr;
ObjectData* initial_map_ = nullptr;
ObjectData* prototype_ = nullptr;
ObjectData* shared_ = nullptr;
ObjectData* feedback_vector_ = nullptr;
ObjectData* feedback_cell_ = nullptr;
ObjectData* code_ = 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 ContextData : public HeapObjectData {
public:
ContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<Context> object);
ObjectData* previous(
JSHeapBroker* broker,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
// Returns nullptr if the slot index isn't valid or wasn't serialized,
// unless {policy} is {kSerializeIfNeeded}.
ObjectData* GetSlot(
JSHeapBroker* broker, int index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
private:
ZoneMap<int, ObjectData*> slots_;
ObjectData* previous_ = nullptr;
};
ContextData::ContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<Context> object)
: HeapObjectData(broker, storage, object), slots_(broker->zone()) {}
ObjectData* ContextData::previous(JSHeapBroker* broker,
SerializationPolicy policy) {
if (policy == SerializationPolicy::kSerializeIfNeeded &&
previous_ == nullptr) {
TraceScope tracer(broker, this, "ContextData::previous");
Handle<Context> context = Handle<Context>::cast(object());
previous_ = broker->GetOrCreateData(context->unchecked_previous());
}
return previous_;
}
ObjectData* ContextData::GetSlot(JSHeapBroker* broker, int index,
SerializationPolicy policy) {
CHECK_GE(index, 0);
auto search = slots_.find(index);
if (search != slots_.end()) {
return search->second;
}
if (policy == SerializationPolicy::kSerializeIfNeeded) {
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) \
ObjectData* name() const { return name##_; }
BROKER_NATIVE_CONTEXT_FIELDS(DECL_ACCESSOR)
#undef DECL_ACCESSOR
const ZoneVector<ObjectData*>& function_maps() const {
CHECK(serialized_);
return function_maps_;
}
ObjectData* 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) ObjectData* name##_ = nullptr;
BROKER_NATIVE_CONTEXT_FIELDS(DECL_MEMBER)
#undef DECL_MEMBER
ZoneVector<ObjectData*> function_maps_;
ObjectData* 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_; }
ObjectData* GetCharAsString(
JSHeapBroker* broker, uint32_t index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
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, ObjectData*>> chars_as_strings_;
};
class SymbolData : public NameData {
public:
SymbolData(JSHeapBroker* broker, ObjectData** storage, Handle<Symbol> object)
: NameData(broker, storage, object) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
};
namespace {
// String to double helper without heap allocation.
base::Optional<double> StringToDouble(Handle<String> object) {
const int kMaxLengthForDoubleConversion = 23;
String string = *object;
int length = string.length();
if (length <= kMaxLengthForDoubleConversion) {
const int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY;
uc16 buffer[kMaxLengthForDoubleConversion];
String::WriteToFlat(*object, buffer, 0, length);
Vector<const uc16> v(buffer, length);
return StringToDouble(v, flags);
}
return base::nullopt;
}
} // namespace
StringData::StringData(JSHeapBroker* broker, ObjectData** storage,
Handle<String> object)
: NameData(broker, storage, object),
length_(object->length()),
first_char_(length_ > 0 ? object->Get(0) : 0),
to_number_(StringToDouble(object)),
is_external_string_(object->IsExternalString()),
is_seq_string_(object->IsSeqString()),
chars_as_strings_(broker->zone()) {}
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_;
};
ObjectData* StringData::GetCharAsString(JSHeapBroker* broker, uint32_t index,
SerializationPolicy policy) {
if (index >= static_cast<uint32_t>(length())) return nullptr;
for (auto const& p : chars_as_strings_) {
if (p.first == index) return p.second;
}
if (policy == SerializationPolicy::kAssumeSerialized) {
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;
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(kRelaxedLoad), isolate);
for (InternalIndex i : boilerplate->map().IterateOwnDescriptors()) {
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 AccessorInfoData : public HeapObjectData {
public:
AccessorInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<AccessorInfo> object);
};
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_; }
ObjectData* 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;
ObjectData* 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 {
ObjectData* key = nullptr;
ObjectData* value = nullptr;
PropertyDetails details = PropertyDetails::Empty();
FieldIndex field_index;
ObjectData* 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 is_abandoned_prototype_map() const {
return is_abandoned_prototype_map_;
}
// Extra information.
void SerializeElementsKindGeneralizations(JSHeapBroker* broker);
const ZoneVector<ObjectData*>& 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,
InternalIndex descriptor_index);
void SerializeOwnDescriptors(JSHeapBroker* broker);
ObjectData* GetStrongValue(InternalIndex descriptor_index) const;
// TODO(neis): This code needs to be changed to allow for ObjectData* instance
// descriptors. However, this is likely to require a non-trivial refactoring
// of how maps are serialized because actual instance descriptors don't
// contain information about owner maps.
DescriptorArrayData* instance_descriptors() const {
return instance_descriptors_;
}
void SerializeRootMap(JSHeapBroker* broker);
ObjectData* FindRootMap() const;
void SerializeConstructor(JSHeapBroker* broker);
ObjectData* GetConstructor() const {
CHECK(serialized_constructor_);
return constructor_;
}
void SerializeBackPointer(JSHeapBroker* broker);
ObjectData* 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_abandoned_prototype_map_;
bool serialized_elements_kind_generalizations_ = false;
ZoneVector<ObjectData*> elements_kind_generalizations_;
bool serialized_own_descriptors_ = false;
DescriptorArrayData* instance_descriptors_ = nullptr;
bool serialized_constructor_ = false;
ObjectData* constructor_ = nullptr;
bool serialized_backpointer_ = false;
ObjectData* backpointer_ = nullptr;
bool serialized_prototype_ = false;
ObjectData* prototype_ = nullptr;
bool serialized_root_map_ = false;
ObjectData* root_map_ = nullptr;
bool serialized_for_element_load_ = false;
bool serialized_for_element_store_ = false;
};
AccessorInfoData::AccessorInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<AccessorInfo> object)
: HeapObjectData(broker, storage, object) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
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());
if (!boilerplate_->should_access_heap()) {
boilerplate_->AsJSObject()->SerializeAsBoilerplate(broker);
}
DCHECK_NULL(nested_site_);
nested_site_ = broker->GetOrCreateData(site->nested_site());
if (nested_site_->IsAllocationSite() && !nested_site_->should_access_heap()) {
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_(broker->GetOrCreateData(object->map())) {
CHECK_EQ(broker->mode(), JSHeapBroker::kSerializing);
}
InstanceType HeapObjectData::GetMapInstanceType() const {
ObjectData* map_data = map();
if (map_data->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(kind());
return Handle<Map>::cast(map_data->object())->instance_type();
}
return map_data->AsMap()->instance_type();
}
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(kRelaxedLoad);
// TODO(jkummerow): We could skip the search and hardcode number == 0.
InternalIndex number = descriptors.Search(*length_string, *jsarray_map);
DCHECK(number.is_found());
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)) &&
Protectors::IsNoElementsIntact(isolate);
}
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_abandoned_prototype_map_(object->is_abandoned_prototype_map()),
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()),
has_attached_optimized_code_(object->HasAttachedOptimizedCode()),
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_cell_);
DCHECK_NULL(feedback_vector_);
DCHECK_NULL(code_);
context_ = broker->GetOrCreateData(function->context());
native_context_ = broker->GetOrCreateData(function->native_context());
shared_ = broker->GetOrCreateData(function->shared());
feedback_cell_ = broker->GetOrCreateData(function->raw_feedback_cell());
feedback_vector_ = has_feedback_vector()
? broker->GetOrCreateData(function->feedback_vector())
: nullptr;
code_ = broker->GetOrCreateData(function->code());
initial_map_ = has_initial_map()
? broker->GetOrCreateData(function->initial_map())
: 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_ != nullptr && !initial_map_->should_access_heap()) {
if (initial_map_->AsMap()->instance_type() == JS_ARRAY_TYPE) {
initial_map_->AsMap()->SerializeElementsKindGeneralizations(broker);
}
initial_map_->AsMap()->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_->AsMap()->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));
}
}
}
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);
ObjectData* value() const { return value_; }
private:
ObjectData* const value_;
};
FeedbackCellData::FeedbackCellData(JSHeapBroker* broker, ObjectData** storage,
Handle<FeedbackCell> object)
: HeapObjectData(broker, storage, object),
value_(broker->GetOrCreateData(object->value())) {}
class FeedbackVectorData : public HeapObjectData {
public:
FeedbackVectorData(JSHeapBroker* broker, ObjectData** storage,
Handle<FeedbackVector> object);
double invocation_count() const { return invocation_count_; }
ObjectData* shared_function_info() {
CHECK(serialized_);
return shared_function_info_;
}
void Serialize(JSHeapBroker* broker);
bool serialized() const { return serialized_; }
ObjectData* GetClosureFeedbackCell(JSHeapBroker* broker, int index) const;
private:
double const invocation_count_;
bool serialized_ = false;
ObjectData* shared_function_info_;
ZoneVector<ObjectData*> closure_feedback_cell_array_;
};
FeedbackVectorData::FeedbackVectorData(JSHeapBroker* broker,
ObjectData** storage,
Handle<FeedbackVector> object)
: HeapObjectData(broker, storage, object),
invocation_count_(object->invocation_count()),
closure_feedback_cell_array_(broker->zone()) {}
ObjectData* FeedbackVectorData::GetClosureFeedbackCell(JSHeapBroker* broker,
int index) const {
CHECK_GE(index, 0);
size_t cell_array_size = closure_feedback_cell_array_.size();
if (!serialized_) {
DCHECK_EQ(cell_array_size, 0);
TRACE_BROKER_MISSING(broker,
" closure feedback cell array for vector " << this);
return nullptr;
}
CHECK_LT(index, cell_array_size);
return closure_feedback_cell_array_[index];
}
void FeedbackVectorData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "FeedbackVectorData::Serialize");
Handle<FeedbackVector> vector = Handle<FeedbackVector>::cast(object());
Handle<SharedFunctionInfo> sfi(vector->shared_function_info(),
broker->isolate());
shared_function_info_ = broker->GetOrCreateData(sfi);
DCHECK(closure_feedback_cell_array_.empty());
int length = vector->closure_feedback_cell_array().length();
closure_feedback_cell_array_.reserve(length);
for (int i = 0; i < length; ++i) {
Handle<FeedbackCell> cell = vector->GetClosureFeedbackCell(i);
ObjectData* cell_data = broker->GetOrCreateData(cell);
closure_feedback_cell_array_.push_back(cell_data);
}
TRACE(broker, "Copied " << length << " feedback cells");
}
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()) {}
JSBoundFunctionData::JSBoundFunctionData(JSHeapBroker* broker,
ObjectData** storage,
Handle<JSBoundFunction> object)
: JSObjectData(broker, storage, object) {}
bool JSBoundFunctionData::Serialize(JSHeapBroker* broker) {
if (serialized_) return true;
if (broker->StackHasOverflowed()) return false;
TraceScope tracer(broker, this, "JSBoundFunctionData::Serialize");
Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(object());
// We don't immediately set {serialized_} in order to correctly handle the
// case where a recursive call to this method reaches the stack limit.
DCHECK_NULL(bound_target_function_);
bound_target_function_ =
broker->GetOrCreateData(function->bound_target_function());
bool serialized_nested = true;
if (!bound_target_function_->should_access_heap()) {
if (bound_target_function_->IsJSBoundFunction()) {
serialized_nested =
bound_target_function_->AsJSBoundFunction()->Serialize(broker);
} else if (bound_target_function_->IsJSFunction()) {
bound_target_function_->AsJSFunction()->Serialize(broker);
}
}
if (!serialized_nested) {
// We couldn't serialize all nested bound functions due to stack
// overflow. Give up.
DCHECK(!serialized_);
bound_target_function_ = nullptr; // Reset to sync with serialized_.
return false;
}
serialized_ = true;
DCHECK_NULL(bound_arguments_);
bound_arguments_ = broker->GetOrCreateData(function->bound_arguments());
if (!bound_arguments_->should_access_heap()) {
bound_arguments_->AsFixedArray()->SerializeContents(broker);
}
DCHECK_NULL(bound_this_);
bound_this_ = broker->GetOrCreateData(function->bound_this());
return true;
}
JSObjectData::JSObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSObject> object)
: JSReceiverData(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_;
}
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()));
}
void SerializeForCompilation(JSHeapBroker* broker) {
if (is_serialized_for_compilation_) return;
// Convinience cast: object() is already a canonical persistent handle.
Handle<BytecodeArray> bytecodes = Handle<BytecodeArray>::cast(object());
DCHECK(constant_pool_.empty());
Handle<FixedArray> constant_pool(bytecodes->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)));
}
is_serialized_for_compilation_ = true;
}
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()),
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<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,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
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,
SerializationPolicy policy) {
for (auto const& p : own_elements_) {
if (p.first == index) return p.second;
}
if (policy == SerializationPolicy::kAssumeSerialized) {
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 ContextLength() const { return context_length_; }
bool HasContextExtensionSlot() const { return has_context_extension_slot_; }
bool HasOuterScopeInfo() const { return has_outer_scope_info_; }
ObjectData* OuterScopeInfo() const { return outer_scope_info_; }
void SerializeScopeInfoChain(JSHeapBroker* broker);
private:
int const context_length_;
bool const has_context_extension_slot_;
bool const has_outer_scope_info_;
// Only serialized via SerializeScopeInfoChain.
ObjectData* outer_scope_info_;
};
ScopeInfoData::ScopeInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<ScopeInfo> object)
: HeapObjectData(broker, storage, object),
context_length_(object->ContextLength()),
has_context_extension_slot_(object->HasContextExtensionSlot()),
has_outer_scope_info_(object->HasOuterScopeInfo()),
outer_scope_info_(nullptr) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
void ScopeInfoData::SerializeScopeInfoChain(JSHeapBroker* broker) {
if (outer_scope_info_) return;
if (!has_outer_scope_info_) return;
outer_scope_info_ = broker->GetOrCreateData(
Handle<ScopeInfo>::cast(object())->OuterScopeInfo());
if (!outer_scope_info_->should_access_heap()) {
outer_scope_info_->AsScopeInfo()->SerializeScopeInfoChain(broker);
}
}
class SharedFunctionInfoData : public HeapObjectData {
public:
SharedFunctionInfoData(JSHeapBroker* broker, ObjectData** storage,
Handle<SharedFunctionInfo> object);
int builtin_id() const { return builtin_id_; }
int context_header_size() const { return context_header_size_; }
ObjectData* GetBytecodeArray() const { return GetBytecodeArray_; }
SharedFunctionInfo::Inlineability GetInlineability() const {
return inlineability_;
}
void SerializeFunctionTemplateInfo(JSHeapBroker* broker);
ObjectData* scope_info() const { return scope_info_; }
void SerializeScopeInfoChain(JSHeapBroker* broker);
ObjectData* function_template_info() const { return function_template_info_; }
ObjectData* 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, ObjectData* 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_;
int const context_header_size_;
ObjectData* const GetBytecodeArray_;
#define DECL_MEMBER(type, name) type const name##_;
BROKER_SFI_FIELDS(DECL_MEMBER)
#undef DECL_MEMBER
SharedFunctionInfo::Inlineability const inlineability_;
ObjectData* function_template_info_;
ZoneMap<int, ObjectData*> template_objects_;
ObjectData* scope_info_;
};
SharedFunctionInfoData::SharedFunctionInfoData(
JSHeapBroker* broker, ObjectData** storage,
Handle<SharedFunctionInfo> object)
: HeapObjectData(broker, storage, object),
builtin_id_(object->HasBuiltinId() ? object->builtin_id()
: Builtins::kNoBuiltinId),
context_header_size_(object->scope_info().ContextHeaderLength()),
GetBytecodeArray_(
object->HasBytecodeArray()
? broker->GetOrCreateData(object->GetBytecodeArray())
: nullptr)
#define INIT_MEMBER(type, name) , name##_(object->name())
BROKER_SFI_FIELDS(INIT_MEMBER)
#undef INIT_MEMBER
,
inlineability_(object->GetInlineability()),
function_template_info_(nullptr),
template_objects_(broker->zone()),
scope_info_(nullptr) {
DCHECK_EQ(HasBuiltinId_, builtin_id_ != Builtins::kNoBuiltinId);
DCHECK_EQ(HasBytecodeArray_, GetBytecodeArray_ != nullptr);
}
void SharedFunctionInfoData::SerializeFunctionTemplateInfo(
JSHeapBroker* broker) {
if (function_template_info_) return;
function_template_info_ = broker->GetOrCreateData(
Handle<SharedFunctionInfo>::cast(object())->function_data(kAcquireLoad));
}
void SharedFunctionInfoData::SerializeScopeInfoChain(JSHeapBroker* broker) {
if (scope_info_) return;
scope_info_ = broker->GetOrCreateData(
Handle<SharedFunctionInfo>::cast(object())->scope_info());
if (!scope_info_->should_access_heap()) {
scope_info_->AsScopeInfo()->SerializeScopeInfoChain(broker);
}
}
class SourceTextModuleData : public HeapObjectData {
public:
SourceTextModuleData(JSHeapBroker* broker, ObjectData** storage,
Handle<SourceTextModule> object);
void Serialize(JSHeapBroker* broker);
ObjectData* GetCell(JSHeapBroker* broker, int cell_index) const;
ObjectData* GetImportMeta(JSHeapBroker* broker) const;
private:
bool serialized_ = false;
ZoneVector<ObjectData*> imports_;
ZoneVector<ObjectData*> exports_;
ObjectData* import_meta_;
};
SourceTextModuleData::SourceTextModuleData(JSHeapBroker* broker,
ObjectData** storage,
Handle<SourceTextModule> object)
: HeapObjectData(broker, storage, object),
imports_(broker->zone()),
exports_(broker->zone()),
import_meta_(nullptr) {}
ObjectData* SourceTextModuleData::GetCell(JSHeapBroker* broker,
int cell_index) const {
if (!serialized_) {
DCHECK(imports_.empty());
TRACE_BROKER_MISSING(broker,
"module cell " << cell_index << " on " << this);
return nullptr;
}
ObjectData* 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;
}
ObjectData* SourceTextModuleData::GetImportMeta(JSHeapBroker* broker) const {
CHECK(serialized_);
return import_meta_;
}
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)));
}
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)));
}
TRACE(broker, "Copied " << exports_.size() << " exports");
DCHECK_NULL(import_meta_);
import_meta_ = broker->GetOrCreateData(module->import_meta());
TRACE(broker, "Copied import_meta");
}
class CellData : public HeapObjectData {
public:
CellData(JSHeapBroker* broker, ObjectData** storage, Handle<Cell> object)
: HeapObjectData(broker, storage, object) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
};
class JSGlobalObjectData : public JSObjectData {
public:
JSGlobalObjectData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalObject> object);
bool IsDetached() const { return is_detached_; }
ObjectData* GetPropertyCell(
JSHeapBroker* broker, ObjectData* name,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
private:
bool const is_detached_;
// 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<ObjectData*, ObjectData*>> properties_;
};
JSGlobalObjectData::JSGlobalObjectData(JSHeapBroker* broker,
ObjectData** storage,
Handle<JSGlobalObject> object)
: JSObjectData(broker, storage, object),
is_detached_(object->IsDetached()),
properties_(broker->zone()) {}
class JSGlobalProxyData : public JSObjectData {
public:
JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalProxy> object);
};
JSGlobalProxyData::JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalProxy> object)
: JSObjectData(broker, storage, object) {}
namespace {
base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker,
Handle<Name> name) {
LookupIterator it(
broker->isolate(),
handle(broker->target_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
ObjectData* JSGlobalObjectData::GetPropertyCell(JSHeapBroker* broker,
ObjectData* name,
SerializationPolicy policy) {
CHECK_NOT_NULL(name);
for (auto const& p : properties_) {
if (p.first == name) return p.second;
}
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_MISSING(broker, "knowledge about global property " << name);
return nullptr;
}
ObjectData* result = nullptr;
base::Optional<PropertyCellRef> cell =
GetPropertyCellFromHeap(broker, Handle<Name>::cast(name->object()));
if (cell.has_value()) {
result = cell->data();
if (!result->should_access_heap()) {
result->AsPropertyCell()->Serialize(broker);
}
}
properties_.push_back({name, result});
return result;
}
class TemplateObjectDescriptionData : public HeapObjectData {
public:
TemplateObjectDescriptionData(JSHeapBroker* broker, ObjectData** storage,
Handle<TemplateObjectDescription> object)
: HeapObjectData(broker, storage, object) {
DCHECK(!FLAG_turbo_direct_heap_access);
}
};
class CodeData : public HeapObjectData {
public:
CodeData(JSHeapBroker* broker, ObjectData** storage, Handle<Code> object)
: HeapObjectData(broker, storage, object),
inlined_bytecode_size_(object->inlined_bytecode_size()) {}
unsigned inlined_bytecode_size() const { return inlined_bytecode_size_; }
private:
unsigned const inlined_bytecode_size_;
};
#define DEFINE_IS(Name) \
bool ObjectData::Is##Name() const { \
if (should_access_heap()) { \
AllowHandleDereferenceIfNeeded allow_handle_dereference(kind()); \
return object()->Is##Name(); \
} \
if (is_smi()) return false; \
InstanceType instance_type = \
static_cast<const HeapObjectData*>(this)->GetMapInstanceType(); \
return InstanceTypeChecker::Is##Name(instance_type); \
}
HEAP_BROKER_SERIALIZED_OBJECT_LIST(DEFINE_IS)
HEAP_BROKER_NEVER_SERIALIZED_OBJECT_LIST(DEFINE_IS)
#undef DEFINE_IS
#define DEFINE_AS(Name) \
Name##Data* ObjectData::As##Name() { \
CHECK(Is##Name()); \
CHECK_EQ(kind_, kSerializedHeapObject); \
return static_cast<Name##Data*>(this); \
}
HEAP_BROKER_SERIALIZED_OBJECT_LIST(DEFINE_AS)
#undef DEFINE_AS
// TODO(solanes, v8:10866): Remove once FLAG_turbo_direct_heap_access is
// removed.
// This macro defines the Asxxx methods for NeverSerialized objects, which
// should only be used with direct heap access off.
#define DEFINE_AS(Name) \
Name##Data* ObjectData::As##Name() { \
DCHECK(!FLAG_turbo_direct_heap_access); \
CHECK(Is##Name()); \
CHECK_EQ(kind_, kSerializedHeapObject); \
return static_cast<Name##Data*>(this); \
}
HEAP_BROKER_NEVER_SERIALIZED_OBJECT_LIST(DEFINE_AS)
#undef DEFINE_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_;
}
ObjectData* 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);
DCHECK(elements_->IsFixedArrayBase());
}
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(!map->IsContextMap());
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_);
DCHECK(!map->IsContextMap());
backpointer_ = broker->GetOrCreateData(map->GetBackPointer());
}
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());
for (InternalIndex i : map->IterateOwnDescriptors()) {
SerializeOwnDescriptor(broker, i);
}
}
ObjectData* MapData::GetStrongValue(InternalIndex descriptor_index) const {
auto data = instance_descriptors_->contents().find(descriptor_index.as_int());
if (data == instance_descriptors_->contents().end()) return nullptr;
return data->second.value;
}
void MapData::SerializeOwnDescriptor(JSHeapBroker* broker,
InternalIndex 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(kRelaxedLoad))
->AsDescriptorArray();
}
ZoneMap<int, PropertyDescriptor>& contents =
instance_descriptors()->contents();
CHECK_LT(descriptor_index.as_int(), map->NumberOfOwnDescriptors());
if (contents.find(descriptor_index.as_int()) != contents.end()) return;
Isolate* const isolate = broker->isolate();
auto descriptors =
Handle<DescriptorArray>::cast(instance_descriptors_->object());
CHECK_EQ(*descriptors, map->instance_descriptors(kRelaxedLoad));
PropertyDescriptor d;
d.key = broker->GetOrCreateData(descriptors->GetKey(descriptor_index));
MaybeObject value = descriptors->GetValue(descriptor_index);
HeapObject obj;
if (value.GetHeapObjectIfStrong(&obj)) {
d.value = broker->GetOrCreateData(obj);
}
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));
d.field_type =
broker->GetOrCreateData(descriptors->GetFieldType(descriptor_index));
d.is_unboxed_double_field = map->IsUnboxedDoubleField(d.field_index);
}
contents[descriptor_index.as_int()] = d;
if (d.details.location() == kField && !d.field_owner->should_access_heap()) {
// Recurse on the owner map.
d.field_owner->AsMap()->SerializeOwnDescriptor(broker, descriptor_index);
}
TRACE(broker, "Copied descriptor " << descriptor_index.as_int() << " into "
<< instance_descriptors_ << " ("
<< contents.size() << " total)");
}
void MapData::SerializeRootMap(JSHeapBroker* broker) {
if (serialized_root_map_) return;
serialized_root_map_ = true;
TraceScope tracer(broker, this, "MapData::SerializeRootMap");
Handle<Map> map = Handle<Map>::cast(object());
DCHECK_NULL(root_map_);
root_map_ = broker->GetOrCreateData(map->FindRootMap(broker->isolate()));
}
ObjectData* MapData::FindRootMap() const { return root_map_; }
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 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);
DCHECK(elements_->IsFixedArrayBase());
if (empty_or_cow || elements_->should_access_heap()) {
// 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);
if (!value_data->should_access_heap()) {
value_data->AsJSObject()->SerializeRecursiveAsBoilerplate(broker,
depth - 1);
}
}
}
} else {
CHECK(boilerplate->HasDoubleElements());
CHECK_LE(elements_object->Size(), kMaxRegularHeapObjectSize);
DCHECK_EQ(elements_->kind(), ObjectDataKind::kSerializedHeapObject);
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(kRelaxedLoad), isolate);
for (InternalIndex i : boilerplate->map().IterateOwnDescriptors()) {
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 double fields we use a sentinel NaN value to mark
// uninitialized fields. A boilerplate value with such a field may migrate
// from its double to a tagged representation. If the double is unboxed,
// the raw double is converted to a heap number, otherwise the (boxed)
// double ceases to be mutable, and becomes a normal 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 (!details.representation().IsDouble() && value->IsHeapNumber() &&
HeapNumber::cast(*value).value_as_bits() == kHoleNanInt64) {
value = isolate->factory()->uninitialized_value();
}
ObjectData* value_data = broker->GetOrCreateData(value);
if (value_data->IsJSObject() && !value_data->should_access_heap()) {
value_data->AsJSObject()->SerializeRecursiveAsBoilerplate(broker,
depth - 1);
}
inobject_fields_.push_back(JSObjectField{value_data});
}
}
TRACE(broker, "Copied " << inobject_fields_.size() << " in-object fields");
if (!map()->should_access_heap()) {
map()->AsMap()->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 {
#ifdef DEBUG
if (broker()->mode() == JSHeapBroker::kSerialized &&
data_->used_status == ObjectData::Usage::kUnused) {
data_->used_status = ObjectData::Usage::kOnlyIdentityUsed;
}
#endif // DEBUG
return data_ == other.data_;
}
Isolate* ObjectRef::isolate() const { return broker()->isolate(); }
ContextRef ContextRef::previous(size_t* depth,
SerializationPolicy policy) const {
DCHECK_NOT_NULL(depth);
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
Context current = *object();
while (*depth != 0 && current.unchecked_previous().IsContext()) {
current = Context::cast(current.unchecked_previous());
(*depth)--;
}
return ContextRef(broker(), broker()->CanonicalPersistentHandle(current));
}
if (*depth == 0) return *this;
ObjectData* previous_data = data()->AsContext()->previous(broker(), policy);
if (previous_data == nullptr || !previous_data->IsContext()) return *this;
*depth = *depth - 1;
return ContextRef(broker(), previous_data).previous(depth, policy);
}
base::Optional<ObjectRef> ContextRef::get(int index,
SerializationPolicy policy) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
Handle<Object> value(object()->get(index), broker()->isolate());
return ObjectRef(broker(), value);
}
ObjectData* optional_slot =
data()->AsContext()->GetSlot(broker(), index, policy);
if (optional_slot != nullptr) {
return ObjectRef(broker(), optional_slot);
}
return base::nullopt;
}
SourceTextModuleRef ContextRef::GetModule(SerializationPolicy policy) const {
ContextRef current = *this;
while (current.map().instance_type() != MODULE_CONTEXT_TYPE) {
size_t depth = 1;
current = current.previous(&depth, policy);
CHECK_EQ(depth, 0);
}
return current.get(Context::EXTENSION_INDEX, policy)
.value()
.AsSourceTextModule();
}
JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone,
bool tracing_enabled, bool is_concurrent_inlining,
CodeKind code_kind)
: isolate_(isolate),
zone_(broker_zone),
refs_(zone()->New<RefsMap>(kMinimalRefsBucketCount, AddressMatcher(),
zone())),
root_index_map_(isolate),
array_and_object_prototypes_(zone()),
tracing_enabled_(tracing_enabled),
is_concurrent_inlining_(is_concurrent_inlining),
code_kind_(code_kind),
feedback_(zone()),
bytecode_analyses_(zone()),
property_access_infos_(zone()),
minimorphic_property_access_infos_(zone()),
typed_array_string_tags_(zone()),
serialized_functions_(zone()) {
// Note that this initialization of {refs_} 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
// immediately with a larger-capacity one. It doesn't seem to affect the
// performance in a noticeable way though.
TRACE(this, "Constructing heap broker");
}
JSHeapBroker::~JSHeapBroker() { DCHECK_NULL(local_isolate_); }
void JSHeapBroker::SetPersistentAndCopyCanonicalHandlesForTesting(
std::unique_ptr<PersistentHandles> persistent_handles,
std::unique_ptr<CanonicalHandlesMap> canonical_handles) {
set_persistent_handles(std::move(persistent_handles));
CopyCanonicalHandlesForTesting(std::move(canonical_handles));
}
void JSHeapBroker::CopyCanonicalHandlesForTesting(
std::unique_ptr<CanonicalHandlesMap> canonical_handles) {
DCHECK_NULL(canonical_handles_);
canonical_handles_ = std::make_unique<CanonicalHandlesMap>(
isolate_->heap(), ZoneAllocationPolicy(zone()));
CanonicalHandlesMap::IteratableScope it_scope(canonical_handles.get());
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
Address* entry = *it.entry();
Object key = it.key();
canonical_handles_->Insert(key, entry);
}
}
std::string JSHeapBroker::Trace() const {
std::ostringstream oss;
oss << "[" << this << "] ";
for (unsigned i = 0; i < trace_indentation_ * 2; ++i) oss.put(' ');
return oss.str();
}
void JSHeapBroker::AttachLocalIsolate(OptimizedCompilationInfo* info,
LocalIsolate* local_isolate) {
set_canonical_handles(info->DetachCanonicalHandles());
DCHECK_NULL(local_isolate_);
local_isolate_ = local_isolate;
DCHECK_NOT_NULL(local_isolate_);
local_isolate_->heap()->AttachPersistentHandles(
info->DetachPersistentHandles());
}
void JSHeapBroker::DetachLocalIsolate(OptimizedCompilationInfo* info) {
DCHECK_NULL(ph_);
DCHECK_NOT_NULL(local_isolate_);
std::unique_ptr<PersistentHandles> ph =
local_isolate_->heap()->DetachPersistentHandles();
local_isolate_ = nullptr;
info->set_canonical_handles(DetachCanonicalHandles());
info->set_persistent_handles(std::move(ph));
}
void JSHeapBroker::StopSerializing() {
CHECK_EQ(mode_, kSerializing);
TRACE(this, "Stopping serialization");
mode_ = kSerialized;
}
#ifdef DEBUG
void JSHeapBroker::PrintRefsAnalysis() const {
// Usage counts
size_t used_total = 0, unused_total = 0, identity_used_total = 0;
for (RefsMap::Entry* ref = refs_->Start(); ref != nullptr;
ref = refs_->Next(ref)) {
switch (ref->value->used_status) {
case ObjectData::Usage::kUnused:
++unused_total;
break;
case ObjectData::Usage::kOnlyIdentityUsed:
++identity_used_total;
break;
case ObjectData::Usage::kDataUsed:
++used_total;
break;
}
}
// Ref types analysis
TRACE_BROKER_MEMORY(
this, "Refs: " << refs_->occupancy() << "; data used: " << used_total
<< "; only identity used: " << identity_used_total
<< "; unused: " << unused_total);
size_t used_smis = 0, unused_smis = 0, identity_used_smis = 0;
size_t used[LAST_TYPE + 1] = {0};
size_t unused[LAST_TYPE + 1] = {0};
size_t identity_used[LAST_TYPE + 1] = {0};
for (RefsMap::Entry* ref = refs_->Start(); ref != nullptr;
ref = refs_->Next(ref)) {
if (ref->value->is_smi()) {
switch (ref->value->used_status) {
case ObjectData::Usage::kUnused:
++unused_smis;
break;
case ObjectData::Usage::kOnlyIdentityUsed:
++identity_used_smis;
break;
case ObjectData::Usage::kDataUsed:
++used_smis;
break;
}
} else {
InstanceType instance_type;
if (ref->value->should_access_heap()) {
instance_type = Handle<HeapObject>::cast(ref->value->object())
->map()
.instance_type();
} else {
instance_type = ref->value->AsHeapObject()->GetMapInstanceType();
}
CHECK_LE(FIRST_TYPE, instance_type);
CHECK_LE(instance_type, LAST_TYPE);
switch (ref->value->used_status) {
case ObjectData::Usage::kUnused:
++unused[instance_type];
break;
case ObjectData::Usage::kOnlyIdentityUsed:
++identity_used[instance_type];
break;
case ObjectData::Usage::kDataUsed:
++used[instance_type];
break;
}
}
}
TRACE_BROKER_MEMORY(
this, "Smis: " << used_smis + identity_used_smis + unused_smis
<< "; data used: " << used_smis << "; only identity used: "
<< identity_used_smis << "; unused: " << unused_smis);
for (uint16_t i = FIRST_TYPE; i <= LAST_TYPE; ++i) {
size_t total = used[i] + identity_used[i] + unused[i];
if (total == 0) continue;
TRACE_BROKER_MEMORY(
this, InstanceType(i) << ": " << total << "; data used: " << used[i]
<< "; only identity used: " << identity_used[i]
<< "; unused: " << unused[i]);
}
}
#endif // DEBUG
void JSHeapBroker::Retire() {
CHECK_EQ(mode_, kSerialized);
TRACE(this, "Retiring");
mode_ = kRetired;
#ifdef DEBUG
PrintRefsAnalysis();
#endif // DEBUG
}
void JSHeapBroker::SetTargetNativeContextRef(
Handle<NativeContext> native_context) {
// The MapData constructor uses {target_native_context_}. This creates a
// benign cycle that we break by setting {target_native_context_} right before
// starting to serialize (thus creating dummy data), and then again properly
// right after.
DCHECK((mode() == kDisabled && !target_native_context_.has_value()) ||
(mode() == kSerializing &&
target_native_context_->object().equals(native_context) &&
target_native_context_->data_->kind() == kUnserializedHeapObject));
target_native_context_ = NativeContextRef(this, native_context);
}
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());
}
StringRef JSHeapBroker::GetTypedArrayStringTag(ElementsKind kind) {
DCHECK(IsTypedArrayElementsKind(kind));
switch (kind) {
#define TYPED_ARRAY_STRING_TAG(Type, type, TYPE, ctype) \
case ElementsKind::TYPE##_ELEMENTS: \
return StringRef(this, isolate()->factory()->Type##Array_string());
TYPED_ARRAYS(TYPED_ARRAY_STRING_TAG)
#undef TYPED_ARRAY_STRING_TAG
default:
UNREACHABLE();
}
}
bool JSHeapBroker::ShouldBeSerializedForCompilation(
const SharedFunctionInfoRef& shared, const FeedbackVectorRef& feedback,
const HintsVector& arguments) const {
if (serialized_functions_.size() >= kMaxSerializedFunctionsCacheSize) {
TRACE_BROKER_MISSING(this,
"opportunity - serialized functions cache is full.");
return false;
}
SerializedFunction function{shared, feedback};
auto matching_functions = serialized_functions_.equal_range(function);
return std::find_if(matching_functions.first, matching_functions.second,
[&arguments](const auto& entry) {
return entry.second == arguments;
}) == matching_functions.second;
}
void JSHeapBroker::SetSerializedForCompilation(
const SharedFunctionInfoRef& shared, const FeedbackVectorRef& feedback,
const HintsVector& arguments) {
SerializedFunction function{shared, feedback};
serialized_functions_.insert({function, arguments});
TRACE(this, "Set function " << shared << " with " << feedback
<< " as serialized for compilation");
}
bool JSHeapBroker::IsSerializedForCompilation(
const SharedFunctionInfoRef& shared,
const FeedbackVectorRef& feedback) const {
if (mode() == kDisabled) return true;
SerializedFunction function = {shared, feedback};
return serialized_functions_.find(function) != serialized_functions_.end();
}
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::InitializeAndStartSerializing(
Handle<NativeContext> native_context) {
TraceScope tracer(this, "JSHeapBroker::InitializeAndStartSerializing");
CHECK_EQ(mode_, kDisabled);
mode_ = kSerializing;
// Throw away the dummy data that we created while disabled.
refs_->Clear();
refs_ = nullptr;
refs_ =
zone()->New<RefsMap>(kInitialRefsBucketCount, AddressMatcher(), zone());
SetTargetNativeContextRef(native_context);
target_native_context().Serialize();
CollectArrayAndObjectPrototypes();
Factory* const f = isolate()->factory();
{
ObjectData* data;
data = GetOrCreateData(f->array_buffer_detaching_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->array_constructor_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->array_iterator_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->array_species_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->no_elements_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->promise_hook_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->promise_species_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->promise_then_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
data = GetOrCreateData(f->string_length_protector());
if (!data->should_access_heap()) data->AsPropertyCell()->Serialize(this);
}
GetOrCreateData(f->many_closures_cell());
GetOrCreateData(
CodeFactory::CEntry(isolate(), 1, kDontSaveFPRegs, kArgvOnStack, true));
TRACE(this, "Finished serializing standard objects");
}
// clang-format off
ObjectData* JSHeapBroker::GetOrCreateData(Handle<Object> object) {
RefsMap::Entry* entry = refs_->LookupOrInsert(object.address());
ObjectData* object_data = entry->value;
if (object_data == nullptr) {
ObjectData** data_storage = &(entry->value);
// TODO(neis): Remove these Allow* once we serialize everything upfront.
AllowHandleDereference handle_dereference;
if (object->IsSmi()) {
object_data = zone()->New<ObjectData>(this, data_storage, object, kSmi);
} else if (IsReadOnlyHeapObject(*object)) {
object_data = zone()->New<ObjectData>(this, data_storage, object,
kUnserializedReadOnlyHeapObject);
// TODO(solanes, v8:10866): Remove the if/else in this macro once we remove the
// FLAG_turbo_direct_heap_access.
#define CREATE_DATA_FOR_DIRECT_READ(name) \
} else if (object->Is##name()) { \
if (FLAG_turbo_direct_heap_access) { \
object_data = zone()->New<ObjectData>( \
this, data_storage, object, kNeverSerializedHeapObject); \
} else { \
CHECK_EQ(mode(), kSerializing); \
AllowHandleAllocation handle_allocation; \
object_data = zone()->New<name##Data>(this, data_storage, \
Handle<name>::cast(object)); \
}
HEAP_BROKER_NEVER_SERIALIZED_OBJECT_LIST(CREATE_DATA_FOR_DIRECT_READ)
#undef CREATE_DATA_FOR_DIRECT_READ
#define CREATE_DATA_FOR_SERIALIZATION(name) \
} else if (object->Is##name()) { \
CHECK_EQ(mode(), kSerializing); \
AllowHandleAllocation handle_allocation; \
object_data = zone()->New<name##Data>(this, data_storage, \
Handle<name>::cast(object));
HEAP_BROKER_SERIALIZED_OBJECT_LIST(CREATE_DATA_FOR_SERIALIZATION)
#undef CREATE_DATA_FOR_SERIALIZATION
} else {
UNREACHABLE();
}
// At this point the entry pointer is not guaranteed to be valid as
// the refs_ hash hable could be resized by one of the constructors above.
DCHECK_EQ(object_data, refs_->Lookup(object.address())->value);
}
return object_data;
}
// clang-format on
ObjectData* JSHeapBroker::GetOrCreateData(Object object) {
return GetOrCreateData(CanonicalPersistentHandle(object));
}
#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_SERIALIZED_OBJECT_LIST(DEFINE_IS_AND_AS)
HEAP_BROKER_NEVER_SERIALIZED_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 (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHeapAllocationIfNeeded allow_heap_allocation(data()->kind(),
broker()->mode());
Handle<Map> instance_map;
if (Map::TryGetObjectCreateMap(broker()->isolate(), object())
.ToHandle(&instance_map)) {
return MapRef(broker(), instance_map);
} else {
return base::Optional<MapRef>();
}
}
ObjectData* map_data = data()->AsJSObject()->object_create_map(broker());
if (map_data == nullptr) return base::Optional<MapRef>();
if (map_data->should_access_heap()) {
return MapRef(broker(), map_data->object());
}
return MapRef(broker(), map_data->AsMap());
}
#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 (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHeapAllocationIfNeeded allow_heap_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return MapRef(broker(),
Map::AsElementsKind(broker()->isolate(), object(), kind));
}
if (kind == elements_kind()) return *this;
const ZoneVector<ObjectData*>& 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() {
if (data()->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeForElementLoad(broker());
}
void MapRef::SerializeForElementStore() {
if (data()->should_access_heap()) return;
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.prototype().AsHeapObject().map();
if (prototype_map.oddball_type() == OddballType::kNull) return true;
if (!map.prototype().IsJSObject() || !prototype_map.is_stable() ||
!IsFastElementsKind(prototype_map.elements_kind())) {
return false;
}
if (prototype_maps != nullptr) prototype_maps->push_back(prototype_map);
}
}
} // namespace
void MapData::SerializeForElementLoad(JSHeapBroker* broker) {
if (serialized_for_element_load_) return;
serialized_for_element_load_ = true;
TraceScope tracer(broker, this, "MapData::SerializeForElementLoad");
SerializePrototype(broker);
}
void MapData::SerializeForElementStore(JSHeapBroker* broker) {
if (serialized_for_element_store_) return;
serialized_for_element_store_ = true;
TraceScope tracer(broker, this, "MapData::SerializeForElementStore");
HasOnlyStablePrototypesWithFastElementsHelper(broker, MapRef(broker, this),
nullptr);
}
bool MapRef::HasOnlyStablePrototypesWithFastElements(
ZoneVector<MapRef>* prototype_maps) {
for (MapRef prototype_map = *this;;) {
if (prototype_maps == nullptr) prototype_map.SerializePrototype();
prototype_map = prototype_map.prototype().AsHeapObject().map();
if (prototype_map.oddball_type() == OddballType::kNull) return true;
if (!prototype().IsJSObject() || !prototype_map.is_stable() ||
!IsFastElementsKind(prototype_map.elements_kind())) {
return false;
}
if (prototype_maps != nullptr) prototype_maps->push_back(prototype_map);
}
}
bool MapRef::supports_fast_array_iteration() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
return SupportsFastArrayIteration(broker()->isolate(), object());
}
return data()->AsMap()->supports_fast_array_iteration();
}
bool MapRef::supports_fast_array_resize() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
return SupportsFastArrayResize(broker()->isolate(), object());
}
return data()->AsMap()->supports_fast_array_resize();
}
int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
return object()->ComputeInstanceSizeWithMinSlack(broker()->isolate());
}
return data()->AsJSFunction()->initial_map_instance_size_with_min_slack();
}
OddballType MapRef::oddball_type() const {
if (instance_type() != ODDBALL_TYPE) {
return OddballType::kNone;
}
Factory* f = broker()->isolate()->factory();
if (equals(MapRef(broker(), f->undefined_map()))) {
return OddballType::kUndefined;
}
if (equals(MapRef(broker(), f->null_map()))) {
return OddballType::kNull;
}
if (equals(MapRef(broker(), f->boolean_map()))) {
return OddballType::kBoolean;
}
if (equals(MapRef(broker(), f->the_hole_map()))) {
return OddballType::kHole;
}
if (equals(MapRef(broker(), f->uninitialized_map()))) {
return OddballType::kUninitialized;
}
DCHECK(equals(MapRef(broker(), f->termination_exception_map())) ||
equals(MapRef(broker(), f->arguments_marker_map())) ||
equals(MapRef(broker(), f->optimized_out_map())) ||
equals(MapRef(broker(), f->stale_register_map())));
return OddballType::kOther;
}
FeedbackCellRef FeedbackVectorRef::GetClosureFeedbackCell(int index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return FeedbackCellRef(broker(), object()->GetClosureFeedbackCell(index));
}
return FeedbackCellRef(
broker(),
data()->AsFeedbackVector()->GetClosureFeedbackCell(broker(), index));
}
double JSObjectRef::RawFastDoublePropertyAt(FieldIndex index) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->RawFastDoublePropertyAt(index);
}
JSObjectData* object_data = data()->AsJSObject();
CHECK(index.is_inobject());
return object_data->GetInobjectField(index.property_index()).AsDouble();
}
uint64_t JSObjectRef::RawFastDoublePropertyAsBitsAt(FieldIndex index) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->RawFastDoublePropertyAsBitsAt(index);
}
JSObjectData* object_data = data()->AsJSObject();
CHECK(index.is_inobject());
return object_data->GetInobjectField(index.property_index()).AsBitsOfDouble();
}
ObjectRef JSObjectRef::RawFastPropertyAt(FieldIndex index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return ObjectRef(broker(), broker()->CanonicalPersistentHandle(
object()->RawFastPropertyAt(index)));
}
JSObjectData* object_data = data()->AsJSObject();
CHECK(index.is_inobject());
return ObjectRef(
broker(),
object_data->GetInobjectField(index.property_index()).AsObject());
}
bool AllocationSiteRef::IsFastLiteral() const {
if (data_->should_access_heap()) {
CHECK_NE(data_->kind(), ObjectDataKind::kNeverSerializedHeapObject);
AllowHeapAllocationIfNeeded allow_heap_allocation(
data()->kind(), broker()->mode()); // For TryMigrateInstance.
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return IsInlinableFastLiteral(
handle(object()->boilerplate(), broker()->isolate()));
}
return data()->AsAllocationSite()->IsFastLiteral();
}
void AllocationSiteRef::SerializeBoilerplate() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsAllocationSite()->SerializeBoilerplate(broker());
}
void JSObjectRef::SerializeElements() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsJSObject()->SerializeElements(broker());
}
void JSObjectRef::EnsureElementsTenured() {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHeapAllocationIfNeeded allow_heap_allocation(data()->kind(),
broker()->mode());
Handle<FixedArrayBase> object_elements = elements().object();
if (ObjectInYoungGeneration(*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()->set_elements(*object_elements);
}
return;
}
CHECK(data()->AsJSObject()->cow_or_empty_elements_tenured());
}
FieldIndex MapRef::GetFieldIndexFor(InternalIndex descriptor_index) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return FieldIndex::ForDescriptor(*object(), descriptor_index);
}
DescriptorArrayData* descriptors = data()->AsMap()->instance_descriptors();
return descriptors->contents().at(descriptor_index.as_int()).field_index;
}
int MapRef::GetInObjectPropertyOffset(int i) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->GetInObjectPropertyOffset(i);
}
return (GetInObjectPropertiesStartInWords() + i) * kTaggedSize;
}
PropertyDetails MapRef::GetPropertyDetails(
InternalIndex descriptor_index) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()
->instance_descriptors(kRelaxedLoad)
.GetDetails(descriptor_index);
}
DescriptorArrayData* descriptors = data()->AsMap()->instance_descriptors();
return descriptors->contents().at(descriptor_index.as_int()).details;
}
NameRef MapRef::GetPropertyKey(InternalIndex descriptor_index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return NameRef(broker(), broker()->CanonicalPersistentHandle(
object()
->instance_descriptors(kRelaxedLoad)
.GetKey(descriptor_index)));
}
DescriptorArrayData* descriptors = data()->AsMap()->instance_descriptors();
return NameRef(broker(),
descriptors->contents().at(descriptor_index.as_int()).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));
}
bool MapRef::IsPrimitiveMap() const {
return instance_type() <= LAST_PRIMITIVE_HEAP_OBJECT_TYPE;
}
MapRef MapRef::FindFieldOwner(InternalIndex descriptor_index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
Handle<Map> owner(
object()->FindFieldOwner(broker()->isolate(), descriptor_index),
broker()->isolate());
return MapRef(broker(), owner);
}
DescriptorArrayData* descriptors = data()->AsMap()->instance_descriptors();
return MapRef(
broker(),
descriptors->contents().at(descriptor_index.as_int()).field_owner);
}
ObjectRef MapRef::GetFieldType(InternalIndex descriptor_index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
Handle<FieldType> field_type(object()
->instance_descriptors(kRelaxedLoad)
.GetFieldType(descriptor_index),
broker()->isolate());
return ObjectRef(broker(), field_type);
}
DescriptorArrayData* descriptors = data()->AsMap()->instance_descriptors();
return ObjectRef(
broker(),
descriptors->contents().at(descriptor_index.as_int()).field_type);
}
bool MapRef::IsUnboxedDoubleField(InternalIndex descriptor_index) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->IsUnboxedDoubleField(
FieldIndex::ForDescriptor(*object(), descriptor_index));
}
DescriptorArrayData* descriptors = data()->AsMap()->instance_descriptors();
return descriptors->contents()
.at(descriptor_index.as_int())
.is_unboxed_double_field;
}
uint16_t StringRef::GetFirstChar() {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->Get(0);
}
return data()->AsString()->first_char();
}
base::Optional<double> StringRef::ToNumber() {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
return StringToDouble(object());
}
return data()->AsString()->to_number();
}
int ArrayBoilerplateDescriptionRef::constants_elements_length() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->constant_elements().length();
}
return data()->AsArrayBoilerplateDescription()->constants_elements_length();
}
ObjectRef FixedArrayRef::get(int i) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return ObjectRef(broker(),
broker()->CanonicalPersistentHandle(object()->get(i)));
}
return ObjectRef(broker(), data()->AsFixedArray()->Get(i));
}
Float64 FixedDoubleArrayRef::get(int i) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return Float64::FromBits(object()->get_representation(i));
} else {
return data()->AsFixedDoubleArray()->Get(i);
}
}
uint8_t BytecodeArrayRef::get(int index) const { return object()->get(index); }
Address BytecodeArrayRef::GetFirstBytecodeAddress() const {
return object()->GetFirstBytecodeAddress();
}
Handle<Object> BytecodeArrayRef::GetConstantAtIndex(int index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return broker()->CanonicalPersistentHandle(
object()->constant_pool().get(index));
}
return data()->AsBytecodeArray()->GetConstantAtIndex(index,
broker()->isolate());
}
bool BytecodeArrayRef::IsConstantAtIndexSmi(int index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->constant_pool().get(index).IsSmi();
}
return data()->AsBytecodeArray()->IsConstantAtIndexSmi(index);
}
Smi BytecodeArrayRef::GetConstantAtIndexAsSmi(int index) const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return Smi::cast(object()->constant_pool().get(index));
}
return data()->AsBytecodeArray()->GetConstantAtIndexAsSmi(index);
}
void BytecodeArrayRef::SerializeForCompilation() {
if (data_->should_access_heap()) return;
data()->AsBytecodeArray()->SerializeForCompilation(broker());
}
Handle<ByteArray> BytecodeArrayRef::SourcePositionTable() const {
return broker()->CanonicalPersistentHandle(object()->SourcePositionTable());
}
Address BytecodeArrayRef::handler_table_address() const {
return reinterpret_cast<Address>(
object()->handler_table().GetDataStartAddress());
}
int BytecodeArrayRef::handler_table_size() const {
return object()->handler_table().length();
}
#define IF_ACCESS_FROM_HEAP_C(name) \
if (data_->should_access_heap()) { \
AllowHandleAllocationIfNeeded handle_allocation(data_->kind(), \
broker()->mode()); \
AllowHandleDereferenceIfNeeded allow_handle_dereference(data_->kind(), \
broker()->mode()); \
return object()->name(); \
}
#define IF_ACCESS_FROM_HEAP(result, name) \
if (data_->should_access_heap()) { \
AllowHandleAllocationIfNeeded handle_allocation(data_->kind(), \
broker()->mode()); \
AllowHandleDereferenceIfNeeded handle_dereference(data_->kind(), \
broker()->mode()); \
return result##Ref(broker(), \
broker()->CanonicalPersistentHandle(object()->name())); \
}
// Macros for definining a const getter that, depending on the data kind,
// either looks into the heap or into the serialized data.
#define BIMODAL_ACCESSOR(holder, result, name) \
result##Ref holder##Ref::name() const { \
IF_ACCESS_FROM_HEAP(result, name); \
return result##Ref(broker(), 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_ACCESS_FROM_HEAP_C(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_ACCESS_FROM_HEAP_C(name); \
return BitField::decode(ObjectRef::data()->As##holder()->field()); \
}
// Like IF_ACCESS_FROM_HEAP[_C] but we also allow direct heap access for
// kSerialized only for methods that we identified to be safe.
#define IF_ACCESS_FROM_HEAP_WITH_FLAG(result, name) \
if (data_->should_access_heap() || FLAG_turbo_direct_heap_access) { \
AllowHandleAllocationIfNeeded handle_allocation( \
data_->kind(), broker()->mode(), FLAG_turbo_direct_heap_access); \
AllowHandleDereferenceIfNeeded allow_handle_dereference( \
data_->kind(), broker()->mode(), FLAG_turbo_direct_heap_access); \
return result##Ref(broker(), \
broker()->CanonicalPersistentHandle(object()->name())); \
}
#define IF_ACCESS_FROM_HEAP_WITH_FLAG_C(name) \
if (data_->should_access_heap() || FLAG_turbo_direct_heap_access) { \
AllowHandleAllocationIfNeeded handle_allocation( \
data_->kind(), broker()->mode(), FLAG_turbo_direct_heap_access); \
AllowHandleDereferenceIfNeeded allow_handle_dereference( \
data_->kind(), broker()->mode(), FLAG_turbo_direct_heap_access); \
return object()->name(); \
}
// Like BIMODAL_ACCESSOR[_C] except that we force a direct heap access if
// FLAG_turbo_direct_heap_access is true (even for kSerialized). This is because
// we identified the method to be safe to use direct heap access, but the
// holder##Data class still needs to be serialized.
#define BIMODAL_ACCESSOR_WITH_FLAG(holder, result, name) \
result##Ref holder##Ref::name() const { \
IF_ACCESS_FROM_HEAP_WITH_FLAG(result, name); \
return result##Ref(broker(), ObjectRef::data()->As##holder()->name()); \
}
#define BIMODAL_ACCESSOR_WITH_FLAG_C(holder, result, name) \
result holder##Ref::name() const { \
IF_ACCESS_FROM_HEAP_WITH_FLAG_C(name); \
return ObjectRef::data()->As##holder()->name(); \
}
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, AllocationType, GetAllocationType)
BIMODAL_ACCESSOR_C(BigInt, uint64_t, AsUint64)
BIMODAL_ACCESSOR_C(BytecodeArray, int, register_count)
BIMODAL_ACCESSOR_C(BytecodeArray, int, parameter_count)
BIMODAL_ACCESSOR_C(BytecodeArray, interpreter::Register,
incoming_new_target_or_generator_register)
BIMODAL_ACCESSOR_C(FeedbackVector, double, invocation_count)
BIMODAL_ACCESSOR(HeapObject, Map, map)
BIMODAL_ACCESSOR_C(HeapNumber, double, value)
BIMODAL_ACCESSOR(JSArray, Object, length)
BIMODAL_ACCESSOR(JSBoundFunction, JSReceiver, bound_target_function)
BIMODAL_ACCESSOR(JSBoundFunction, Object, bound_this)
BIMODAL_ACCESSOR(JSBoundFunction, FixedArray, bound_arguments)
BIMODAL_ACCESSOR_C(JSDataView, size_t, byte_length)
BIMODAL_ACCESSOR_C(JSFunction, bool, has_feedback_vector)
BIMODAL_ACCESSOR_C(JSFunction, bool, has_initial_map)
BIMODAL_ACCESSOR_C(JSFunction, bool, has_prototype)
BIMODAL_ACCESSOR_C(JSFunction, bool, HasAttachedOptimizedCode)
BIMODAL_ACCESSOR_C(JSFunction, bool, PrototypeRequiresRuntimeLookup)
BIMODAL_ACCESSOR(JSFunction, Context, context)
BIMODAL_ACCESSOR(JSFunction, NativeContext, native_context)
BIMODAL_ACCESSOR(JSFunction, Map, initial_map)
BIMODAL_ACCESSOR(JSFunction, Object, prototype)
BIMODAL_ACCESSOR(JSFunction, SharedFunctionInfo, shared)
BIMODAL_ACCESSOR(JSFunction, FeedbackCell, raw_feedback_cell)
BIMODAL_ACCESSOR(JSFunction, FeedbackVector, feedback_vector)
BIMODAL_ACCESSOR(JSFunction, Code, code)
BIMODAL_ACCESSOR_C(JSGlobalObject, bool, IsDetached)
BIMODAL_ACCESSOR_C(JSTypedArray, bool, is_on_heap)
BIMODAL_ACCESSOR_C(JSTypedArray, size_t, length)
BIMODAL_ACCESSOR(JSTypedArray, HeapObject, buffer)
BIMODAL_ACCESSOR_B(Map, bit_field2, elements_kind, Map::Bits2::ElementsKindBits)
BIMODAL_ACCESSOR_B(Map, bit_field3, is_dictionary_map,
Map::Bits3::IsDictionaryMapBit)
BIMODAL_ACCESSOR_B(Map, bit_field3, is_deprecated, Map::Bits3::IsDeprecatedBit)
BIMODAL_ACCESSOR_B(Map, bit_field3, NumberOfOwnDescriptors,
Map::Bits3::NumberOfOwnDescriptorsBits)
BIMODAL_ACCESSOR_B(Map, bit_field3, is_migration_target,
Map::Bits3::IsMigrationTargetBit)
BIMODAL_ACCESSOR_B(Map, bit_field3, is_extensible, Map::Bits3::IsExtensibleBit)
BIMODAL_ACCESSOR_B(Map, bit_field, has_prototype_slot,
Map::Bits1::HasPrototypeSlotBit)
BIMODAL_ACCESSOR_B(Map, bit_field, is_access_check_needed,
Map::Bits1::IsAccessCheckNeededBit)
BIMODAL_ACCESSOR_B(Map, bit_field, is_callable, Map::Bits1::IsCallableBit)
BIMODAL_ACCESSOR_B(Map, bit_field, has_indexed_interceptor,
Map::Bits1::HasIndexedInterceptorBit)
BIMODAL_ACCESSOR_B(Map, bit_field, is_constructor, Map::Bits1::IsConstructorBit)
BIMODAL_ACCESSOR_B(Map, bit_field, is_undetectable,
Map::Bits1::IsUndetectableBit)
BIMODAL_ACCESSOR_C(Map, int, instance_size)
BIMODAL_ACCESSOR_C(Map, int, NextFreePropertyIndex)
BIMODAL_ACCESSOR_C(Map, int, UnusedPropertyFields)
BIMODAL_ACCESSOR(Map, HeapObject, prototype)
BIMODAL_ACCESSOR_C(Map, InstanceType, instance_type)
BIMODAL_ACCESSOR(Map, Object, GetConstructor)
BIMODAL_ACCESSOR(Map, HeapObject, GetBackPointer)
BIMODAL_ACCESSOR_C(Map, bool, is_abandoned_prototype_map)
BIMODAL_ACCESSOR_C(Code, unsigned, inlined_bytecode_size)
#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_C(ObjectBoilerplateDescription, int, size)
BIMODAL_ACCESSOR(PropertyCell, Object, value)
BIMODAL_ACCESSOR_C(PropertyCell, PropertyDetails, property_details)
base::Optional<CallHandlerInfoRef> FunctionTemplateInfoRef::call_code() const {
if (data_->should_access_heap()) {
return CallHandlerInfoRef(broker(), broker()->CanonicalPersistentHandle(
object()->call_code(kAcquireLoad)));
}
ObjectData* call_code = data()->AsFunctionTemplateInfo()->call_code();
if (!call_code) return base::nullopt;
return CallHandlerInfoRef(broker(), call_code);
}
bool FunctionTemplateInfoRef::is_signature_undefined() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
return object()->signature().IsUndefined(broker()->isolate());
}
return data()->AsFunctionTemplateInfo()->is_signature_undefined();
}
bool FunctionTemplateInfoRef::has_call_code() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
CallOptimization call_optimization(broker()->isolate(), object());
return call_optimization.is_simple_api_call();
}
return data()->AsFunctionTemplateInfo()->has_call_code();
}
BIMODAL_ACCESSOR_C(FunctionTemplateInfo, bool, accept_any_receiver)
HolderLookupResult FunctionTemplateInfoRef::LookupHolderOfExpectedType(
MapRef receiver_map, SerializationPolicy policy) {
const HolderLookupResult not_found;
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
CallOptimization call_optimization(broker()->isolate(), object());
Handle<Map> receiver_map_ref(receiver_map.object());
if (!receiver_map_ref->IsJSReceiverMap() ||
(receiver_map_ref->is_access_check_needed() &&
!object()->accept_any_receiver())) {
return not_found;
}
HolderLookupResult result;
Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType(
receiver_map_ref, &result.lookup);
switch (result.lookup) {
case CallOptimization::kHolderFound:
result.holder = JSObjectRef(broker(), holder);
break;
default:
DCHECK_EQ(result.holder, base::nullopt);
break;
}
return result;
}
FunctionTemplateInfoData* fti_data = data()->AsFunctionTemplateInfo();
KnownReceiversMap::iterator lookup_it =
fti_data->known_receivers().find(receiver_map.data());
if (lookup_it != fti_data->known_receivers().cend()) {
return lookup_it->second;
}
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_BROKER_MISSING(broker(),
"holder for receiver with map " << receiver_map);
return not_found;
}
if (!receiver_map.IsJSReceiverMap() ||
(receiver_map.is_access_check_needed() && !accept_any_receiver())) {
fti_data->known_receivers().insert({receiver_map.data(), not_found});
return not_found;
}
HolderLookupResult result;
CallOptimization call_optimization(broker()->isolate(), object());
Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType(
receiver_map.object(), &result.lookup);
switch (result.lookup) {
case CallOptimization::kHolderFound: {
result.holder = JSObjectRef(broker(), holder);
fti_data->known_receivers().insert({receiver_map.data(), result});
break;
}
default: {
DCHECK_EQ(result.holder, base::nullopt);
fti_data->known_receivers().insert({receiver_map.data(), result});
}
}
return result;
}
BIMODAL_ACCESSOR(CallHandlerInfo, Object, data)
BIMODAL_ACCESSOR_C(ScopeInfo, int, ContextLength)
BIMODAL_ACCESSOR_C(ScopeInfo, bool, HasContextExtensionSlot)
BIMODAL_ACCESSOR_C(ScopeInfo, bool, HasOuterScopeInfo)
BIMODAL_ACCESSOR(ScopeInfo, ScopeInfo, OuterScopeInfo)
BIMODAL_ACCESSOR_C(SharedFunctionInfo, int, builtin_id)
BIMODAL_ACCESSOR(SharedFunctionInfo, BytecodeArray, GetBytecodeArray)
#define DEF_SFI_ACCESSOR(type, name) \
BIMODAL_ACCESSOR_WITH_FLAG_C(SharedFunctionInfo, type, name)
BROKER_SFI_FIELDS(DEF_SFI_ACCESSOR)
#undef DEF_SFI_ACCESSOR
BIMODAL_ACCESSOR_C(SharedFunctionInfo, SharedFunctionInfo::Inlineability,
GetInlineability)
BIMODAL_ACCESSOR_C(String, int, length)
BIMODAL_ACCESSOR(FeedbackCell, HeapObject, value)
base::Optional<ObjectRef> MapRef::GetStrongValue(
InternalIndex descriptor_index) const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
MaybeObject value =
object()->instance_descriptors(kRelaxedLoad).GetValue(descriptor_index);
HeapObject object;
if (value.GetHeapObjectIfStrong(&object)) {
return ObjectRef(broker(), broker()->CanonicalPersistentHandle((object)));
}
return base::nullopt;
}
ObjectData* value = data()->AsMap()->GetStrongValue(descriptor_index);
if (!value) {
return base::nullopt;
}
return ObjectRef(broker(), value);
}
void MapRef::SerializeRootMap() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeRootMap(broker());
}
base::Optional<MapRef> MapRef::FindRootMap() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return MapRef(broker(), broker()->CanonicalPersistentHandle(
object()->FindRootMap(broker()->isolate())));
}
ObjectData* map_data = data()->AsMap()->FindRootMap();
if (map_data != nullptr) {
return MapRef(broker(), map_data);
}
TRACE_BROKER_MISSING(broker(), "root map for object " << *this);
return base::nullopt;
}
void* JSTypedArrayRef::data_ptr() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->DataPtr();
}
return data()->AsJSTypedArray()->data_ptr();
}
bool MapRef::IsInobjectSlackTrackingInProgress() const {
IF_ACCESS_FROM_HEAP_C(IsInobjectSlackTrackingInProgress);
return Map::Bits3::ConstructionCounterBits::decode(
data()->AsMap()->bit_field3()) != Map::kNoSlackTracking;
}
int MapRef::constructor_function_index() const {
IF_ACCESS_FROM_HEAP_C(GetConstructorFunctionIndex);
CHECK(IsPrimitiveMap());
return data()->AsMap()->constructor_function_index();
}
bool MapRef::is_stable() const {
IF_ACCESS_FROM_HEAP_C(is_stable);
return !Map::Bits3::IsUnstableBit::decode(data()->AsMap()->bit_field3());
}
bool MapRef::CanBeDeprecated() const {
IF_ACCESS_FROM_HEAP_C(CanBeDeprecated);
CHECK_GT(NumberOfOwnDescriptors(), 0);
return data()->AsMap()->can_be_deprecated();
}
bool MapRef::CanTransition() const {
IF_ACCESS_FROM_HEAP_C(CanTransition);
return data()->AsMap()->can_transition();
}
int MapRef::GetInObjectPropertiesStartInWords() const {
IF_ACCESS_FROM_HEAP_C(GetInObjectPropertiesStartInWords);
return data()->AsMap()->in_object_properties_start_in_words();
}
int MapRef::GetInObjectProperties() const {
IF_ACCESS_FROM_HEAP_C(GetInObjectProperties);
return data()->AsMap()->in_object_properties();
}
void ScopeInfoRef::SerializeScopeInfoChain() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsScopeInfo()->SerializeScopeInfoChain(broker());
}
bool StringRef::IsExternalString() const {
IF_ACCESS_FROM_HEAP_C(IsExternalString);
return data()->AsString()->is_external_string();
}
Address CallHandlerInfoRef::callback() const {
if (data_->should_access_heap()) {
return v8::ToCData<Address>(object()->callback());
}
return HeapObjectRef::data()->AsCallHandlerInfo()->callback();
}
Address FunctionTemplateInfoRef::c_function() const {
if (data_->should_access_heap()) {
return v8::ToCData<Address>(object()->GetCFunction());
}
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_function();
}
const CFunctionInfo* FunctionTemplateInfoRef::c_signature() const {
if (data_->should_access_heap()) {
return v8::ToCData<CFunctionInfo*>(object()->GetCSignature());
}
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_signature();
}
bool StringRef::IsSeqString() const {
IF_ACCESS_FROM_HEAP_C(IsSeqString);
return data()->AsString()->is_seq_string();
}
ScopeInfoRef NativeContextRef::scope_info() const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return ScopeInfoRef(
broker(), broker()->CanonicalPersistentHandle(object()->scope_info()));
}
return ScopeInfoRef(broker(), data()->AsNativeContext()->scope_info());
}
SharedFunctionInfoRef FeedbackVectorRef::shared_function_info() const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return SharedFunctionInfoRef(
broker(),
broker()->CanonicalPersistentHandle(object()->shared_function_info()));
}
return SharedFunctionInfoRef(
broker(), data()->AsFeedbackVector()->shared_function_info());
}
MapRef NativeContextRef::GetFunctionMapFromIndex(int index) const {
DCHECK_GE(index, Context::FIRST_FUNCTION_MAP_INDEX);
DCHECK_LE(index, Context::LAST_FUNCTION_MAP_INDEX);
if (data_->should_access_heap()) {
return get(index).value().AsMap();
}
return MapRef(broker(), 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();
}
}
base::Optional<JSFunctionRef> NativeContextRef::GetConstructorFunction(
const MapRef& map) const {
CHECK(map.IsPrimitiveMap());
switch (map.constructor_function_index()) {
case Map::kNoConstructorFunctionIndex:
return base::nullopt;
case Context::BIGINT_FUNCTION_INDEX:
return bigint_function();
case Context::BOOLEAN_FUNCTION_INDEX:
return boolean_function();
case Context::NUMBER_FUNCTION_INDEX:
return number_function();
case Context::STRING_FUNCTION_INDEX:
return string_function();
case Context::SYMBOL_FUNCTION_INDEX:
return symbol_function();
default:
UNREACHABLE();
}
}
bool ObjectRef::IsNullOrUndefined() const {
if (IsSmi()) return false;
OddballType type = AsHeapObject().map().oddball_type();
return type == OddballType::kNull || type == OddballType::kUndefined;
}
bool ObjectRef::IsTheHole() const {
return IsHeapObject() &&
AsHeapObject().map().oddball_type() == OddballType::kHole;
}
bool ObjectRef::BooleanValue() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return object()->BooleanValue(broker()->isolate());
}
return IsSmi() ? (AsSmi() != 0) : data()->AsHeapObject()->boolean_value();
}
Maybe<double> ObjectRef::OddballToNumber() const {
OddballType type = AsHeapObject().map().oddball_type();
switch (type) {
case OddballType::kBoolean: {
ObjectRef true_ref(broker(),
broker()->isolate()->factory()->true_value());
return this->equals(true_ref) ? Just(1.0) : Just(0.0);
break;
}
case OddballType::kUndefined: {
return Just(std::numeric_limits<double>::quiet_NaN());
break;
}
case OddballType::kNull: {
return Just(0.0);
break;
}
default: {
return Nothing<double>();
break;
}
}
}
base::Optional<ObjectRef> ObjectRef::GetOwnConstantElement(
uint32_t index, SerializationPolicy policy) const {
if (!(IsJSObject() || IsString())) return base::nullopt;
if (data_->should_access_heap()) {
// TODO(solanes, neis, v8:7790, v8:11012): Re-enable this optmization for
// concurrent inlining when we have the infrastructure to safely do so.
if (broker()->is_concurrent_inlining() && IsString()) return base::nullopt;
CHECK_EQ(data_->kind(), ObjectDataKind::kUnserializedHeapObject);
return GetOwnElementFromHeap(broker(), object(), index, true);
}
ObjectData* element = nullptr;
if (IsJSObject()) {
element =
data()->AsJSObject()->GetOwnConstantElement(broker(), index, policy);
} else if (IsString()) {
element = data()->AsString()->GetCharAsString(broker(), index, policy);
}
if (element == nullptr) return base::nullopt;
return ObjectRef(broker(), element);
}
base::Optional<ObjectRef> JSObjectRef::GetOwnDataProperty(
Representation field_representation, FieldIndex index,
SerializationPolicy policy) const {
if (data_->should_access_heap()) {
return GetOwnDataPropertyFromHeap(broker(),
Handle<JSObject>::cast(object()),
field_representation, index);
}
ObjectData* property = data()->AsJSObject()->GetOwnDataProperty(
broker(), field_representation, index, policy);
if (property == nullptr) return base::nullopt;
return ObjectRef(broker(), property);
}
base::Optional<ObjectRef> JSArrayRef::GetOwnCowElement(
uint32_t index, SerializationPolicy policy) const {
if (data_->should_access_heap()) {
if (!object()->elements().IsCowArray()) return base::nullopt;
return GetOwnElementFromHeap(broker(), object(), index, false);
}
if (policy == SerializationPolicy::kSerializeIfNeeded) {
data()->AsJSObject()->SerializeElements(broker());
} else if (!data()->AsJSObject()->serialized_elements()) {
TRACE(broker(), "'elements' on " << this);
return base::nullopt;
}
if (!elements().map().IsFixedCowArrayMap()) return base::nullopt;
ObjectData* element =
data()->AsJSArray()->GetOwnElement(broker(), index, policy);
if (element == nullptr) return base::nullopt;
return ObjectRef(broker(), element);
}
base::Optional<CellRef> SourceTextModuleRef::GetCell(int cell_index) const {
if (data_->should_access_heap() || FLAG_turbo_direct_heap_access) {
AllowHandleAllocationIfNeeded allow_handle_allocation(
data()->kind(), broker()->mode(), FLAG_turbo_direct_heap_access);
AllowHandleDereferenceIfNeeded allow_handle_dereference(
data()->kind(), broker()->mode(), FLAG_turbo_direct_heap_access);
return CellRef(broker(), broker()->CanonicalPersistentHandle(
object()->GetCell(cell_index)));
}
ObjectData* cell =
data()->AsSourceTextModule()->GetCell(broker(), cell_index);
if (cell == nullptr) return base::nullopt;
return CellRef(broker(), cell);
}
ObjectRef SourceTextModuleRef::import_meta() const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return ObjectRef(
broker(), broker()->CanonicalPersistentHandle(object()->import_meta()));
}
return ObjectRef(broker(),
data()->AsSourceTextModule()->GetImportMeta(broker()));
}
ObjectRef::ObjectRef(JSHeapBroker* broker, Handle<Object> object,
bool check_type)
: broker_(broker) {
switch (broker->mode()) {
// We may have to create data in JSHeapBroker::kSerialized as well since we
// read the data from read only heap objects directly instead of serializing
// them.
case JSHeapBroker::kSerialized:
case JSHeapBroker::kSerializing:
data_ = broker->GetOrCreateData(object);
break;
case JSHeapBroker::kDisabled: {
RefsMap::Entry* entry = broker->refs_->LookupOrInsert(object.address());
ObjectData** storage = &(entry->value);
if (*storage == nullptr) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(
kUnserializedHeapObject, broker->mode());
entry->value = broker->zone()->New<ObjectData>(
broker, storage, object,
object->IsSmi() ? kSmi : kUnserializedHeapObject);
}
data_ = *storage;
break;
}
case JSHeapBroker::kRetired:
UNREACHABLE();
}
if (!data_) { // TODO(mslekova): Remove once we're on the background thread.
AllowHandleDereferenceIfNeeded allow_handle_dereference(data_->kind(),
broker->mode());
object->Print();
}
CHECK_WITH_MSG(data_ != nullptr, "Object is not known to the heap broker");
}
namespace {
OddballType GetOddballType(Isolate* isolate, Map map) {
if (map.instance_type() != ODDBALL_TYPE) {
return OddballType::kNone;
}
ReadOnlyRoots roots(isolate);
if (map == roots.undefined_map()) {
return OddballType::kUndefined;
}
if (map == roots.null_map()) {
return OddballType::kNull;
}
if (map == roots.boolean_map()) {
return OddballType::kBoolean;
}
if (map == roots.the_hole_map()) {
return OddballType::kHole;
}
if (map == roots.uninitialized_map()) {
return OddballType::kUninitialized;
}
DCHECK(map == roots.termination_exception_map() ||
map == roots.arguments_marker_map() ||
map == roots.optimized_out_map() || map == roots.stale_register_map());
return OddballType::kOther;
}
} // namespace
HeapObjectType HeapObjectRef::GetHeapObjectType() const {
if (data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
Map map = Handle<HeapObject>::cast(object())->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,
GetOddballType(broker()->isolate(), 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, map().oddball_type());
}
base::Optional<JSObjectRef> AllocationSiteRef::boilerplate() const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return JSObjectRef(
broker(), broker()->CanonicalPersistentHandle(object()->boilerplate()));
}
ObjectData* boilerplate = data()->AsAllocationSite()->boilerplate();
if (boilerplate) {
return JSObjectRef(broker(), boilerplate);
} else {
return base::nullopt;
}
}
ElementsKind JSObjectRef::GetElementsKind() const {
return map().elements_kind();
}
FixedArrayBaseRef JSObjectRef::elements() const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return FixedArrayBaseRef(
broker(), broker()->CanonicalPersistentHandle(object()->elements()));
}
return FixedArrayBaseRef(broker(), data()->AsJSObject()->elements());
}
int FixedArrayBaseRef::length() const {
IF_ACCESS_FROM_HEAP_C(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];
}
base::Optional<SharedFunctionInfoRef> FeedbackCellRef::shared_function_info()
const {
if (value().IsFeedbackVector()) {
FeedbackVectorRef vector = value().AsFeedbackVector();
if (vector.serialized()) {
return value().AsFeedbackVector().shared_function_info();
}
}
return base::nullopt;
}
void FeedbackVectorRef::Serialize() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsFeedbackVector()->Serialize(broker());
}
bool FeedbackVectorRef::serialized() const {
if (data_->should_access_heap()) return true;
return data()->AsFeedbackVector()->serialized();
}
bool NameRef::IsUniqueName() const {
// Must match Name::IsUniqueName.
return IsInternalizedString() || IsSymbol();
}
ObjectRef JSRegExpRef::data() const {
IF_ACCESS_FROM_HEAP(Object, data);
return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->data());
}
ObjectRef JSRegExpRef::flags() const {
IF_ACCESS_FROM_HEAP(Object, flags);
return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->flags());
}
ObjectRef JSRegExpRef::last_index() const {
IF_ACCESS_FROM_HEAP(Object, last_index);
return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->last_index());
}
ObjectRef JSRegExpRef::raw_properties_or_hash() const {
IF_ACCESS_FROM_HEAP(Object, raw_properties_or_hash);
return ObjectRef(broker(),
ObjectRef::data()->AsJSRegExp()->raw_properties_or_hash());
}
ObjectRef JSRegExpRef::source() const {
IF_ACCESS_FROM_HEAP(Object, source);
return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->source());
}
void JSRegExpRef::SerializeAsRegExpBoilerplate() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
JSObjectRef::data()->AsJSRegExp()->SerializeAsRegExpBoilerplate(broker());
}
Handle<Object> ObjectRef::object() const {
#ifdef DEBUG
if (broker()->mode() == JSHeapBroker::kSerialized &&
data_->used_status == ObjectData::Usage::kUnused) {
data_->used_status = ObjectData::Usage::kOnlyIdentityUsed;
}
#endif // DEBUG
return data_->object();
}
#ifdef DEBUG
#define DEF_OBJECT_GETTER(T) \
Handle<T> T##Ref::object() const { \
if (broker()->mode() == JSHeapBroker::kSerialized && \
data_->used_status == ObjectData::Usage::kUnused) { \
data_->used_status = ObjectData::Usage::kOnlyIdentityUsed; \
} \
return Handle<T>(reinterpret_cast<Address*>(data_->object().address())); \
}
#else
#define DEF_OBJECT_GETTER(T) \
Handle<T> T##Ref::object() const { \
return Handle<T>(reinterpret_cast<Address*>(data_->object().address())); \
}
#endif // DEBUG
HEAP_BROKER_SERIALIZED_OBJECT_LIST(DEF_OBJECT_GETTER)
HEAP_BROKER_NEVER_SERIALIZED_OBJECT_LIST(DEF_OBJECT_GETTER)
#undef DEF_OBJECT_GETTER
JSHeapBroker* ObjectRef::broker() const { return broker_; }
ObjectData* ObjectRef::data() const {
switch (broker()->mode()) {
case JSHeapBroker::kDisabled:
CHECK_NE(data_->kind(), kSerializedHeapObject);
return data_;
case JSHeapBroker::kSerializing:
CHECK_NE(data_->kind(), kUnserializedHeapObject);
return data_;
case JSHeapBroker::kSerialized:
#ifdef DEBUG
data_->used_status = ObjectData::Usage::kDataUsed;
#endif // DEBUG
CHECK_NE(data_->kind(), kUnserializedHeapObject);
return data_;
case JSHeapBroker::kRetired:
UNREACHABLE();
}
}
Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker,
const char* function, int line) {
TRACE_MISSING(broker, "data in function " << function << " at line " << line);
return AdvancedReducer::NoChange();
}
NativeContextData::NativeContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<NativeContext> object)
: ContextData(broker, storage, object), function_maps_(broker->zone()) {}
void NativeContextData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
TraceScope tracer(broker, this, "NativeContextData::Serialize");
Handle<NativeContext> context = Handle<NativeContext>::cast(object());
#define SERIALIZE_MEMBER(type, name) \
DCHECK_NULL(name##_); \
name##_ = broker->GetOrCreateData(context->name()); \
if (!name##_->should_access_heap()) { \
if (name##_->IsJSFunction()) name##_->AsJSFunction()->Serialize(broker); \
if (name##_->IsMap() && \
!InstanceTypeChecker::IsContext(name##_->AsMap()->instance_type())) { \
name##_->AsMap()->SerializeConstructor(broker); \
} \
}
BROKER_COMPULSORY_NATIVE_CONTEXT_FIELDS(SERIALIZE_MEMBER)
if (!broker->isolate()->bootstrapper()->IsActive()) {
BROKER_OPTIONAL_NATIVE_CONTEXT_FIELDS(SERIALIZE_MEMBER)
}
#undef SERIALIZE_MEMBER
if (!bound_function_with_constructor_map_->should_access_heap()) {
bound_function_with_constructor_map_->AsMap()->SerializePrototype(broker);
}
if (!bound_function_without_constructor_map_->should_access_heap()) {
bound_function_without_constructor_map_->AsMap()->SerializePrototype(
broker);
}
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)));
}
scope_info_ = broker->GetOrCreateData(context->scope_info());
}
void JSFunctionRef::Serialize() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsJSFunction()->Serialize(broker());
}
bool JSBoundFunctionRef::serialized() const {
if (data_->should_access_heap()) return true;
return data()->AsJSBoundFunction()->serialized();
}
bool JSFunctionRef::serialized() const {
if (data_->should_access_heap()) return true;
return data()->AsJSFunction()->serialized();
}
JSArrayRef SharedFunctionInfoRef::GetTemplateObject(
TemplateObjectDescriptionRef description, FeedbackSource const& source,
SerializationPolicy policy) {
// First, see if we have processed feedback from the vector, respecting
// the serialization policy.
ProcessedFeedback const& feedback =
policy == SerializationPolicy::kSerializeIfNeeded
? broker()->ProcessFeedbackForTemplateObject(source)
: broker()->GetFeedbackForTemplateObject(source);
if (!feedback.IsInsufficient()) {
return feedback.AsTemplateObject().value();
}
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
Handle<JSArray> template_object =
TemplateObjectDescription::GetTemplateObject(
isolate(), broker()->target_native_context().object(),
description.object(), object(), source.slot.ToInt());
return JSArrayRef(broker(), template_object);
}
ObjectData* array =
data()->AsSharedFunctionInfo()->GetTemplateObject(source.slot);
if (array != nullptr) return JSArrayRef(broker(), array);
CHECK_EQ(policy, SerializationPolicy::kSerializeIfNeeded);
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
Handle<JSArray> template_object =
TemplateObjectDescription::GetTemplateObject(
broker()->isolate(), broker()->target_native_context().object(),
description.object(), object(), source.slot.ToInt());
array = broker()->GetOrCreateData(template_object);
data()->AsSharedFunctionInfo()->SetTemplateObject(source.slot, array);
return JSArrayRef(broker(), array);
}
void SharedFunctionInfoRef::SerializeFunctionTemplateInfo() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsSharedFunctionInfo()->SerializeFunctionTemplateInfo(broker());
}
void SharedFunctionInfoRef::SerializeScopeInfoChain() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsSharedFunctionInfo()->SerializeScopeInfoChain(broker());
}
base::Optional<FunctionTemplateInfoRef>
SharedFunctionInfoRef::function_template_info() const {
if (data_->should_access_heap()) {
if (object()->IsApiFunction()) {
return FunctionTemplateInfoRef(
broker(), broker()->CanonicalPersistentHandle(
object()->function_data(kAcquireLoad)));
}
return base::nullopt;
}
ObjectData* function_template_info =
data()->AsSharedFunctionInfo()->function_template_info();
if (!function_template_info) return base::nullopt;
return FunctionTemplateInfoRef(broker(), function_template_info);
}
int SharedFunctionInfoRef::context_header_size() const {
IF_ACCESS_FROM_HEAP_C(scope_info().ContextHeaderLength);
return data()->AsSharedFunctionInfo()->context_header_size();
}
ScopeInfoRef SharedFunctionInfoRef::scope_info() const {
if (data_->should_access_heap()) {
AllowHandleAllocationIfNeeded allow_handle_allocation(data()->kind(),
broker()->mode());
AllowHandleDereferenceIfNeeded allow_handle_dereference(data()->kind(),
broker()->mode());
return ScopeInfoRef(
broker(), broker()->CanonicalPersistentHandle(object()->scope_info()));
}
return ScopeInfoRef(broker(), data()->AsSharedFunctionInfo()->scope_info());
}
void JSObjectRef::SerializeObjectCreateMap() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsJSObject()->SerializeObjectCreateMap(broker());
}
void MapRef::SerializeOwnDescriptors() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeOwnDescriptors(broker());
}
void MapRef::SerializeOwnDescriptor(InternalIndex descriptor_index) {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeOwnDescriptor(broker(), descriptor_index);
}
bool MapRef::serialized_own_descriptor(InternalIndex descriptor_index) const {
CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
if (data_->should_access_heap()) return true;
DescriptorArrayData* desc_array_data =
data()->AsMap()->instance_descriptors();
if (!desc_array_data) return false;
return desc_array_data->contents().find(descriptor_index.as_int()) !=
desc_array_data->contents().end();
}
void MapRef::SerializeBackPointer() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializeBackPointer(broker());
}
void MapRef::SerializePrototype() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsMap()->SerializePrototype(broker());
}
bool MapRef::serialized_prototype() const {
CHECK_NE(broker()->mode(), JSHeapBroker::kDisabled);
if (data_->should_access_heap()) return true;
return data()->AsMap()->serialized_prototype();
}
void SourceTextModuleRef::Serialize() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsSourceTextModule()->Serialize(broker());
}
void NativeContextRef::Serialize() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsNativeContext()->Serialize(broker());
}
void JSTypedArrayRef::Serialize() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsJSTypedArray()->Serialize(broker());
}
bool JSTypedArrayRef::serialized() const {
CHECK_NE(broker()->mode(), JSHeapBroker::kDisabled);
return data()->AsJSTypedArray()->serialized();
}
bool JSBoundFunctionRef::Serialize() {
if (data_->should_access_heap()) return true;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
return data()->AsJSBoundFunction()->Serialize(broker());
}
void PropertyCellRef::Serialize() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsPropertyCell()->Serialize(broker());
}
void FunctionTemplateInfoRef::SerializeCallCode() {
if (data_->should_access_heap()) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsFunctionTemplateInfo()->SerializeCallCode(broker());
}
base::Optional<PropertyCellRef> JSGlobalObjectRef::GetPropertyCell(
NameRef const& name, SerializationPolicy policy) const {
if (data_->should_access_heap()) {
return GetPropertyCellFromHeap(broker(), name.object());
}
ObjectData* property_cell_data = data()->AsJSGlobalObject()->GetPropertyCell(
broker(), name.data(), policy);
if (property_cell_data == nullptr) return base::nullopt;
return PropertyCellRef(broker(), property_cell_data);
}
bool CanInlineElementAccess(MapRef const& map) {
if (!map.IsJSObjectMap()) return false;
if (map.is_access_check_needed()) return false;
if (map.has_indexed_interceptor()) return false;
ElementsKind const elements_kind = map.elements_kind();
if (IsFastElementsKind(elements_kind)) return true;
if (IsTypedArrayElementsKind(elements_kind) &&
elements_kind != BIGUINT64_ELEMENTS &&
elements_kind != BIGINT64_ELEMENTS) {
return true;
}
return false;
}
ProcessedFeedback::ProcessedFeedback(Kind kind, FeedbackSlotKind slot_kind)
: kind_(kind), slot_kind_(slot_kind) {}
KeyedAccessMode ElementAccessFeedback::keyed_mode() const {
return keyed_mode_;
}
ZoneVector<ElementAccessFeedback::TransitionGroup> const&
ElementAccessFeedback::transition_groups() const {
return transition_groups_;
}
ElementAccessFeedback const& ElementAccessFeedback::Refine(
ZoneVector<Handle<Map>> const& inferred_maps, Zone* zone) const {
ElementAccessFeedback& refined_feedback =
*zone->New<ElementAccessFeedback>(zone, keyed_mode(), slot_kind());
if (inferred_maps.empty()) return refined_feedback;
ZoneUnorderedSet<Handle<Map>, Handle<Map>::hash, Handle<Map>::equal_to>
inferred(zone);
inferred.insert(inferred_maps.begin(), inferred_maps.end());
for (auto const& group : transition_groups()) {
DCHECK(!group.empty());
TransitionGroup new_group(zone);
for (size_t i = 1; i < group.size(); ++i) {
Handle<Map> source = group[i];
if (inferred.find(source) != inferred.end()) {
new_group.push_back(source);
}
}
Handle<Map> target = group.front();
bool const keep_target =
inferred.find(target) != inferred.end() || new_group.size() > 1;
if (keep_target) {
new_group.push_back(target);
// The target must be at the front, the order of sources doesn't matter.
std::swap(new_group[0], new_group[new_group.size() - 1]);
}
if (!new_group.empty()) {
DCHECK(new_group.size() == 1 || new_group.front().equals(target));
refined_feedback.transition_groups_.push_back(std::move(new_group));
}
}
return refined_feedback;
}
InsufficientFeedback::InsufficientFeedback(FeedbackSlotKind slot_kind)
: ProcessedFeedback(kInsufficient, slot_kind) {}
GlobalAccessFeedback::GlobalAccessFeedback(PropertyCellRef cell,
FeedbackSlotKind slot_kind)
: ProcessedFeedback(kGlobalAccess, slot_kind),
cell_or_context_(cell),
index_and_immutable_(0 /* doesn't matter */) {
DCHECK(IsGlobalICKind(slot_kind));
}
GlobalAccessFeedback::GlobalAccessFeedback(FeedbackSlotKind slot_kind)
: ProcessedFeedback(kGlobalAccess, slot_kind),
cell_or_context_(),
index_and_immutable_(0 /* doesn't matter */) {
DCHECK(IsGlobalICKind(slot_kind));
}
GlobalAccessFeedback::GlobalAccessFeedback(ContextRef script_context,
int slot_index, bool immutable,
FeedbackSlotKind slot_kind)
: ProcessedFeedback(kGlobalAccess, slot_kind),
cell_or_context_(script_context),
index_and_immutable_(FeedbackNexus::SlotIndexBits::encode(slot_index) |
FeedbackNexus::ImmutabilityBit::encode(immutable)) {
DCHECK_EQ(this->slot_index(), slot_index);
DCHECK_EQ(this->immutable(), immutable);
DCHECK(IsGlobalICKind(slot_kind));
}
bool GlobalAccessFeedback::IsMegamorphic() const {
return !cell_or_context_.has_value();
}
bool GlobalAccessFeedback::IsPropertyCell() const {
return cell_or_context_.has_value() && cell_or_context_->IsPropertyCell();
}
bool GlobalAccessFeedback::IsScriptContextSlot() const {
return cell_or_context_.has_value() && cell_or_context_->IsContext();
}
PropertyCellRef GlobalAccessFeedback::property_cell() const {
CHECK(IsPropertyCell());
return cell_or_context_->AsPropertyCell();
}
ContextRef GlobalAccessFeedback::script_context() const {
CHECK(IsScriptContextSlot());
return cell_or_context_->AsContext();
}
int GlobalAccessFeedback::slot_index() const {
DCHECK(IsScriptContextSlot());
return FeedbackNexus::SlotIndexBits::decode(index_and_immutable_);
}
bool GlobalAccessFeedback::immutable() const {
DCHECK(IsScriptContextSlot());
return FeedbackNexus::ImmutabilityBit::decode(index_and_immutable_);
}
base::Optional<ObjectRef> GlobalAccessFeedback::GetConstantHint() const {
if (IsPropertyCell()) {
return property_cell().value();
} else if (IsScriptContextSlot() && immutable()) {
return script_context().get(slot_index());
} else {
return base::nullopt;
}
}
KeyedAccessMode KeyedAccessMode::FromNexus(FeedbackNexus const& nexus) {
FeedbackSlotKind kind = nexus.kind();
if (IsKeyedLoadICKind(kind)) {
return KeyedAccessMode(AccessMode::kLoad, nexus.GetKeyedAccessLoadMode());
}
if (IsKeyedHasICKind(kind)) {
return KeyedAccessMode(AccessMode::kHas, nexus.GetKeyedAccessLoadMode());
}
if (IsKeyedStoreICKind(kind)) {
return KeyedAccessMode(AccessMode::kStore, nexus.GetKeyedAccessStoreMode());
}
if (IsStoreInArrayLiteralICKind(kind) ||
IsStoreDataPropertyInLiteralKind(kind)) {
return KeyedAccessMode(AccessMode::kStoreInLiteral,
nexus.GetKeyedAccessStoreMode());
}
UNREACHABLE();
}
AccessMode KeyedAccessMode::access_mode() const { return access_mode_; }
bool KeyedAccessMode::IsLoad() const {
return access_mode_ == AccessMode::kLoad || access_mode_ == AccessMode::kHas;
}
bool KeyedAccessMode::IsStore() const {
return access_mode_ == AccessMode::kStore ||
access_mode_ == AccessMode::kStoreInLiteral;
}
KeyedAccessLoadMode KeyedAccessMode::load_mode() const {
CHECK(IsLoad());
return load_store_mode_.load_mode;
}
KeyedAccessStoreMode KeyedAccessMode::store_mode() const {
CHECK(IsStore());
return load_store_mode_.store_mode;
}
KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessLoadMode load_mode)
: load_mode(load_mode) {}
KeyedAccessMode::LoadStoreMode::LoadStoreMode(KeyedAccessStoreMode store_mode)
: store_mode(store_mode) {}
KeyedAccessMode::KeyedAccessMode(AccessMode access_mode,
KeyedAccessLoadMode load_mode)
: access_mode_(access_mode), load_store_mode_(load_mode) {
CHECK(!IsStore());
CHECK(IsLoad());
}
KeyedAccessMode::KeyedAccessMode(AccessMode access_mode,
KeyedAccessStoreMode store_mode)
: access_mode_(access_mode), load_store_mode_(store_mode) {
CHECK(!IsLoad());
CHECK(IsStore());
}
ElementAccessFeedback::ElementAccessFeedback(Zone* zone,
KeyedAccessMode const& keyed_mode,
FeedbackSlotKind slot_kind)
: ProcessedFeedback(kElementAccess, slot_kind),
keyed_mode_(keyed_mode),
transition_groups_(zone) {
DCHECK(IsKeyedLoadICKind(slot_kind) || IsKeyedHasICKind(slot_kind) ||
IsStoreDataPropertyInLiteralKind(slot_kind) ||
IsKeyedStoreICKind(slot_kind) ||
IsStoreInArrayLiteralICKind(slot_kind));
}
bool ElementAccessFeedback::HasOnlyStringMaps(JSHeapBroker* broker) const {
for (auto const& group : transition_groups()) {
for (Handle<Map> map : group) {
if (!MapRef(broker, map).IsStringMap()) return false;
}
}
return true;
}
MinimorphicLoadPropertyAccessFeedback::MinimorphicLoadPropertyAccessFeedback(
NameRef const& name, FeedbackSlotKind slot_kind, Handle<Object> handler,
ZoneVector<Handle<Map>> const& maps, bool has_migration_target_maps)
: ProcessedFeedback(kMinimorphicPropertyAccess, slot_kind),
name_(name),
handler_(handler),
maps_(maps),
has_migration_target_maps_(has_migration_target_maps) {
DCHECK(IsLoadICKind(slot_kind));
}
NamedAccessFeedback::NamedAccessFeedback(NameRef const& name,
ZoneVector<Handle<Map>> const& maps,
FeedbackSlotKind slot_kind)
: ProcessedFeedback(kNamedAccess, slot_kind), name_(name), maps_(maps) {
DCHECK(IsLoadICKind(slot_kind) || IsStoreICKind(slot_kind) ||
IsStoreOwnICKind(slot_kind) || IsKeyedLoadICKind(slot_kind) ||
IsKeyedHasICKind(slot_kind) || IsKeyedStoreICKind(slot_kind) ||
IsStoreInArrayLiteralICKind(slot_kind) ||
IsStoreDataPropertyInLiteralKind(slot_kind));
}
void JSHeapBroker::SetFeedback(FeedbackSource const& source,
ProcessedFeedback const* feedback) {
CHECK(source.IsValid());
auto insertion = feedback_.insert({source, feedback});
CHECK(insertion.second);
}
bool JSHeapBroker::HasFeedback(FeedbackSource const& source) const {
DCHECK(source.IsValid());
return feedback_.find(source) != feedback_.end();
}
ProcessedFeedback const& JSHeapBroker::GetFeedback(
FeedbackSource const& source) const {
DCHECK(source.IsValid());
auto it = feedback_.find(source);
CHECK_NE(it, feedback_.end());
return *it->second;
}
FeedbackSlotKind JSHeapBroker::GetFeedbackSlotKind(
FeedbackSource const& source) const {
if (is_concurrent_inlining_) {
ProcessedFeedback const& processed = GetFeedback(source);
return processed.slot_kind();
}
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
return nexus.kind();
}
bool JSHeapBroker::FeedbackIsInsufficient(FeedbackSource const& source) const {
return is_concurrent_inlining_ ? GetFeedback(source).IsInsufficient()
: FeedbackNexus(source.vector, source.slot,
feedback_nexus_config())
.IsUninitialized();
}
namespace {
// Remove unupdatable and abandoned prototype maps in-place.
void FilterRelevantReceiverMaps(Isolate* isolate, MapHandles* maps) {
auto in = maps->begin();
auto out = in;
auto end = maps->end();
for (; in != end; ++in) {
Handle<Map> map = *in;
if (Map::TryUpdate(isolate, map).ToHandle(&map) &&
!map->is_abandoned_prototype_map()) {
DCHECK(!map->is_deprecated());
*out = *in;
++out;
}
}
// Remove everything between the last valid map and the end of the vector.
maps->erase(out, end);
}
MaybeObjectHandle TryGetMinimorphicHandler(
std::vector<MapAndHandler> const& maps_and_handlers, FeedbackSlotKind kind,
Handle<NativeContext> native_context, bool is_turboprop) {
if (!is_turboprop || !FLAG_turboprop_dynamic_map_checks ||
!IsLoadICKind(kind)) {
return MaybeObjectHandle();
}
// Don't use dynamic map checks when loading properties from Array.prototype.
// Using dynamic map checks prevents constant folding and hence does not
// inline the array builtins. We only care about monomorphic cases here. For
// polymorphic loads currently we don't inline the builtins even without
// dynamic map checks.
if (maps_and_handlers.size() == 1 &&
*maps_and_handlers[0].first ==
native_context->initial_array_prototype().map()) {
return MaybeObjectHandle();
}
MaybeObjectHandle initial_handler;
for (MapAndHandler map_and_handler : maps_and_handlers) {
auto map = map_and_handler.first;
MaybeObjectHandle handler = map_and_handler.second;
if (handler.is_null()) return MaybeObjectHandle();
DCHECK(!handler->IsCleared());
// TODO(mythria): extend this to DataHandlers too
if (!handler.object()->IsSmi()) return MaybeObjectHandle();
if (LoadHandler::GetHandlerKind(handler.object()->ToSmi()) !=
LoadHandler::Kind::kField) {
return MaybeObjectHandle();
}
CHECK(!map->IsJSGlobalProxyMap());
if (initial_handler.is_null()) {
initial_handler = handler;
} else if (!handler.is_identical_to(initial_handler)) {
return MaybeObjectHandle();
}
}
return initial_handler;
}
bool HasMigrationTargets(const MapHandles& maps) {
for (Handle<Map> map : maps) {
if (map->is_migration_target()) return true;
}
return false;
}
} // namespace
bool JSHeapBroker::CanUseFeedback(const FeedbackNexus& nexus) const {
// TODO(jgruber,v8:8888): Currently, nci code does not use any
// feedback. This restriction will be relaxed in the future.
return !is_native_context_independent() && !nexus.IsUninitialized();
}
const ProcessedFeedback& JSHeapBroker::NewInsufficientFeedback(
FeedbackSlotKind kind) const {
return *zone()->New<InsufficientFeedback>(kind);
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess(
FeedbackSource const& source, AccessMode mode,
base::Optional<NameRef> static_name) {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
FeedbackSlotKind kind = nexus.kind();
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(kind);
std::vector<MapAndHandler> maps_and_handlers;
nexus.ExtractMapsAndFeedback(&maps_and_handlers);
MapHandles maps;
for (auto const& entry : maps_and_handlers) {
maps.push_back(entry.first);
}
base::Optional<NameRef> name =
static_name.has_value() ? static_name : GetNameFeedback(nexus);
MaybeObjectHandle handler = TryGetMinimorphicHandler(
maps_and_handlers, kind, target_native_context().object(),
is_turboprop());
if (!handler.is_null()) {
return *zone()->New<MinimorphicLoadPropertyAccessFeedback>(
*name, kind, handler.object(),
ZoneVector<Handle<Map>>(maps.begin(), maps.end(), zone()),
HasMigrationTargets(maps));
}
FilterRelevantReceiverMaps(isolate(), &maps);
// If no maps were found for a non-megamorphic access, then our maps died
// and we should soft-deopt.
if (maps.empty() && nexus.ic_state() != MEGAMORPHIC) {
return NewInsufficientFeedback(kind);
}
if (name.has_value()) {
// We rely on this invariant in JSGenericLowering.
DCHECK_IMPLIES(maps.empty(), nexus.ic_state() == MEGAMORPHIC);
return *zone()->New<NamedAccessFeedback>(
*name, ZoneVector<Handle<Map>>(maps.begin(), maps.end(), zone()), kind);
} else if (nexus.GetKeyType() == ELEMENT && !maps.empty()) {
return ProcessFeedbackMapsForElementAccess(
maps, KeyedAccessMode::FromNexus(nexus), kind);
} else {
// No actionable feedback.
DCHECK(maps.empty());
DCHECK_EQ(nexus.ic_state(), MEGAMORPHIC);
// TODO(neis): Using ElementAccessFeedback here is kind of an abuse.
return *zone()->New<ElementAccessFeedback>(
zone(), KeyedAccessMode::FromNexus(nexus), kind);
}
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForGlobalAccess(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot);
DCHECK(nexus.kind() == FeedbackSlotKind::kLoadGlobalInsideTypeof ||
nexus.kind() == FeedbackSlotKind::kLoadGlobalNotInsideTypeof ||
nexus.kind() == FeedbackSlotKind::kStoreGlobalSloppy ||
nexus.kind() == FeedbackSlotKind::kStoreGlobalStrict);
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
if (nexus.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) {
return *zone()->New<GlobalAccessFeedback>(nexus.kind());
}
Handle<Object> feedback_value(nexus.GetFeedback()->GetHeapObjectOrSmi(),
isolate());
if (feedback_value->IsSmi()) {
// The wanted name belongs to a script-scope variable and the feedback
// tells us where to find its value.
int number = feedback_value->Number();
int const script_context_index =
FeedbackNexus::ContextIndexBits::decode(number);
int const context_slot_index = FeedbackNexus::SlotIndexBits::decode(number);
bool const immutable = FeedbackNexus::ImmutabilityBit::decode(number);
Handle<Context> context = ScriptContextTable::GetContext(
isolate(), target_native_context().script_context_table().object(),
script_context_index);
{
ObjectRef contents(this,
handle(context->get(context_slot_index), isolate()));
CHECK(!contents.equals(
ObjectRef(this, isolate()->factory()->the_hole_value())));
}
ContextRef context_ref(this, context);
if (immutable) {
context_ref.get(context_slot_index,
SerializationPolicy::kSerializeIfNeeded);
}
return *zone()->New<GlobalAccessFeedback>(context_ref, context_slot_index,
immutable, nexus.kind());
}
CHECK(feedback_value->IsPropertyCell());
// The wanted name belongs (or did belong) to a property on the global
// object and the feedback is the cell holding its value.
PropertyCellRef cell(this, Handle<PropertyCell>::cast(feedback_value));
cell.Serialize();
return *zone()->New<GlobalAccessFeedback>(cell, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForBinaryOperation(
FeedbackSource const& source) const {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
BinaryOperationHint hint = nexus.GetBinaryOperationFeedback();
DCHECK_NE(hint, BinaryOperationHint::kNone); // Not uninitialized.
return *zone()->New<BinaryOperationFeedback>(hint, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCompareOperation(
FeedbackSource const& source) const {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
CompareOperationHint hint = nexus.GetCompareOperationFeedback();
DCHECK_NE(hint, CompareOperationHint::kNone); // Not uninitialized.
return *zone()->New<CompareOperationFeedback>(hint, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForForIn(
FeedbackSource const& source) const {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
ForInHint hint = nexus.GetForInFeedback();
DCHECK_NE(hint, ForInHint::kNone); // Not uninitialized.
return *zone()->New<ForInFeedback>(hint, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForInstanceOf(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
base::Optional<JSObjectRef> optional_constructor;
{
MaybeHandle<JSObject> maybe_constructor = nexus.GetConstructorFeedback();
Handle<JSObject> constructor;
if (maybe_constructor.ToHandle(&constructor)) {
optional_constructor = JSObjectRef(this, constructor);
}
}
return *zone()->New<InstanceOfFeedback>(optional_constructor, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForArrayOrObjectLiteral(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
HeapObject object;
if (!nexus.GetFeedback()->GetHeapObject(&object)) {
return NewInsufficientFeedback(nexus.kind());
}
AllocationSiteRef site(this, handle(object, isolate()));
if (site.IsFastLiteral()) {
site.SerializeBoilerplate();
}
return *zone()->New<LiteralFeedback>(site, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForRegExpLiteral(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
HeapObject object;
if (!nexus.GetFeedback()->GetHeapObject(&object)) {
return NewInsufficientFeedback(nexus.kind());
}
JSRegExpRef regexp(this, handle(object, isolate()));
regexp.SerializeAsRegExpBoilerplate();
return *zone()->New<RegExpLiteralFeedback>(regexp, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForTemplateObject(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
HeapObject object;
if (!nexus.GetFeedback()->GetHeapObject(&object)) {
return NewInsufficientFeedback(nexus.kind());
}
JSArrayRef array(this, handle(object, isolate()));
return *zone()->New<TemplateObjectFeedback>(array, nexus.kind());
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCall(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot, feedback_nexus_config());
if (!CanUseFeedback(nexus)) return NewInsufficientFeedback(nexus.kind());
base::Optional<HeapObjectRef> target_ref;
{
MaybeObject maybe_target = nexus.GetFeedback();
HeapObject target_object;
if (maybe_target->GetHeapObject(&target_object)) {
target_ref = HeapObjectRef(this, handle(target_object, isolate()));
}
}
float frequency = nexus.ComputeCallFrequency();
SpeculationMode mode = nexus.GetSpeculationMode();
return *zone()->New<CallFeedback>(target_ref, frequency, mode, nexus.kind());
}
BinaryOperationHint JSHeapBroker::GetFeedbackForBinaryOperation(
FeedbackSource const& source) {
ProcessedFeedback const& feedback =
is_concurrent_inlining_ ? GetFeedback(source)
: ProcessFeedbackForBinaryOperation(source);
return feedback.IsInsufficient() ? BinaryOperationHint::kNone
: feedback.AsBinaryOperation().value();
}
CompareOperationHint JSHeapBroker::GetFeedbackForCompareOperation(
FeedbackSource const& source) {
ProcessedFeedback const& feedback =
is_concurrent_inlining_ ? GetFeedback(source)
: ProcessFeedbackForCompareOperation(source);
return feedback.IsInsufficient() ? CompareOperationHint::kNone
: feedback.AsCompareOperation().value();
}
ForInHint JSHeapBroker::GetFeedbackForForIn(FeedbackSource const& source) {
ProcessedFeedback const& feedback = is_concurrent_inlining_
? GetFeedback(source)
: ProcessFeedbackForForIn(source);
return feedback.IsInsufficient() ? ForInHint::kNone
: feedback.AsForIn().value();
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForPropertyAccess(
FeedbackSource const& source, AccessMode mode,
base::Optional<NameRef> static_name) {
return is_concurrent_inlining_
? GetFeedback(source)
: ProcessFeedbackForPropertyAccess(source, mode, static_name);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForInstanceOf(
FeedbackSource const& source) {
return is_concurrent_inlining_ ? GetFeedback(source)
: ProcessFeedbackForInstanceOf(source);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForCall(
FeedbackSource const& source) {
return is_concurrent_inlining_ ? GetFeedback(source)
: ProcessFeedbackForCall(source);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForGlobalAccess(
FeedbackSource const& source) {
return is_concurrent_inlining_ ? GetFeedback(source)
: ProcessFeedbackForGlobalAccess(source);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForArrayOrObjectLiteral(
FeedbackSource const& source) {
return is_concurrent_inlining_
? GetFeedback(source)
: ProcessFeedbackForArrayOrObjectLiteral(source);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForRegExpLiteral(
FeedbackSource const& source) {
return is_concurrent_inlining_ ? GetFeedback(source)
: ProcessFeedbackForRegExpLiteral(source);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForTemplateObject(
FeedbackSource const& source) {
return is_concurrent_inlining_ ? GetFeedback(source)
: ProcessFeedbackForTemplateObject(source);
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForArrayOrObjectLiteral(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback =
ReadFeedbackForArrayOrObjectLiteral(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForRegExpLiteral(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForRegExpLiteral(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForTemplateObject(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForTemplateObject(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForBinaryOperation(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForBinaryOperation(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForCompareOperation(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForCompareOperation(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForForIn(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForForIn(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForPropertyAccess(
FeedbackSource const& source, AccessMode mode,
base::Optional<NameRef> static_name) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback =
ReadFeedbackForPropertyAccess(source, mode, static_name);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForInstanceOf(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForInstanceOf(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForCall(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForCall(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForGlobalAccess(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForGlobalAccess(source);
SetFeedback(source, &feedback);
return feedback;
}
ElementAccessFeedback const& JSHeapBroker::ProcessFeedbackMapsForElementAccess(
MapHandles const& maps, KeyedAccessMode const& keyed_mode,
FeedbackSlotKind slot_kind) {
DCHECK(!maps.empty());
// Collect possible transition targets.
MapHandles possible_transition_targets;
possible_transition_targets.reserve(maps.size());
for (Handle<Map> map : maps) {
MapRef map_ref(this, map);
map_ref.SerializeRootMap();
if (CanInlineElementAccess(map_ref) &&
IsFastElementsKind(map->elements_kind()) &&
GetInitialFastElementsKind() != map->elements_kind()) {
possible_transition_targets.push_back(map);
}
}
using TransitionGroup = ElementAccessFeedback::TransitionGroup;
ZoneUnorderedMap<Handle<Map>, TransitionGroup, Handle<Map>::hash,
Handle<Map>::equal_to>
transition_groups(zone());
// Separate the actual receiver maps and the possible transition sources.
for (Handle<Map> map : maps) {
// Don't generate elements kind transitions from stable maps.
Map transition_target = map->is_stable()
? Map()
: map->FindElementsKindTransitionedMap(
isolate(), possible_transition_targets);
if (transition_target.is_null()) {
TransitionGroup group(1, map, zone());
transition_groups.insert({map, group});
} else {
Handle<Map> target(transition_target, isolate());
TransitionGroup new_group(1, target, zone());
TransitionGroup& actual_group =
transition_groups.insert({target, new_group}).first->second;
actual_group.push_back(map);
}
}
ElementAccessFeedback* result =
zone()->New<ElementAccessFeedback>(zone(), keyed_mode, slot_kind);
for (auto entry : transition_groups) {
result->AddGroup(std::move(entry.second));
}
CHECK(!result->transition_groups().empty());
return *result;
}
void ElementAccessFeedback::AddGroup(TransitionGroup&& group) {
CHECK(!group.empty());
transition_groups_.push_back(std::move(group));
#ifdef ENABLE_SLOW_DCHECKS
// Check that each of the group's maps occurs exactly once in the whole
// feedback. This implies that "a source is not a target".
for (Handle<Map> map : group) {
int count = 0;
for (TransitionGroup const& some_group : transition_groups()) {
count += std::count_if(
some_group.begin(), some_group.end(),
[&](Handle<Map> some_map) { return some_map.equals(map); });
}
CHECK_EQ(count, 1);
}
#endif
}
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref) {
if (!FLAG_concurrent_recompilation) {
// We cannot be in a background thread so it's safe to read the heap.
AllowHandleDereference allow_handle_dereference;
return os << ref.data() << " {" << ref.object() << "}";
} else if (ref.data_->should_access_heap()) {
AllowHandleDereferenceIfNeeded allow_handle_dereference(
ref.data()->kind(), ref.broker()->mode());
return os << ref.data() << " {" << ref.object() << "}";
} else {
return os << ref.data();
}
}
base::Optional<NameRef> JSHeapBroker::GetNameFeedback(
FeedbackNexus const& nexus) {
Name raw_name = nexus.GetName();
if (raw_name.is_null()) return base::nullopt;
return NameRef(this, handle(raw_name, isolate()));
}
PropertyAccessInfo JSHeapBroker::GetPropertyAccessInfo(
MapRef map, NameRef name, AccessMode access_mode,
CompilationDependencies* dependencies, SerializationPolicy policy) {
PropertyAccessTarget target({map, name, access_mode});
auto it = property_access_infos_.find(target);
if (it != property_access_infos_.end()) return it->second;
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_BROKER_MISSING(this, "PropertyAccessInfo for "
<< access_mode << " of property " << name
<< " on map " << map);
return PropertyAccessInfo::Invalid(zone());
}
CHECK_NOT_NULL(dependencies);
AccessInfoFactory factory(this, dependencies, zone());
PropertyAccessInfo access_info = factory.ComputePropertyAccessInfo(
map.object(), name.object(), access_mode);
if (is_concurrent_inlining_) {
CHECK_EQ(mode(), kSerializing);
TRACE(this, "Storing PropertyAccessInfo for "
<< access_mode << " of property " << name << " on map "
<< map);
property_access_infos_.insert({target, access_info});
}
return access_info;
}
MinimorphicLoadPropertyAccessInfo JSHeapBroker::GetPropertyAccessInfo(
MinimorphicLoadPropertyAccessFeedback const& feedback,
FeedbackSource const& source, SerializationPolicy policy) {
auto it = minimorphic_property_access_infos_.find(source);
if (it != minimorphic_property_access_infos_.end()) return it->second;
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_BROKER_MISSING(this, "MinimorphicLoadPropertyAccessInfo for slot "
<< source.index() << " "
<< ObjectRef(this, source.vector));
return MinimorphicLoadPropertyAccessInfo::Invalid();
}
AccessInfoFactory factory(this, nullptr, zone());
MinimorphicLoadPropertyAccessInfo access_info =
factory.ComputePropertyAccessInfo(feedback);
if (is_concurrent_inlining_) {
TRACE(this, "Storing MinimorphicLoadPropertyAccessInfo for "
<< source.index() << " "
<< ObjectRef(this, source.vector));
minimorphic_property_access_infos_.insert({source, access_info});
}
return access_info;
}
BinaryOperationFeedback const& ProcessedFeedback::AsBinaryOperation() const {
CHECK_EQ(kBinaryOperation, kind());
return *static_cast<BinaryOperationFeedback const*>(this);
}
CallFeedback const& ProcessedFeedback::AsCall() const {
CHECK_EQ(kCall, kind());
return *static_cast<CallFeedback const*>(this);
}
CompareOperationFeedback const& ProcessedFeedback::AsCompareOperation() const {
CHECK_EQ(kCompareOperation, kind());
return *static_cast<CompareOperationFeedback const*>(this);
}
ElementAccessFeedback const& ProcessedFeedback::AsElementAccess() const {
CHECK_EQ(kElementAccess, kind());
return *static_cast<ElementAccessFeedback const*>(this);
}
ForInFeedback const& ProcessedFeedback::AsForIn() const {
CHECK_EQ(kForIn, kind());
return *static_cast<ForInFeedback const*>(this);
}
GlobalAccessFeedback const& ProcessedFeedback::AsGlobalAccess() const {
CHECK_EQ(kGlobalAccess, kind());
return *static_cast<GlobalAccessFeedback const*>(this);
}
InstanceOfFeedback const& ProcessedFeedback::AsInstanceOf() const {
CHECK_EQ(kInstanceOf, kind());
return *static_cast<InstanceOfFeedback const*>(this);
}
NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const {
CHECK_EQ(kNamedAccess, kind());
return *static_cast<NamedAccessFeedback const*>(this);
}
MinimorphicLoadPropertyAccessFeedback const&
ProcessedFeedback::AsMinimorphicPropertyAccess() const {
CHECK_EQ(kMinimorphicPropertyAccess, kind());
return *static_cast<MinimorphicLoadPropertyAccessFeedback const*>(this);
}
LiteralFeedback const& ProcessedFeedback::AsLiteral() const {
CHECK_EQ(kLiteral, kind());
return *static_cast<LiteralFeedback const*>(this);
}
RegExpLiteralFeedback const& ProcessedFeedback::AsRegExpLiteral() const {
CHECK_EQ(kRegExpLiteral, kind());
return *static_cast<RegExpLiteralFeedback const*>(this);
}
TemplateObjectFeedback const& ProcessedFeedback::AsTemplateObject() const {
CHECK_EQ(kTemplateObject, kind());
return *static_cast<TemplateObjectFeedback const*>(this);
}
BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis(
Handle<BytecodeArray> bytecode_array, BailoutId osr_bailout_id,
bool analyze_liveness, SerializationPolicy policy) {
ObjectData* bytecode_array_data = GetOrCreateData(bytecode_array);
CHECK_NOT_NULL(bytecode_array_data);
auto it = bytecode_analyses_.find(bytecode_array_data);
if (it != bytecode_analyses_.end()) {
// Bytecode analysis can be run for OSR or for non-OSR. In the rare case
// where we optimize for OSR and consider the top-level function itself for
// inlining (because of recursion), we need both the OSR and the non-OSR
// analysis. Fortunately, the only difference between the two lies in
// whether the OSR entry offset gets computed (from the OSR bailout id).
// Hence it's okay to reuse the OSR-version when asked for the non-OSR
// version, such that we need to store at most one analysis result per
// bytecode array.
CHECK_IMPLIES(osr_bailout_id != it->second->osr_bailout_id(),
osr_bailout_id.IsNone());
CHECK_EQ(analyze_liveness, it->second->liveness_analyzed());
return *it->second;
}
CHECK_EQ(policy, SerializationPolicy::kSerializeIfNeeded);
BytecodeAnalysis* analysis = zone()->New<BytecodeAnalysis>(
bytecode_array, zone(), osr_bailout_id, analyze_liveness);
DCHECK_EQ(analysis->osr_bailout_id(), osr_bailout_id);
bytecode_analyses_[bytecode_array_data] = analysis;
return *analysis;
}
bool JSHeapBroker::StackHasOverflowed() const {
DCHECK_IMPLIES(local_isolate_ == nullptr,
ThreadId::Current() == isolate_->thread_id());
return (local_isolate_ != nullptr)
? StackLimitCheck::HasOverflowed(local_isolate_)
: StackLimitCheck(isolate_).HasOverflowed();
}
OffHeapBytecodeArray::OffHeapBytecodeArray(BytecodeArrayRef bytecode_array)
: array_(bytecode_array) {}
int OffHeapBytecodeArray::length() const { return array_.length(); }
int OffHeapBytecodeArray::parameter_count() const {
return array_.parameter_count();
}
uint8_t OffHeapBytecodeArray::get(int index) const { return array_.get(index); }
void OffHeapBytecodeArray::set(int index, uint8_t value) { UNREACHABLE(); }
Address OffHeapBytecodeArray::GetFirstBytecodeAddress() const {
return array_.GetFirstBytecodeAddress();
}
Handle<Object> OffHeapBytecodeArray::GetConstantAtIndex(
int index, Isolate* isolate) const {
return array_.GetConstantAtIndex(index);
}
bool OffHeapBytecodeArray::IsConstantAtIndexSmi(int index) const {
return array_.IsConstantAtIndexSmi(index);
}
Smi OffHeapBytecodeArray::GetConstantAtIndexAsSmi(int index) const {
return array_.GetConstantAtIndexAsSmi(index);
}
#undef BIMODAL_ACCESSOR
#undef BIMODAL_ACCESSOR_B
#undef BIMODAL_ACCESSOR_C
#undef IF_ACCESS_FROM_HEAP
#undef IF_ACCESS_FROM_HEAP_C
#undef TRACE
#undef TRACE_MISSING
} // namespace compiler
} // namespace internal
} // namespace v8