// 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 #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/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/boxed-float.h" #include "src/utils/utils.h" namespace v8 { namespace internal { namespace compiler { #define TRACE(broker, x) TRACE_BROKER(broker, x) #define TRACE_MISSING(broker, x) TRACE_BROKER_MISSING(broker, x) #define FORWARD_DECL(Name) class Name##Data; HEAP_BROKER_OBJECT_LIST(FORWARD_DECL) #undef FORWARD_DECL // There are three kinds of ObjectData values. // // kSmi: The underlying V8 object is a Smi and the data is an instance of the // base class (ObjectData), i.e. it's basically just the handle. Because the // object is a Smi, it's safe to access the handle in order to extract the // number value, and AsSmi() does exactly that. // // kSerializedHeapObject: The underlying V8 object is a HeapObject and the // data is an instance of the corresponding (most-specific) subclass, e.g. // JSFunctionData, which provides serialized information about the object. // // kUnserializedHeapObject: The underlying V8 object is a HeapObject and the // data is an instance of the base class (ObjectData), i.e. it basically // carries no information other than the handle. // // 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, kUnserializedReadOnlyHeapObject }; class AllowHandleAllocationIf { public: explicit AllowHandleAllocationIf(ObjectDataKind kind, JSHeapBroker::BrokerMode mode) { DCHECK_IMPLIES(mode == JSHeapBroker::BrokerMode::kSerialized, kind == kUnserializedReadOnlyHeapObject); if (kind == kUnserializedHeapObject) maybe_allow_handle_.emplace(); } private: base::Optional maybe_allow_handle_; }; class AllowHandleDereferenceIf { public: explicit AllowHandleDereferenceIf(ObjectDataKind kind, JSHeapBroker::BrokerMode mode) { DCHECK_IMPLIES(mode == JSHeapBroker::BrokerMode::kSerialized, kind == kUnserializedReadOnlyHeapObject); if (kind == kUnserializedHeapObject || kind == kUnserializedReadOnlyHeapObject) maybe_allow_handle_.emplace(); } explicit AllowHandleDereferenceIf(ObjectDataKind kind) { if (kind == kUnserializedHeapObject || kind == kUnserializedReadOnlyHeapObject) maybe_allow_handle_.emplace(); } private: base::Optional maybe_allow_handle_; }; class AllowHeapAllocationIf { public: explicit AllowHeapAllocationIf(ObjectDataKind kind, JSHeapBroker::BrokerMode mode) { DCHECK_IMPLIES(mode == JSHeapBroker::BrokerMode::kSerialized, kind == kUnserializedReadOnlyHeapObject); if (kind == kUnserializedHeapObject) maybe_allow_handle_.emplace(); } private: base::Optional 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, 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, IsReadOnlyHeapObject(*object)); } #define DECLARE_IS_AND_AS(Name) \ bool Is##Name() const; \ Name##Data* As##Name(); HEAP_BROKER_OBJECT_LIST(DECLARE_IS_AND_AS) #undef DECLARE_IS_AND_AS Handle object() const { return object_; } ObjectDataKind kind() const { return kind_; } bool is_smi() const { return kind_ == kSmi; } bool should_access_heap() const { return kind_ == kUnserializedHeapObject || kind_ == kUnserializedReadOnlyHeapObject; } #ifdef DEBUG enum class Usage{kUnused, kOnlyIdentityUsed, kDataUsed}; mutable Usage used_status = Usage::kUnused; #endif // DEBUG private: Handle const object_; ObjectDataKind const kind_; }; class HeapObjectData : public ObjectData { public: HeapObjectData(JSHeapBroker* broker, ObjectData** storage, Handle object); bool boolean_value() const { return boolean_value_; } ObjectData* map() const { return map_; } InstanceType GetMapInstanceType() const; static HeapObjectData* Serialize(JSHeapBroker* broker, Handle object); private: bool const boolean_value_; ObjectData* const map_; }; class PropertyCellData : public HeapObjectData { public: PropertyCellData(JSHeapBroker* broker, ObjectData** storage, Handle 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 KnownReceiversMap; class FunctionTemplateInfoData : public HeapObjectData { public: FunctionTemplateInfoData(JSHeapBroker* broker, ObjectData** storage, Handle object); bool is_signature_undefined() const { return is_signature_undefined_; } bool accept_any_receiver() const { return accept_any_receiver_; } bool has_call_code() const { return has_call_code_; } void SerializeCallCode(JSHeapBroker* broker); CallHandlerInfoData* call_code() const { return call_code_; } 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; CallHandlerInfoData* 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 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 object) : HeapObjectData(broker, storage, object), c_function_(v8::ToCData
(object->GetCFunction())), c_signature_(v8::ToCData(object->GetCSignature())), known_receivers_(broker->zone()) { auto function_template_info = Handle::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 object) : HeapObjectData(broker, storage, object), callback_(v8::ToCData
(object->callback())) {} void JSHeapBroker::IncrementTracingIndentation() { ++trace_indentation_; } void JSHeapBroker::DecrementTracingIndentation() { --trace_indentation_; } PropertyCellData::PropertyCellData(JSHeapBroker* broker, ObjectData** storage, Handle 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::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::cast(object()); call_code_ = broker->GetOrCreateData(function_template_info->call_code()) ->AsCallHandlerInfo(); call_code_->Serialize(broker); } void CallHandlerInfoData::Serialize(JSHeapBroker* broker) { if (data_ != nullptr) return; TraceScope tracer(broker, this, "CallHandlerInfoData::Serialize"); auto call_handler_info = Handle::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(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 object) : HeapObjectData(broker, storage, object) {} }; class JSObjectData : public JSReceiverData { public: JSObjectData(JSHeapBroker* broker, ObjectData** storage, Handle 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 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> 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 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 = Handle::cast(object()); if (jsobject->map().is_prototype_map()) { Handle maybe_proto_info(jsobject->map().prototype_info(), broker->isolate()); if (maybe_proto_info->IsPrototypeInfo()) { auto proto_info = Handle::cast(maybe_proto_info); if (proto_info->HasObjectCreateMap()) { DCHECK_NULL(object_create_map_); object_create_map_ = broker->GetOrCreateData(proto_info->ObjectCreateMap()); } } } } namespace { base::Optional GetOwnElementFromHeap(JSHeapBroker* broker, Handle receiver, uint32_t index, bool constant_only) { LookupIterator it(broker->isolate(), receiver, index, LookupIterator::OWN); if (it.state() == LookupIterator::DATA && (!constant_only || (it.IsReadOnly() && !it.IsConfigurable()))) { return ObjectRef(broker, it.GetDataValue()); } return base::nullopt; } ObjectRef GetOwnDataPropertyFromHeap(JSHeapBroker* broker, Handle receiver, Representation representation, FieldIndex field_index) { Handle 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 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::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 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_; } HeapObjectData* buffer() const { return buffer_; } private: bool const is_on_heap_; size_t const length_; void* const data_ptr_; bool serialized_ = false; HeapObjectData* buffer_ = nullptr; }; JSTypedArrayData::JSTypedArrayData(JSHeapBroker* broker, ObjectData** storage, Handle 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 typed_array = Handle::cast(object()); if (!is_on_heap()) { DCHECK_NULL(buffer_); buffer_ = broker->GetOrCreateData(typed_array->buffer())->AsHeapObject(); } } class ArrayBoilerplateDescriptionData : public HeapObjectData { public: ArrayBoilerplateDescriptionData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object), constants_elements_length_(object->constant_elements().length()) {} 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 object) : HeapObjectData(broker, storage, object), size_(object->size()) {} int size() const { return size_; } private: int const size_; }; class JSDataViewData : public JSObjectData { public: JSDataViewData(JSHeapBroker* broker, ObjectData** storage, Handle object); size_t byte_length() const { return byte_length_; } size_t byte_offset() const { return byte_offset_; } private: size_t const byte_length_; size_t const byte_offset_; }; class JSBoundFunctionData : public JSObjectData { public: JSBoundFunctionData(JSHeapBroker* broker, ObjectData** storage, Handle object); void 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 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 IsOptimized() const { return is_optimized_; } bool PrototypeRequiresRuntimeLookup() const { return PrototypeRequiresRuntimeLookup_; } void Serialize(JSHeapBroker* broker); bool serialized() const { return serialized_; } ContextData* context() const { return context_; } NativeContextData* native_context() const { return native_context_; } ObjectData* initial_map() const { return initial_map_; } ObjectData* prototype() const { return prototype_; } SharedFunctionInfoData* shared() const { return shared_; } FeedbackVectorData* feedback_vector() const { return feedback_vector_; } CodeData* 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 is_optimized_; bool PrototypeRequiresRuntimeLookup_; bool serialized_ = false; ContextData* context_ = nullptr; NativeContextData* native_context_ = nullptr; ObjectData* initial_map_ = nullptr; ObjectData* prototype_ = nullptr; SharedFunctionInfoData* shared_ = nullptr; FeedbackVectorData* feedback_vector_ = nullptr; CodeData* code_ = nullptr; int initial_map_instance_size_with_min_slack_; }; class JSRegExpData : public JSObjectData { public: JSRegExpData(JSHeapBroker* broker, ObjectData** storage, Handle 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 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 object); // {previous} will return the closest valid context possible to desired // {depth}, decrementing {depth} for each previous link successfully followed. ContextData* previous( JSHeapBroker* broker, size_t* depth, 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 slots_; ContextData* previous_ = nullptr; }; ContextData::ContextData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object), slots_(broker->zone()) {} ContextData* ContextData::previous(JSHeapBroker* broker, size_t* depth, SerializationPolicy policy) { if (*depth == 0) return this; if (policy == SerializationPolicy::kSerializeIfNeeded && previous_ == nullptr) { TraceScope tracer(broker, this, "ContextData::previous"); Handle context = Handle::cast(object()); Object prev = context->unchecked_previous(); if (prev.IsContext()) { previous_ = broker->GetOrCreateData(prev)->AsContext(); } } if (previous_ != nullptr) { *depth = *depth - 1; return previous_->previous(broker, depth, policy); } return this; } 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 = Handle::cast(object()); if (index < context->length()) { TraceScope tracer(broker, this, "ContextData::GetSlot"); TRACE(broker, "Serializing context slot " << index); ObjectData* odata = broker->GetOrCreateData(context->get(index)); slots_.insert(std::make_pair(index, odata)); return odata; } } return nullptr; } class NativeContextData : public ContextData { public: #define DECL_ACCESSOR(type, name) \ type##Data* name() const { return name##_; } BROKER_NATIVE_CONTEXT_FIELDS(DECL_ACCESSOR) #undef DECL_ACCESSOR const ZoneVector& function_maps() const { CHECK(serialized_); return function_maps_; } ScopeInfoData* scope_info() const { CHECK(serialized_); return scope_info_; } NativeContextData(JSHeapBroker* broker, ObjectData** storage, Handle object); void Serialize(JSHeapBroker* broker); private: bool serialized_ = false; #define DECL_MEMBER(type, name) type##Data* name##_ = nullptr; BROKER_NATIVE_CONTEXT_FIELDS(DECL_MEMBER) #undef DECL_MEMBER ZoneVector function_maps_; ScopeInfoData* scope_info_ = nullptr; }; class NameData : public HeapObjectData { public: NameData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object) {} }; class StringData : public NameData { public: StringData(JSHeapBroker* broker, ObjectData** storage, Handle object); int length() const { return length_; } uint16_t first_char() const { return first_char_; } base::Optional to_number() const { return to_number_; } bool is_external_string() const { return is_external_string_; } bool is_seq_string() const { return is_seq_string_; } StringData* GetCharAsString( JSHeapBroker* broker, uint32_t index, SerializationPolicy policy = SerializationPolicy::kAssumeSerialized); private: int const length_; uint16_t const first_char_; base::Optional 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> chars_as_strings_; static constexpr int kMaxLengthForDoubleConversion = 23; }; class SymbolData : public NameData { public: SymbolData(JSHeapBroker* broker, ObjectData** storage, Handle object) : NameData(broker, storage, object) {} }; StringData::StringData(JSHeapBroker* broker, ObjectData** storage, Handle object) : NameData(broker, storage, object), length_(object->length()), first_char_(length_ > 0 ? object->Get(0) : 0), is_external_string_(object->IsExternalString()), is_seq_string_(object->IsSeqString()), chars_as_strings_(broker->zone()) { if (length_ <= kMaxLengthForDoubleConversion) { const int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY; uc16 buffer[kMaxLengthForDoubleConversion]; String::WriteToFlat(*object, buffer, 0, length_); Vector v(buffer, length_); to_number_ = StringToDouble(v, flags); } } class InternalizedStringData : public StringData { public: InternalizedStringData(JSHeapBroker* broker, ObjectData** storage, Handle object); uint32_t array_index() const { return array_index_; } private: uint32_t array_index_; }; StringData* StringData::GetCharAsString(JSHeapBroker* broker, uint32_t index, SerializationPolicy policy) { if (index >= static_cast(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 element = GetOwnElementFromHeap(broker, object(), index, true); StringData* result = element.has_value() ? element->data()->AsString() : nullptr; chars_as_strings_.push_back({index, result}); return result; } InternalizedStringData::InternalizedStringData( JSHeapBroker* broker, ObjectData** storage, Handle object) : StringData(broker, storage, object) {} namespace { bool IsFastLiteralHelper(Handle 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 elements(boilerplate->elements(), isolate); if (elements->length() > 0 && elements->map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) { if (boilerplate->HasSmiOrObjectElements()) { Handle fast_elements = Handle::cast(elements); int length = elements->length(); for (int i = 0; i < length; i++) { if ((*max_properties)-- == 0) return false; Handle value(fast_elements->get(i), isolate); if (value->IsJSObject()) { Handle value_object = Handle::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 descriptors(boilerplate->map().instance_descriptors(), 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 value(boilerplate->RawFastPropertyAt(field_index), isolate); if (value->IsJSObject()) { Handle value_object = Handle::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 boilerplate) { int max_properties = kMaxFastLiteralProperties; return IsFastLiteralHelper(boilerplate, kMaxFastLiteralDepth, &max_properties); } } // namespace class AccessorInfoData : public HeapObjectData { public: AccessorInfoData(JSHeapBroker* broker, ObjectData** storage, Handle object); }; class AllocationSiteData : public HeapObjectData { public: AllocationSiteData(JSHeapBroker* broker, ObjectData** storage, Handle object); void SerializeBoilerplate(JSHeapBroker* broker); bool PointsToLiteral() const { return PointsToLiteral_; } AllocationType GetAllocationType() const { return GetAllocationType_; } ObjectData* nested_site() const { return nested_site_; } bool IsFastLiteral() const { return IsFastLiteral_; } JSObjectData* boilerplate() const { return boilerplate_; } // These are only valid if PointsToLiteral is false. ElementsKind GetElementsKind() const { return GetElementsKind_; } bool CanInlineCall() const { return CanInlineCall_; } private: bool const PointsToLiteral_; AllocationType const GetAllocationType_; ObjectData* nested_site_ = nullptr; bool IsFastLiteral_ = false; JSObjectData* boilerplate_ = nullptr; ElementsKind GetElementsKind_ = NO_ELEMENTS; bool CanInlineCall_ = false; bool serialized_boilerplate_ = false; }; class BigIntData : public HeapObjectData { public: BigIntData(JSHeapBroker* broker, ObjectData** storage, Handle 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 object) : HeapObjectData(broker, storage, object) {} }; struct PropertyDescriptor { NameData* key = nullptr; ObjectData* value = nullptr; PropertyDetails details = PropertyDetails::Empty(); FieldIndex field_index; MapData* field_owner = nullptr; ObjectData* field_type = nullptr; bool is_unboxed_double_field = false; }; class MapData : public HeapObjectData { public: MapData(JSHeapBroker* broker, ObjectData** storage, Handle 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& 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; DescriptorArrayData* instance_descriptors() const { return instance_descriptors_; } void SerializeRootMap(JSHeapBroker* broker); MapData* FindRootMap() const; void SerializeConstructor(JSHeapBroker* broker); ObjectData* GetConstructor() const { CHECK(serialized_constructor_); return constructor_; } void SerializeBackPointer(JSHeapBroker* broker); HeapObjectData* GetBackPointer() const { CHECK(serialized_backpointer_); return backpointer_; } void SerializePrototype(JSHeapBroker* broker); bool serialized_prototype() const { return serialized_prototype_; } ObjectData* prototype() const { CHECK(serialized_prototype_); return prototype_; } void SerializeForElementLoad(JSHeapBroker* broker); void SerializeForElementStore(JSHeapBroker* broker); private: InstanceType const instance_type_; int const instance_size_; byte const bit_field_; byte const bit_field2_; uint32_t const bit_field3_; bool const can_be_deprecated_; bool const can_transition_; int const in_object_properties_start_in_words_; int const in_object_properties_; int const constructor_function_index_; int const next_free_property_index_; int const unused_property_fields_; bool const supports_fast_array_iteration_; bool const supports_fast_array_resize_; bool const is_abandoned_prototype_map_; bool serialized_elements_kind_generalizations_ = false; ZoneVector elements_kind_generalizations_; bool serialized_own_descriptors_ = false; DescriptorArrayData* instance_descriptors_ = nullptr; bool serialized_constructor_ = false; ObjectData* constructor_ = nullptr; bool serialized_backpointer_ = false; HeapObjectData* backpointer_ = nullptr; bool serialized_prototype_ = false; ObjectData* prototype_ = nullptr; bool serialized_root_map_ = false; MapData* root_map_ = nullptr; bool serialized_for_element_load_ = false; bool serialized_for_element_store_ = false; }; AccessorInfoData::AccessorInfoData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object) {} AllocationSiteData::AllocationSiteData(JSHeapBroker* broker, ObjectData** storage, Handle 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 site = Handle::cast(object()); CHECK(IsFastLiteral_); DCHECK_NULL(boilerplate_); boilerplate_ = broker->GetOrCreateData(site->boilerplate())->AsJSObject(); boilerplate_->SerializeAsBoilerplate(broker); DCHECK_NULL(nested_site_); nested_site_ = broker->GetOrCreateData(site->nested_site()); if (nested_site_->IsAllocationSite()) { nested_site_->AsAllocationSite()->SerializeBoilerplate(broker); } } HeapObjectData::HeapObjectData(JSHeapBroker* broker, ObjectData** storage, Handle 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(broker->SerializingAllowed()); } InstanceType HeapObjectData::GetMapInstanceType() const { ObjectData* map_data = map(); if (map_data->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(kind()); return Handle::cast(map_data->object())->instance_type(); } return map_data->AsMap()->instance_type(); } namespace { bool IsReadOnlyLengthDescriptor(Isolate* isolate, Handle jsarray_map) { DCHECK(!jsarray_map->is_dictionary_map()); Handle length_string = isolate->factory()->length_string(); DescriptorArray descriptors = jsarray_map->instance_descriptors(); // 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) { 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) { return SupportsFastArrayIteration(isolate, map) && map->is_extensible() && !map->is_dictionary_map() && !IsReadOnlyLengthDescriptor(isolate, map); } } // namespace MapData::MapData(JSHeapBroker* broker, ObjectData** storage, Handle 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 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()), is_optimized_(object->IsOptimized()), PrototypeRequiresRuntimeLookup_( object->PrototypeRequiresRuntimeLookup()) {} void JSFunctionData::Serialize(JSHeapBroker* broker) { if (serialized_) return; serialized_ = true; TraceScope tracer(broker, this, "JSFunctionData::Serialize"); Handle function = Handle::cast(object()); DCHECK_NULL(context_); DCHECK_NULL(native_context_); DCHECK_NULL(initial_map_); DCHECK_NULL(prototype_); DCHECK_NULL(shared_); DCHECK_NULL(feedback_vector_); DCHECK_NULL(code_); context_ = broker->GetOrCreateData(function->context())->AsContext(); native_context_ = broker->GetOrCreateData(function->native_context())->AsNativeContext(); shared_ = broker->GetOrCreateData(function->shared())->AsSharedFunctionInfo(); feedback_vector_ = has_feedback_vector() ? broker->GetOrCreateData(function->feedback_vector()) ->AsFeedbackVector() : nullptr; code_ = broker->GetOrCreateData(function->code())->AsCode(); initial_map_ = has_initial_map() ? broker->GetOrCreateData(function->initial_map())->AsMap() : nullptr; prototype_ = has_prototype() ? broker->GetOrCreateData(function->prototype()) : nullptr; if (initial_map_ != nullptr) { initial_map_instance_size_with_min_slack_ = function->ComputeInstanceSizeWithMinSlack(broker->isolate()); } if (initial_map_ != 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(i); if (IsMoreGeneralElementsKindTransition(from_kind, to_kind)) { Handle target = Map::AsElementsKind(broker->isolate(), self.object(), to_kind); elements_kind_generalizations_.push_back( broker->GetOrCreateData(target)->AsMap()); } } } class DescriptorArrayData : public HeapObjectData { public: DescriptorArrayData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object), contents_(broker->zone()) {} ZoneMap& contents() { return contents_; } private: ZoneMap contents_; }; class FeedbackCellData : public HeapObjectData { public: FeedbackCellData(JSHeapBroker* broker, ObjectData** storage, Handle object); HeapObjectData* value() const { return value_; } private: HeapObjectData* const value_; }; FeedbackCellData::FeedbackCellData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object), value_(broker->GetOrCreateData(object->value())->AsHeapObject()) {} class FeedbackVectorData : public HeapObjectData { public: FeedbackVectorData(JSHeapBroker* broker, ObjectData** storage, Handle object); double invocation_count() const { return invocation_count_; } SharedFunctionInfoData* shared_function_info() { CHECK(serialized_); return shared_function_info_; } void Serialize(JSHeapBroker* broker); bool serialized() const { return serialized_; } FeedbackCellData* GetClosureFeedbackCell(JSHeapBroker* broker, int index) const; private: double const invocation_count_; bool serialized_ = false; SharedFunctionInfoData* shared_function_info_; ZoneVector closure_feedback_cell_array_; }; FeedbackVectorData::FeedbackVectorData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object), invocation_count_(object->invocation_count()), closure_feedback_cell_array_(broker->zone()) {} FeedbackCellData* 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]->AsFeedbackCell(); } void FeedbackVectorData::Serialize(JSHeapBroker* broker) { if (serialized_) return; serialized_ = true; TraceScope tracer(broker, this, "FeedbackVectorData::Serialize"); Handle vector = Handle::cast(object()); Handle sfi(vector->shared_function_info(), broker->isolate()); shared_function_info_ = broker->GetOrCreateData(sfi)->AsSharedFunctionInfo(); 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 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 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 object); // Creates all elements of the fixed array. void SerializeContents(JSHeapBroker* broker); ObjectData* Get(int i) const; private: bool serialized_contents_ = false; ZoneVector contents_; }; JSDataViewData::JSDataViewData(JSHeapBroker* broker, ObjectData** storage, Handle object) : JSObjectData(broker, storage, object), byte_length_(object->byte_length()), byte_offset_(object->byte_offset()) {} JSBoundFunctionData::JSBoundFunctionData(JSHeapBroker* broker, ObjectData** storage, Handle object) : JSObjectData(broker, storage, object) {} void JSBoundFunctionData::Serialize(JSHeapBroker* broker) { if (serialized_) return; serialized_ = true; TraceScope tracer(broker, this, "JSBoundFunctionData::Serialize"); Handle function = Handle::cast(object()); DCHECK_NULL(bound_target_function_); bound_target_function_ = broker->GetOrCreateData(function->bound_target_function()); if (bound_target_function_->IsJSBoundFunction()) { bound_target_function_->AsJSBoundFunction()->Serialize(broker); } else if (bound_target_function_->IsJSFunction()) { bound_target_function_->AsJSFunction()->Serialize(broker); } 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()); } JSObjectData::JSObjectData(JSHeapBroker* broker, ObjectData** storage, Handle 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 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 array = Handle::cast(object()); CHECK_EQ(array->length(), length()); CHECK(contents_.empty()); contents_.reserve(static_cast(length())); for (int i = 0; i < length(); i++) { Handle 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 object); // Serializes all elements of the fixed array. void SerializeContents(JSHeapBroker* broker); Float64 Get(int i) const; private: bool serialized_contents_ = false; ZoneVector contents_; }; FixedDoubleArrayData::FixedDoubleArrayData(JSHeapBroker* broker, ObjectData** storage, Handle 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 self = Handle::cast(object()); CHECK_EQ(self->length(), length()); CHECK(contents_.empty()); contents_.reserve(static_cast(length())); for (int i = 0; i < length(); i++) { contents_.push_back(Float64::FromBits(self->get_representation(i))); } TRACE(broker, "Copied " << contents_.size() << " elements"); } class BytecodeArrayData : public FixedArrayBaseData { public: int register_count() const { return register_count_; } int parameter_count() const { return parameter_count_; } interpreter::Register incoming_new_target_or_generator_register() const { return incoming_new_target_or_generator_register_; } uint8_t get(int index) const { DCHECK(is_serialized_for_compilation_); return bytecodes_[index]; } Address GetFirstBytecodeAddress() const { return reinterpret_cast
(bytecodes_.data()); } Handle 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::cast(constant_pool_[index]->object())); } void SerializeForCompilation(JSHeapBroker* broker) { if (is_serialized_for_compilation_) return; Handle bytecode_array = Handle::cast(object()); DCHECK(bytecodes_.empty()); bytecodes_.reserve(bytecode_array->length()); for (int i = 0; i < bytecode_array->length(); i++) { bytecodes_.push_back(bytecode_array->get(i)); } DCHECK(constant_pool_.empty()); Handle constant_pool(bytecode_array->constant_pool(), broker->isolate()); constant_pool_.reserve(constant_pool->length()); for (int i = 0; i < constant_pool->length(); i++) { constant_pool_.push_back(broker->GetOrCreateData(constant_pool->get(i))); } Handle source_position_table( bytecode_array->SourcePositionTableIfCollected(), broker->isolate()); source_positions_.reserve(source_position_table->length()); for (int i = 0; i < source_position_table->length(); i++) { source_positions_.push_back(source_position_table->get(i)); } Handle handlers(bytecode_array->handler_table(), broker->isolate()); handler_table_.reserve(handlers->length()); for (int i = 0; i < handlers->length(); i++) { handler_table_.push_back(handlers->get(i)); } is_serialized_for_compilation_ = true; } const byte* source_positions_address() const { return source_positions_.data(); } size_t source_positions_size() const { return source_positions_.size(); } Address handler_table_address() const { CHECK(is_serialized_for_compilation_); return reinterpret_cast
(handler_table_.data()); } int handler_table_size() const { CHECK(is_serialized_for_compilation_); return static_cast(handler_table_.size()); } BytecodeArrayData(JSHeapBroker* broker, ObjectData** storage, Handle object) : FixedArrayBaseData(broker, storage, object), register_count_(object->register_count()), parameter_count_(object->parameter_count()), incoming_new_target_or_generator_register_( object->incoming_new_target_or_generator_register()), bytecodes_(broker->zone()), source_positions_(broker->zone()), handler_table_(broker->zone()), constant_pool_(broker->zone()) {} private: int const register_count_; int const parameter_count_; interpreter::Register const incoming_new_target_or_generator_register_; bool is_serialized_for_compilation_ = false; ZoneVector bytecodes_; ZoneVector source_positions_; ZoneVector handler_table_; ZoneVector constant_pool_; }; class JSArrayData : public JSObjectData { public: JSArrayData(JSHeapBroker* broker, ObjectData** storage, Handle 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> own_elements_; }; JSArrayData::JSArrayData(JSHeapBroker* broker, ObjectData** storage, Handle 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 = Handle::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 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 object); int context_length() const { return context_length_; } bool has_outer_scope_info() const { return has_outer_scope_info_; } int flags() const { return flags_; } ScopeInfoData* outer_scope_info() const { return outer_scope_info_; } void SerializeScopeInfoChain(JSHeapBroker* broker); private: int const context_length_; bool const has_outer_scope_info_; int const flags_; // Only serialized via SerializeScopeInfoChain. ScopeInfoData* outer_scope_info_; }; ScopeInfoData::ScopeInfoData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object), context_length_(object->ContextLength()), has_outer_scope_info_(object->HasOuterScopeInfo()), flags_(object->Flags()), outer_scope_info_(nullptr) {} void ScopeInfoData::SerializeScopeInfoChain(JSHeapBroker* broker) { if (outer_scope_info_) return; if (!has_outer_scope_info_) return; outer_scope_info_ = broker ->GetOrCreateData(Handle::cast(object())->OuterScopeInfo()) ->AsScopeInfo(); outer_scope_info_->SerializeScopeInfoChain(broker); } class SharedFunctionInfoData : public HeapObjectData { public: SharedFunctionInfoData(JSHeapBroker* broker, ObjectData** storage, Handle object); int builtin_id() const { return builtin_id_; } int context_header_size() const { return context_header_size_; } BytecodeArrayData* GetBytecodeArray() const { return GetBytecodeArray_; } void SerializeFunctionTemplateInfo(JSHeapBroker* broker); ScopeInfoData* scope_info() const { return scope_info_; } void SerializeScopeInfoChain(JSHeapBroker* broker); FunctionTemplateInfoData* function_template_info() const { return function_template_info_; } JSArrayData* GetTemplateObject(FeedbackSlot slot) const { auto lookup_it = template_objects_.find(slot.ToInt()); if (lookup_it != template_objects_.cend()) { return lookup_it->second; } return nullptr; } void SetTemplateObject(FeedbackSlot slot, JSArrayData* object) { CHECK( template_objects_.insert(std::make_pair(slot.ToInt(), object)).second); } #define DECL_ACCESSOR(type, name) \ type name() const { return name##_; } BROKER_SFI_FIELDS(DECL_ACCESSOR) #undef DECL_ACCESSOR private: int const builtin_id_; int context_header_size_; BytecodeArrayData* const GetBytecodeArray_; #define DECL_MEMBER(type, name) type const name##_; BROKER_SFI_FIELDS(DECL_MEMBER) #undef DECL_MEMBER FunctionTemplateInfoData* function_template_info_; ZoneMap template_objects_; ScopeInfoData* scope_info_; }; SharedFunctionInfoData::SharedFunctionInfoData( JSHeapBroker* broker, ObjectData** storage, Handle 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()) ->AsBytecodeArray() : nullptr) #define INIT_MEMBER(type, name) , name##_(object->name()) BROKER_SFI_FIELDS(INIT_MEMBER) #undef INIT_MEMBER , 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( Handle::cast(object())->function_data(), broker->isolate())) ->AsFunctionTemplateInfo(); } void SharedFunctionInfoData::SerializeScopeInfoChain(JSHeapBroker* broker) { if (scope_info_) return; scope_info_ = broker ->GetOrCreateData( Handle::cast(object())->scope_info()) ->AsScopeInfo(); scope_info_->SerializeScopeInfoChain(broker); } class SourceTextModuleData : public HeapObjectData { public: SourceTextModuleData(JSHeapBroker* broker, ObjectData** storage, Handle object); void Serialize(JSHeapBroker* broker); CellData* GetCell(JSHeapBroker* broker, int cell_index) const; private: bool serialized_ = false; ZoneVector imports_; ZoneVector exports_; }; SourceTextModuleData::SourceTextModuleData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object), imports_(broker->zone()), exports_(broker->zone()) {} CellData* 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; } CellData* cell; switch (SourceTextModuleDescriptor::GetCellIndexKind(cell_index)) { case SourceTextModuleDescriptor::kImport: cell = imports_.at(SourceTextModule::ImportIndex(cell_index)); break; case SourceTextModuleDescriptor::kExport: cell = exports_.at(SourceTextModule::ExportIndex(cell_index)); break; case SourceTextModuleDescriptor::kInvalid: UNREACHABLE(); } CHECK_NOT_NULL(cell); return cell; } void SourceTextModuleData::Serialize(JSHeapBroker* broker) { if (serialized_) return; serialized_ = true; TraceScope tracer(broker, this, "SourceTextModuleData::Serialize"); Handle module = Handle::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 imports(module->regular_imports(), broker->isolate()); int const imports_length = imports->length(); imports_.reserve(imports_length); for (int i = 0; i < imports_length; ++i) { imports_.push_back(broker->GetOrCreateData(imports->get(i))->AsCell()); } TRACE(broker, "Copied " << imports_.size() << " imports"); DCHECK(exports_.empty()); Handle exports(module->regular_exports(), broker->isolate()); int const exports_length = exports->length(); exports_.reserve(exports_length); for (int i = 0; i < exports_length; ++i) { exports_.push_back(broker->GetOrCreateData(exports->get(i))->AsCell()); } TRACE(broker, "Copied " << exports_.size() << " exports"); } class CellData : public HeapObjectData { public: CellData(JSHeapBroker* broker, ObjectData** storage, Handle object); void Serialize(JSHeapBroker* broker); ObjectData* value() { return value_; } private: ObjectData* value_ = nullptr; }; CellData::CellData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object) {} void CellData::Serialize(JSHeapBroker* broker) { if (value_ != nullptr) return; TraceScope tracer(broker, this, "CellData::Serialize"); auto cell = Handle::cast(object()); value_ = broker->GetOrCreateData(cell->value()); } class JSGlobalObjectData : public JSObjectData { public: JSGlobalObjectData(JSHeapBroker* broker, ObjectData** storage, Handle object); bool IsDetached() const { return is_detached_; } PropertyCellData* GetPropertyCell( JSHeapBroker* broker, NameData* 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> properties_; }; JSGlobalObjectData::JSGlobalObjectData(JSHeapBroker* broker, ObjectData** storage, Handle object) : JSObjectData(broker, storage, object), is_detached_(object->IsDetached()), properties_(broker->zone()) {} class JSGlobalProxyData : public JSObjectData { public: JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage, Handle object); }; JSGlobalProxyData::JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage, Handle object) : JSObjectData(broker, storage, object) {} namespace { base::Optional GetPropertyCellFromHeap(JSHeapBroker* broker, Handle 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()->IsJSGlobalObject()) { return PropertyCellRef(broker, it.GetPropertyCell()); } return base::nullopt; } } // namespace PropertyCellData* JSGlobalObjectData::GetPropertyCell( JSHeapBroker* broker, NameData* 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; } PropertyCellData* result = nullptr; base::Optional cell = GetPropertyCellFromHeap(broker, Handle::cast(name->object())); if (cell.has_value()) { cell->Serialize(); result = cell->data()->AsPropertyCell(); } properties_.push_back({name, result}); return result; } class TemplateObjectDescriptionData : public HeapObjectData { public: TemplateObjectDescriptionData(JSHeapBroker* broker, ObjectData** storage, Handle object) : HeapObjectData(broker, storage, object) {} }; class CodeData : public HeapObjectData { public: CodeData(JSHeapBroker* broker, ObjectData** storage, Handle 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_AND_AS(Name) \ bool ObjectData::Is##Name() const { \ if (should_access_heap()) { \ AllowHandleDereferenceIf allow_handle_dereference(kind()); \ return object()->Is##Name(); \ } \ if (is_smi()) return false; \ InstanceType instance_type = \ static_cast(this)->GetMapInstanceType(); \ return InstanceTypeChecker::Is##Name(instance_type); \ } \ Name##Data* ObjectData::As##Name() { \ CHECK(Is##Name()); \ return static_cast(this); \ } HEAP_BROKER_OBJECT_LIST(DEFINE_IS_AND_AS) #undef DEFINE_IS_AND_AS const JSObjectField& JSObjectData::GetInobjectField(int property_index) const { CHECK_LT(static_cast(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 boilerplate = Handle::cast(object()); Handle 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 = Handle::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 = Handle::cast(object()); DCHECK_NULL(backpointer_); DCHECK(!map->IsContextMap()); backpointer_ = broker->GetOrCreateData(map->GetBackPointer())->AsHeapObject(); } void MapData::SerializePrototype(JSHeapBroker* broker) { if (serialized_prototype_) return; serialized_prototype_ = true; TraceScope tracer(broker, this, "MapData::SerializePrototype"); Handle map = Handle::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 = Handle::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 = Handle::cast(object()); if (instance_descriptors_ == nullptr) { instance_descriptors_ = broker->GetOrCreateData(map->instance_descriptors()) ->AsDescriptorArray(); } ZoneMap& 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::cast(instance_descriptors_->object()); CHECK_EQ(*descriptors, map->instance_descriptors()); PropertyDescriptor d; d.key = broker->GetOrCreateData(descriptors->GetKey(descriptor_index))->AsName(); MaybeObject value = descriptors->GetValue(descriptor_index); HeapObject obj; if (value.GetHeapObjectIfStrong(&obj)) { d.value = broker->GetOrCreateData(handle(obj, broker->isolate())); } d.details = descriptors->GetDetails(descriptor_index); if (d.details.location() == kField) { d.field_index = FieldIndex::ForDescriptor(*map, descriptor_index); d.field_owner = broker->GetOrCreateData(map->FindFieldOwner(isolate, descriptor_index)) ->AsMap(); d.field_type = broker->GetOrCreateData(descriptors->GetFieldType(descriptor_index)); d.is_unboxed_double_field = map->IsUnboxedDoubleField(d.field_index); } contents[descriptor_index.as_int()] = d; if (d.details.location() == kField) { // Recurse on the owner map. d.field_owner->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 = Handle::cast(object()); DCHECK_NULL(root_map_); root_map_ = broker->GetOrCreateData(map->FindRootMap(broker->isolate()))->AsMap(); } MapData* 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 boilerplate = Handle::cast(object()); // We only serialize boilerplates that pass the IsInlinableFastLiteral // check, so we only do a sanity check on the depth here. CHECK_GT(depth, 0); CHECK(!boilerplate->map().is_deprecated()); // Serialize the elements. Isolate* const isolate = broker->isolate(); Handle 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::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 fast_elements = Handle::cast(elements_object); int length = elements_object->length(); for (int i = 0; i < length; i++) { Handle value(fast_elements->get(i), isolate); if (value->IsJSObject()) { ObjectData* value_data = broker->GetOrCreateData(value); value_data->AsJSObject()->SerializeRecursiveAsBoilerplate(broker, depth - 1); } } } else { CHECK(boilerplate->HasDoubleElements()); CHECK_LE(elements_object->Size(), kMaxRegularHeapObjectSize); elements_->AsFixedDoubleArray()->SerializeContents(broker); } // TODO(turbofan): Do we want to support out-of-object properties? CHECK(boilerplate->HasFastProperties() && boilerplate->property_array().length() == 0); CHECK_EQ(inobject_fields_.size(), 0u); // Check the in-object properties. Handle descriptors(boilerplate->map().instance_descriptors(), 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(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 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->IsJSObject()) { 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 boilerplate = Handle::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()) { AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf 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(), handle(current, broker()->isolate())); } ContextData* current = this->data()->AsContext(); return ContextRef(broker(), current->previous(broker(), depth, policy)); } base::Optional ContextRef::get(int index, SerializationPolicy policy) const { if (data_->should_access_heap()) { AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); Handle 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; } JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone, bool tracing_enabled, bool is_concurrent_inlining, bool is_native_context_independent) : isolate_(isolate), zone_(broker_zone), refs_(new (zone()) RefsMap(kMinimalRefsBucketCount, AddressMatcher(), zone())), root_index_map_(isolate), array_and_object_prototypes_(zone()), tracing_enabled_(tracing_enabled), is_concurrent_inlining_(is_concurrent_inlining), is_native_context_independent_(is_native_context_independent), feedback_(zone()), bytecode_analyses_(zone()), 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"); } 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::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::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 } bool JSHeapBroker::SerializingAllowed() const { return mode() == kSerializing; } void JSHeapBroker::SetTargetNativeContextRef( Handle 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 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_ = new (zone()) RefsMap(kInitialRefsBucketCount, AddressMatcher(), zone()); SetTargetNativeContextRef(native_context); target_native_context().Serialize(); CollectArrayAndObjectPrototypes(); // Serialize Cells Factory* const f = isolate()->factory(); GetOrCreateData(f->array_buffer_detaching_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->array_constructor_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->array_iterator_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->array_species_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->many_closures_cell())->AsFeedbackCell(); GetOrCreateData(f->no_elements_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->promise_hook_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->promise_species_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->promise_then_protector()) ->AsPropertyCell() ->Serialize(this); GetOrCreateData(f->string_length_protector()) ->AsPropertyCell() ->Serialize(this); // - CEntry stub GetOrCreateData( CodeFactory::CEntry(isolate(), 1, kDontSaveFPRegs, kArgvOnStack, true)); TRACE(this, "Finished serializing standard objects"); } // clang-format off ObjectData* JSHeapBroker::GetOrCreateData(Handle object) { RefsMap::Entry* entry = refs_->LookupOrInsert(object.address(), zone()); ObjectData** data_storage = &(entry->value); if (*data_storage == nullptr) { // TODO(neis): Remove these Allow* once we serialize everything upfront. AllowHandleDereference handle_dereference; if (object->IsSmi()) { new (zone()) ObjectData(this, data_storage, object, kSmi); } else if (IsReadOnlyHeapObject(*object)) { new (zone()) ObjectData(this, data_storage, object, kUnserializedReadOnlyHeapObject); #define CREATE_DATA_IF_MATCH(name) \ } else if (object->Is##name()) { \ CHECK(SerializingAllowed()); \ AllowHandleAllocation handle_allocation; \ new (zone()) name##Data(this, data_storage, Handle::cast(object)); HEAP_BROKER_OBJECT_LIST(CREATE_DATA_IF_MATCH) #undef CREATE_DATA_IF_MATCH } else { UNREACHABLE(); } } CHECK_NOT_NULL(*data_storage); return (*data_storage); } // clang-format on ObjectData* JSHeapBroker::GetOrCreateData(Object object) { return GetOrCreateData(handle(object, isolate())); } #define DEFINE_IS_AND_AS(Name) \ bool ObjectRef::Is##Name() const { return data()->Is##Name(); } \ Name##Ref ObjectRef::As##Name() const { \ DCHECK(Is##Name()); \ return Name##Ref(broker(), data()); \ } HEAP_BROKER_OBJECT_LIST(DEFINE_IS_AND_AS) #undef DEFINE_IS_AND_AS bool ObjectRef::IsSmi() const { return data()->is_smi(); } int ObjectRef::AsSmi() const { DCHECK(IsSmi()); // Handle-dereference is always allowed for Handle. return Handle::cast(object())->value(); } base::Optional JSObjectRef::GetObjectCreateMap() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHeapAllocationIf allow_heap_allocation(data()->kind(), broker()->mode()); Handle instance_map; if (Map::TryGetObjectCreateMap(broker()->isolate(), object()) .ToHandle(&instance_map)) { return MapRef(broker(), instance_map); } else { return base::Optional(); } } ObjectData* map_data = data()->AsJSObject()->object_create_map(broker()); if (map_data == nullptr) return base::Optional(); 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::AsElementsKind(ElementsKind kind) const { if (data_->should_access_heap()) { AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHeapAllocationIf allow_heap_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return MapRef(broker(), Map::AsElementsKind(broker()->isolate(), object(), kind)); } if (kind == elements_kind()) return *this; const ZoneVector& 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(); } void MapRef::SerializeForElementLoad() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); if (data()->kind() == ObjectDataKind::kUnserializedReadOnlyHeapObject) return; data()->AsMap()->SerializeForElementLoad(broker()); } void MapRef::SerializeForElementStore() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); if (data()->kind() == ObjectDataKind::kUnserializedReadOnlyHeapObject) return; 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* 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* 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()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHandleAllocationIf 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()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHandleAllocationIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHandleAllocationIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return ObjectRef(broker(), handle(object()->RawFastPropertyAt(index), broker()->isolate())); } 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHeapAllocationIf allow_heap_allocation( data()->kind(), broker()->mode()); // For TryMigrateInstance. AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return IsInlinableFastLiteral( handle(object()->boilerplate(), broker()->isolate())); } return data()->AsAllocationSite()->IsFastLiteral(); } void AllocationSiteRef::SerializeBoilerplate() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsAllocationSite()->SerializeBoilerplate(broker()); } void JSObjectRef::SerializeElements() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsJSObject()->SerializeElements(broker()); } void JSObjectRef::EnsureElementsTenured() { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHeapAllocationIf allow_heap_allocation(data()->kind(), broker()->mode()); Handle 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::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()) { AllowHandleDereferenceIf 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()) { AllowHandleDereferenceIf 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()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->instance_descriptors().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()) { AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return NameRef( broker(), handle(object()->instance_descriptors().GetKey(descriptor_index), broker()->isolate())); } DescriptorArrayData* descriptors = data()->AsMap()->instance_descriptors(); return NameRef(broker(), descriptors->contents().at(descriptor_index.as_int()).key); } bool MapRef::IsFixedCowArrayMap() const { Handle 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()) { AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); Handle 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()) { AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); Handle field_type( object()->instance_descriptors().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()) { AllowHandleDereferenceIf 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()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->Get(0); } return data()->AsString()->first_char(); } base::Optional StringRef::ToNumber() { if (data_->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHeapAllocationIf allow_heap_allocation(data()->kind(), broker()->mode()); int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY; return StringToDouble(broker()->isolate(), object(), flags); } return data()->AsString()->to_number(); } int ArrayBoilerplateDescriptionRef::constants_elements_length() const { if (data_->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->constant_elements().length(); } return data()->AsArrayBoilerplateDescription()->constants_elements_length(); } int ObjectBoilerplateDescriptionRef::size() const { if (data_->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->size(); } return data()->AsObjectBoilerplateDescription()->size(); } ObjectRef FixedArrayRef::get(int i) const { if (data_->should_access_heap()) { AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return ObjectRef(broker(), handle(object()->get(i), broker()->isolate())); } return ObjectRef(broker(), data()->AsFixedArray()->Get(i)); } bool FixedDoubleArrayRef::is_the_hole(int i) const { if (data_->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->is_the_hole(i); } return data()->AsFixedDoubleArray()->Get(i).is_hole_nan(); } double FixedDoubleArrayRef::get_scalar(int i) const { if (data_->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->get_scalar(i); } CHECK(!data()->AsFixedDoubleArray()->Get(i).is_hole_nan()); return data()->AsFixedDoubleArray()->Get(i).get_scalar(); } uint8_t BytecodeArrayRef::get(int index) const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->get(index); } return data()->AsBytecodeArray()->get(index); } Address BytecodeArrayRef::GetFirstBytecodeAddress() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->GetFirstBytecodeAddress(); } return data()->AsBytecodeArray()->GetFirstBytecodeAddress(); } Handle BytecodeArrayRef::GetConstantAtIndex(int index) const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return handle(object()->constant_pool().get(index), broker()->isolate()); } return data()->AsBytecodeArray()->GetConstantAtIndex(index, broker()->isolate()); } bool BytecodeArrayRef::IsConstantAtIndexSmi(int index) const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return Smi::cast(object()->constant_pool().get(index)); } return data()->AsBytecodeArray()->GetConstantAtIndexAsSmi(index); } void BytecodeArrayRef::SerializeForCompilation() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (data_->should_access_heap()) return; data()->AsBytecodeArray()->SerializeForCompilation(broker()); } const byte* BytecodeArrayRef::source_positions_address() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->SourcePositionTableIfCollected().GetDataStartAddress(); } return data()->AsBytecodeArray()->source_positions_address(); } int BytecodeArrayRef::source_positions_size() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->SourcePositionTableIfCollected().length(); } return static_cast(data()->AsBytecodeArray()->source_positions_size()); } Address BytecodeArrayRef::handler_table_address() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return reinterpret_cast
( object()->handler_table().GetDataStartAddress()); } return data()->AsBytecodeArray()->handler_table_address(); } int BytecodeArrayRef::handler_table_size() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->handler_table().length(); } return data()->AsBytecodeArray()->handler_table_size(); } Handle JSHeapBroker::GetRootHandle(Object object) { RootIndex root_index; CHECK(root_index_map().Lookup(object.ptr(), &root_index)); return Handle(isolate()->root_handle(root_index).location()); } #define IF_ACCESS_FROM_HEAP_C(holder, name) \ if (data_->should_access_heap()) { \ CHECK(broker()->mode() == JSHeapBroker::kDisabled || \ ReadOnlyHeap::Contains(HeapObject::cast(*object()))); \ AllowHandleAllocationIf handle_allocation(data_->kind(), \ broker()->mode()); \ AllowHandleDereferenceIf allow_handle_dereference(data_->kind(), \ broker()->mode()); \ return object()->name(); \ } #define IF_ACCESS_FROM_HEAP(holder, result, name) \ if (data_->kind() == ObjectDataKind::kUnserializedHeapObject) { \ AllowHandleAllocationIf handle_allocation(data_->kind(), \ broker()->mode()); \ AllowHandleDereferenceIf handle_dereference(data_->kind(), \ broker()->mode()); \ return result##Ref(broker(), \ handle(object()->name(), broker()->isolate())); \ } else if (data_->kind() == \ ObjectDataKind::kUnserializedReadOnlyHeapObject) { \ AllowHandleDereferenceIf handle_dereference(data_->kind(), \ broker()->mode()); \ return result##Ref(broker(), broker()->GetRootHandle(object()->name())); \ } // Macros for definining a const getter that, depending on the broker mode, // either looks into the handle or into the serialized data. #define BIMODAL_ACCESSOR(holder, result, name) \ result##Ref holder##Ref::name() const { \ IF_ACCESS_FROM_HEAP(holder, result, name); \ ObjectData* data = ObjectRef::data()->As##holder()->name(); \ if (data->kind() == ObjectDataKind::kUnserializedHeapObject) { \ return result##Ref(broker(), data->object()); \ } else { \ 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(holder, name); \ return ObjectRef::data()->As##holder()->name(); \ } // Like above but for BitFields. #define BIMODAL_ACCESSOR_B(holder, field, name, BitField) \ typename BitField::FieldType holder##Ref::name() const { \ IF_ACCESS_FROM_HEAP_C(holder, name); \ return BitField::decode(ObjectRef::data()->As##holder()->field()); \ } BIMODAL_ACCESSOR(AllocationSite, Object, nested_site) BIMODAL_ACCESSOR_C(AllocationSite, bool, CanInlineCall) BIMODAL_ACCESSOR_C(AllocationSite, bool, PointsToLiteral) BIMODAL_ACCESSOR_C(AllocationSite, ElementsKind, GetElementsKind) BIMODAL_ACCESSOR_C(AllocationSite, AllocationType, GetAllocationType) 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(Cell, Object, value) BIMODAL_ACCESSOR_C(FeedbackVector, double, invocation_count) BIMODAL_ACCESSOR(HeapObject, Map, map) 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(JSDataView, size_t, byte_offset) 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, IsOptimized) 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, 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(PropertyCell, Object, value) BIMODAL_ACCESSOR_C(PropertyCell, PropertyDetails, property_details) base::Optional FunctionTemplateInfoRef::call_code() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); return CallHandlerInfoRef( broker(), handle(object()->call_code(), broker()->isolate())); } CallHandlerInfoData* 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHandleAllocationIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHandleAllocationIf 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); CallOptimization call_optimization(broker()->isolate(), object()); Handle 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 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()->AsMap()); 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()->AsMap(), not_found}); return not_found; } HolderLookupResult result; CallOptimization call_optimization(broker()->isolate(), object()); Handle 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()->AsMap(), result}); break; } default: { DCHECK_EQ(result.holder, base::nullopt); fti_data->known_receivers().insert( {receiver_map.data()->AsMap(), result}); } } return result; } BIMODAL_ACCESSOR(CallHandlerInfo, Object, data) BIMODAL_ACCESSOR_C(SharedFunctionInfo, int, builtin_id) BIMODAL_ACCESSOR(SharedFunctionInfo, BytecodeArray, GetBytecodeArray) #define DEF_SFI_ACCESSOR(type, name) \ BIMODAL_ACCESSOR_C(SharedFunctionInfo, type, name) BROKER_SFI_FIELDS(DEF_SFI_ACCESSOR) #undef DEF_SFI_ACCESSOR BIMODAL_ACCESSOR_C(String, int, length) BIMODAL_ACCESSOR(FeedbackCell, HeapObject, value) base::Optional MapRef::GetStrongValue( InternalIndex descriptor_index) const { if (data_->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); MaybeObject value = object()->instance_descriptors().GetValue(descriptor_index); HeapObject object; if (value.GetHeapObjectIfStrong(&object)) { return ObjectRef(broker(), handle(object, broker()->isolate())); } 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::FindRootMap() const { if (data_->kind() == ObjectDataKind::kUnserializedHeapObject) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return MapRef(broker(), handle(object()->FindRootMap(broker()->isolate()), broker()->isolate())); } else if (data_->kind() == ObjectDataKind::kUnserializedReadOnlyHeapObject) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return MapRef(broker(), broker()->GetRootHandle( object()->FindRootMap(broker()->isolate()))); } MapData* map_data = data()->AsMap()->FindRootMap(); if (map_data) { 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()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->DataPtr(); } return data()->AsJSTypedArray()->data_ptr(); } bool MapRef::IsInobjectSlackTrackingInProgress() const { IF_ACCESS_FROM_HEAP_C(Map, IsInobjectSlackTrackingInProgress); return Map::Bits3::ConstructionCounterBits::decode( data()->AsMap()->bit_field3()) != Map::kNoSlackTracking; } int MapRef::constructor_function_index() const { IF_ACCESS_FROM_HEAP_C(Map, GetConstructorFunctionIndex); CHECK(IsPrimitiveMap()); return data()->AsMap()->constructor_function_index(); } bool MapRef::is_stable() const { IF_ACCESS_FROM_HEAP_C(Map, is_stable); return !Map::Bits3::IsUnstableBit::decode(data()->AsMap()->bit_field3()); } bool MapRef::CanBeDeprecated() const { IF_ACCESS_FROM_HEAP_C(Map, CanBeDeprecated); CHECK_GT(NumberOfOwnDescriptors(), 0); return data()->AsMap()->can_be_deprecated(); } bool MapRef::CanTransition() const { IF_ACCESS_FROM_HEAP_C(Map, CanTransition); return data()->AsMap()->can_transition(); } int MapRef::GetInObjectPropertiesStartInWords() const { IF_ACCESS_FROM_HEAP_C(Map, GetInObjectPropertiesStartInWords); return data()->AsMap()->in_object_properties_start_in_words(); } int MapRef::GetInObjectProperties() const { IF_ACCESS_FROM_HEAP_C(Map, GetInObjectProperties); return data()->AsMap()->in_object_properties(); } int ScopeInfoRef::ContextLength() const { IF_ACCESS_FROM_HEAP_C(ScopeInfo, ContextLength); return data()->AsScopeInfo()->context_length(); } int ScopeInfoRef::Flags() const { IF_ACCESS_FROM_HEAP_C(ScopeInfo, Flags); return data()->AsScopeInfo()->flags(); } bool ScopeInfoRef::HasContextExtension() const { return ScopeInfo::HasContextExtensionSlotBit::decode(Flags()); } bool ScopeInfoRef::HasOuterScopeInfo() const { IF_ACCESS_FROM_HEAP_C(ScopeInfo, HasOuterScopeInfo); return data()->AsScopeInfo()->has_outer_scope_info(); } ScopeInfoRef ScopeInfoRef::OuterScopeInfo() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return ScopeInfoRef( broker(), handle(object()->OuterScopeInfo(), broker()->isolate())); } return ScopeInfoRef(broker(), data()->AsScopeInfo()->outer_scope_info()); } void ScopeInfoRef::SerializeScopeInfoChain() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsScopeInfo()->SerializeScopeInfoChain(broker()); } bool StringRef::IsExternalString() const { IF_ACCESS_FROM_HEAP_C(String, IsExternalString); return data()->AsString()->is_external_string(); } Address CallHandlerInfoRef::callback() const { if (data_->should_access_heap()) { return v8::ToCData
(object()->callback()); } return HeapObjectRef::data()->AsCallHandlerInfo()->callback(); } Address FunctionTemplateInfoRef::c_function() const { if (broker()->mode() == JSHeapBroker::kDisabled) { return v8::ToCData
(object()->GetCFunction()); } return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_function(); } const CFunctionInfo* FunctionTemplateInfoRef::c_signature() const { if (broker()->mode() == JSHeapBroker::kDisabled) { return v8::ToCData(object()->GetCSignature()); } return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_signature(); } bool StringRef::IsSeqString() const { IF_ACCESS_FROM_HEAP_C(String, IsSeqString); return data()->AsString()->is_seq_string(); } ScopeInfoRef NativeContextRef::scope_info() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return ScopeInfoRef(broker(), handle(object()->scope_info(), broker()->isolate())); } return ScopeInfoRef(broker(), data()->AsNativeContext()->scope_info()); } SharedFunctionInfoRef FeedbackVectorRef::shared_function_info() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return SharedFunctionInfoRef( broker(), handle(object()->shared_function_info(), broker()->isolate())); } 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 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::BooleanValue() const { if (data_->should_access_heap()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return object()->BooleanValue(broker()->isolate()); } return IsSmi() ? (AsSmi() != 0) : data()->AsHeapObject()->boolean_value(); } Maybe 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::quiet_NaN()); break; } case OddballType::kNull: { return Just(0.0); break; } default: { return Nothing(); break; } } } base::Optional ObjectRef::GetOwnConstantElement( uint32_t index, SerializationPolicy policy) const { if (data_->kind() == ObjectDataKind::kUnserializedHeapObject) { return (IsJSObject() || IsString()) ? GetOwnElementFromHeap(broker(), object(), index, true) : base::nullopt; } else if (data_->kind() == ObjectDataKind::kUnserializedReadOnlyHeapObject) { DCHECK(!IsJSObject()); // TODO(mythria): For ReadOnly strings, currently we cannot access data from // heap without creating handles since we use LookupIterator. We should have // a custom implementation for read only strings that doesn't create // handles. Till then it is OK to disable this optimization since this only // impacts keyed accesses on read only strings. return base::nullopt; } 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 JSObjectRef::GetOwnDataProperty( Representation field_representation, FieldIndex index, SerializationPolicy policy) const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); return GetOwnDataPropertyFromHeap(broker(), Handle::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 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); } double HeapNumberRef::value() const { IF_ACCESS_FROM_HEAP_C(HeapNumber, value); return data()->AsHeapNumber()->value(); } uint64_t BigIntRef::AsUint64() const { IF_ACCESS_FROM_HEAP_C(BigInt, AsUint64); return data()->AsBigInt()->AsUint64(); } base::Optional SourceTextModuleRef::GetCell(int cell_index) const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return CellRef(broker(), handle(object()->GetCell(cell_index), broker()->isolate())); } CellData* cell = data()->AsSourceTextModule()->GetCell(broker(), cell_index); if (cell == nullptr) return base::nullopt; return CellRef(broker(), cell); } ObjectRef::ObjectRef(JSHeapBroker* broker, Handle 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(), broker->zone()); ObjectData** storage = &(entry->value); if (*storage == nullptr) { AllowHandleDereferenceIf allow_handle_dereference( kUnserializedHeapObject, broker->mode()); entry->value = new (broker->zone()) 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. AllowHandleDereferenceIf 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()) { AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); Map map = Handle::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 AllocationSiteRef::boilerplate() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return JSObjectRef(broker(), handle(object()->boilerplate(), broker()->isolate())); } JSObjectData* 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return FixedArrayBaseRef(broker(), handle(object()->elements(), broker()->isolate())); } ObjectData* elements_data = data()->AsJSObject()->elements(); if (elements_data->kind() == ObjectDataKind::kUnserializedReadOnlyHeapObject) { return FixedArrayBaseRef(broker(), elements_data->object()); } return FixedArrayBaseRef(broker(), elements_data->AsFixedArrayBase()); } int FixedArrayBaseRef::length() const { IF_ACCESS_FROM_HEAP_C(FixedArrayBase, length); return data()->AsFixedArrayBase()->length(); } ObjectData* FixedArrayData::Get(int i) const { CHECK_LT(i, static_cast(contents_.size())); CHECK_NOT_NULL(contents_[i]); return contents_[i]; } Float64 FixedDoubleArrayData::Get(int i) const { CHECK_LT(i, static_cast(contents_.size())); return contents_[i]; } base::Optional 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() { data()->AsFeedbackVector()->Serialize(broker()); } bool FeedbackVectorRef::serialized() const { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); 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(JSRegExp, Object, data); return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->data()); } ObjectRef JSRegExpRef::flags() const { IF_ACCESS_FROM_HEAP(JSRegExp, Object, flags); return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->flags()); } ObjectRef JSRegExpRef::last_index() const { IF_ACCESS_FROM_HEAP(JSRegExp, Object, last_index); return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->last_index()); } ObjectRef JSRegExpRef::raw_properties_or_hash() const { IF_ACCESS_FROM_HEAP(JSRegExp, Object, raw_properties_or_hash); return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->raw_properties_or_hash()); } ObjectRef JSRegExpRef::source() const { IF_ACCESS_FROM_HEAP(JSRegExp, Object, source); return ObjectRef(broker(), ObjectRef::data()->AsJSRegExp()->source()); } void JSRegExpRef::SerializeAsRegExpBoilerplate() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); JSObjectRef::data()->AsJSRegExp()->SerializeAsRegExpBoilerplate(broker()); } Handle 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##Ref::object() const { \ if (broker()->mode() == JSHeapBroker::kSerialized && \ data_->used_status == ObjectData::Usage::kUnused) { \ data_->used_status = ObjectData::Usage::kOnlyIdentityUsed; \ } \ return Handle(reinterpret_cast(data_->object().address())); \ } #else #define DEF_OBJECT_GETTER(T) \ Handle T##Ref::object() const { \ return Handle(reinterpret_cast(data_->object().address())); \ } #endif // DEBUG HEAP_BROKER_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 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 context = Handle::cast(object()); #define SERIALIZE_MEMBER(type, name) \ DCHECK_NULL(name##_); \ name##_ = broker->GetOrCreateData(context->name())->As##type(); \ 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 bound_function_with_constructor_map_->SerializePrototype(broker); bound_function_without_constructor_map_->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))->AsMap()); } scope_info_ = broker->GetOrCreateData(context->scope_info())->AsScopeInfo(); } void JSFunctionRef::Serialize() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (data_->should_access_heap()) return; CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsJSFunction()->Serialize(broker()); } bool JSBoundFunctionRef::serialized() const { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (data_->should_access_heap()) return true; return data()->AsJSBoundFunction()->serialized(); } bool JSFunctionRef::serialized() const { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); 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()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); Handle template_object = TemplateObjectDescription::GetTemplateObject( isolate(), broker()->target_native_context().object(), description.object(), object(), source.slot.ToInt()); return JSArrayRef(broker(), template_object); } JSArrayData* array = data()->AsSharedFunctionInfo()->GetTemplateObject(source.slot); if (array != nullptr) return JSArrayRef(broker(), array); CHECK_EQ(policy, SerializationPolicy::kSerializeIfNeeded); CHECK(broker()->SerializingAllowed()); Handle template_object = TemplateObjectDescription::GetTemplateObject( broker()->isolate(), broker()->target_native_context().object(), description.object(), object(), source.slot.ToInt()); array = broker()->GetOrCreateData(template_object)->AsJSArray(); data()->AsSharedFunctionInfo()->SetTemplateObject(source.slot, array); return JSArrayRef(broker(), array); } void SharedFunctionInfoRef::SerializeFunctionTemplateInfo() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsSharedFunctionInfo()->SerializeFunctionTemplateInfo(broker()); } void SharedFunctionInfoRef::SerializeScopeInfoChain() { CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsSharedFunctionInfo()->SerializeScopeInfoChain(broker()); } base::Optional SharedFunctionInfoRef::function_template_info() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (object()->IsApiFunction()) { return FunctionTemplateInfoRef( broker(), handle(object()->function_data(), broker()->isolate())); } return base::nullopt; } FunctionTemplateInfoData* 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(SharedFunctionInfo, scope_info().ContextHeaderLength); return data()->AsSharedFunctionInfo()->context_header_size(); } ScopeInfoRef SharedFunctionInfoRef::scope_info() const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); AllowHandleAllocationIf allow_handle_allocation(data()->kind(), broker()->mode()); AllowHandleDereferenceIf allow_handle_dereference(data()->kind(), broker()->mode()); return ScopeInfoRef(broker(), handle(object()->scope_info(), broker()->isolate())); } return ScopeInfoRef(broker(), data()->AsSharedFunctionInfo()->scope_info()); } void JSObjectRef::SerializeObjectCreateMap() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); 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() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (data_->should_access_heap()) return; CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsSourceTextModule()->Serialize(broker()); } void NativeContextRef::Serialize() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); 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(); } void JSBoundFunctionRef::Serialize() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (data_->should_access_heap()) return; CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsJSBoundFunction()->Serialize(broker()); } void PropertyCellRef::Serialize() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (data_->should_access_heap()) return; CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsPropertyCell()->Serialize(broker()); } void FunctionTemplateInfoRef::SerializeCallCode() { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); if (data_->should_access_heap()) return; CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); data()->AsFunctionTemplateInfo()->SerializeCallCode(broker()); } base::Optional JSGlobalObjectRef::GetPropertyCell( NameRef const& name, SerializationPolicy policy) const { if (data_->should_access_heap()) { DCHECK(data_->kind() != ObjectDataKind::kUnserializedReadOnlyHeapObject); return GetPropertyCellFromHeap(broker(), name.object()); } PropertyCellData* property_cell_data = data()->AsJSGlobalObject()->GetPropertyCell( broker(), name.data()->AsName(), 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 const& ElementAccessFeedback::transition_groups() const { return transition_groups_; } ElementAccessFeedback const& ElementAccessFeedback::Refine( ZoneVector> const& inferred_maps, Zone* zone) const { ElementAccessFeedback& refined_feedback = *new (zone) ElementAccessFeedback(zone, keyed_mode(), slot_kind()); if (inferred_maps.empty()) return refined_feedback; ZoneUnorderedSet, Handle::hash, Handle::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 source = group[i]; if (inferred.find(source) != inferred.end()) { new_group.push_back(source); } } Handle 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), 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 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 : group) { if (!MapRef(broker, map).IsStringMap()) return false; } } return true; } NamedAccessFeedback::NamedAccessFeedback(NameRef const& name, ZoneVector> 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); return nexus.kind(); } bool JSHeapBroker::FeedbackIsInsufficient(FeedbackSource const& source) const { return is_concurrent_inlining_ ? GetFeedback(source).IsInsufficient() : FeedbackNexus(source.vector, source.slot).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 = *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); } } // namespace ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess( FeedbackSource const& source, AccessMode mode, base::Optional static_name) { FeedbackNexus nexus(source.vector, source.slot); FeedbackSlotKind kind = nexus.kind(); if (nexus.IsUninitialized()) return *new (zone()) InsufficientFeedback(kind); MapHandles maps; nexus.ExtractMaps(&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 *new (zone()) InsufficientFeedback(kind); } base::Optional name = static_name.has_value() ? static_name : GetNameFeedback(nexus); if (name.has_value()) { // We rely on this invariant in JSGenericLowering. DCHECK_IMPLIES(maps.empty(), nexus.ic_state() == MEGAMORPHIC); return *new (zone()) NamedAccessFeedback( *name, ZoneVector>(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 *new (zone()) 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 (nexus.IsUninitialized()) { return *new (zone()) InsufficientFeedback(nexus.kind()); } if (nexus.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) { return *new (zone()) GlobalAccessFeedback(nexus.kind()); } Handle 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 = 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 *new (zone()) 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::cast(feedback_value)); cell.Serialize(); return *new (zone()) GlobalAccessFeedback(cell, nexus.kind()); } BinaryOperationHint JSHeapBroker::ReadFeedbackForBinaryOperation( FeedbackSource const& source) const { return FeedbackNexus(source.vector, source.slot).GetBinaryOperationFeedback(); } CompareOperationHint JSHeapBroker::ReadFeedbackForCompareOperation( FeedbackSource const& source) const { return FeedbackNexus(source.vector, source.slot) .GetCompareOperationFeedback(); } ForInHint JSHeapBroker::ReadFeedbackForForIn( FeedbackSource const& source) const { return FeedbackNexus(source.vector, source.slot).GetForInFeedback(); } ProcessedFeedback const& JSHeapBroker::ReadFeedbackForInstanceOf( FeedbackSource const& source) { FeedbackNexus nexus(source.vector, source.slot); if (nexus.IsUninitialized()) return *new (zone()) InsufficientFeedback(nexus.kind()); base::Optional optional_constructor; { MaybeHandle maybe_constructor = nexus.GetConstructorFeedback(); Handle constructor; if (maybe_constructor.ToHandle(&constructor)) { optional_constructor = JSObjectRef(this, constructor); } } return *new (zone()) InstanceOfFeedback(optional_constructor, nexus.kind()); } ProcessedFeedback const& JSHeapBroker::ReadFeedbackForArrayOrObjectLiteral( FeedbackSource const& source) { FeedbackNexus nexus(source.vector, source.slot); HeapObject object; if (nexus.IsUninitialized() || !nexus.GetFeedback()->GetHeapObject(&object)) { return *new (zone()) InsufficientFeedback(nexus.kind()); } AllocationSiteRef site(this, handle(object, isolate())); if (site.IsFastLiteral()) { site.SerializeBoilerplate(); } return *new (zone()) LiteralFeedback(site, nexus.kind()); } ProcessedFeedback const& JSHeapBroker::ReadFeedbackForRegExpLiteral( FeedbackSource const& source) { FeedbackNexus nexus(source.vector, source.slot); HeapObject object; if (nexus.IsUninitialized() || !nexus.GetFeedback()->GetHeapObject(&object)) { return *new (zone()) InsufficientFeedback(nexus.kind()); } JSRegExpRef regexp(this, handle(object, isolate())); regexp.SerializeAsRegExpBoilerplate(); return *new (zone()) RegExpLiteralFeedback(regexp, nexus.kind()); } ProcessedFeedback const& JSHeapBroker::ReadFeedbackForTemplateObject( FeedbackSource const& source) { FeedbackNexus nexus(source.vector, source.slot); HeapObject object; if (nexus.IsUninitialized() || !nexus.GetFeedback()->GetHeapObject(&object)) { return *new (zone()) InsufficientFeedback(nexus.kind()); } JSArrayRef array(this, handle(object, isolate())); return *new (zone()) TemplateObjectFeedback(array, nexus.kind()); } ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCall( FeedbackSource const& source) { FeedbackNexus nexus(source.vector, source.slot); if (nexus.IsUninitialized()) return *new (zone()) InsufficientFeedback(nexus.kind()); base::Optional 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 *new (zone()) 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 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); BinaryOperationHint hint = ReadFeedbackForBinaryOperation(source); ProcessedFeedback const* feedback; if (hint == BinaryOperationHint::kNone) { feedback = new (zone()) InsufficientFeedback(source.vector->GetKind(source.slot)); } else { feedback = new (zone()) BinaryOperationFeedback(hint, source.vector->GetKind(source.slot)); } SetFeedback(source, feedback); return *feedback; } ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForCompareOperation( FeedbackSource const& source) { if (HasFeedback(source)) return GetFeedback(source); CompareOperationHint hint = ReadFeedbackForCompareOperation(source); ProcessedFeedback const* feedback; if (hint == CompareOperationHint::kNone) { feedback = new (zone()) InsufficientFeedback(source.vector->GetKind(source.slot)); } else { feedback = new (zone()) CompareOperationFeedback(hint, source.vector->GetKind(source.slot)); } SetFeedback(source, feedback); return *feedback; } ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForForIn( FeedbackSource const& source) { if (HasFeedback(source)) return GetFeedback(source); ForInHint hint = ReadFeedbackForForIn(source); ProcessedFeedback const* feedback; if (hint == ForInHint::kNone) { feedback = new (zone()) InsufficientFeedback(source.vector->GetKind(source.slot)); } else { feedback = new (zone()) ForInFeedback(hint, source.vector->GetKind(source.slot)); } SetFeedback(source, feedback); return *feedback; } ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForPropertyAccess( FeedbackSource const& source, AccessMode mode, base::Optional 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 : 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, TransitionGroup, Handle::hash, Handle::equal_to> transition_groups(zone()); // Separate the actual receiver maps and the possible transition sources. for (Handle 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 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 = new (zone()) 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 : group) { int count = 0; for (TransitionGroup const& some_group : transition_groups()) { count += std::count_if( some_group.begin(), some_group.end(), [&](Handle some_map) { return some_map.equals(map); }); } CHECK_EQ(count, 1); } #endif } std::ostream& operator<<(std::ostream& os, const ObjectRef& ref) { if (ref.data_->kind() == ObjectDataKind::kUnserializedHeapObject || !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_->kind() == ObjectDataKind::kUnserializedReadOnlyHeapObject) { AllowHandleDereferenceIf allow_handle_dereference(ref.data()->kind(), ref.broker()->mode()); return os << ref.data() << " {" << ref.object() << "}"; } else { return os << ref.data(); } } base::Optional 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(SerializingAllowed()); TRACE(this, "Storing PropertyAccessInfo for " << access_mode << " of property " << name << " on map " << map); property_access_infos_.insert({target, access_info}); } return access_info; } BinaryOperationFeedback const& ProcessedFeedback::AsBinaryOperation() const { CHECK_EQ(kBinaryOperation, kind()); return *static_cast(this); } CallFeedback const& ProcessedFeedback::AsCall() const { CHECK_EQ(kCall, kind()); return *static_cast(this); } CompareOperationFeedback const& ProcessedFeedback::AsCompareOperation() const { CHECK_EQ(kCompareOperation, kind()); return *static_cast(this); } ElementAccessFeedback const& ProcessedFeedback::AsElementAccess() const { CHECK_EQ(kElementAccess, kind()); return *static_cast(this); } ForInFeedback const& ProcessedFeedback::AsForIn() const { CHECK_EQ(kForIn, kind()); return *static_cast(this); } GlobalAccessFeedback const& ProcessedFeedback::AsGlobalAccess() const { CHECK_EQ(kGlobalAccess, kind()); return *static_cast(this); } InstanceOfFeedback const& ProcessedFeedback::AsInstanceOf() const { CHECK_EQ(kInstanceOf, kind()); return *static_cast(this); } NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const { CHECK_EQ(kNamedAccess, kind()); return *static_cast(this); } LiteralFeedback const& ProcessedFeedback::AsLiteral() const { CHECK_EQ(kLiteral, kind()); return *static_cast(this); } RegExpLiteralFeedback const& ProcessedFeedback::AsRegExpLiteral() const { CHECK_EQ(kRegExpLiteral, kind()); return *static_cast(this); } TemplateObjectFeedback const& ProcessedFeedback::AsTemplateObject() const { CHECK_EQ(kTemplateObject, kind()); return *static_cast(this); } BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis( Handle 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 = new (zone()) 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; } 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 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