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