summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/heap-refs.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/heap-refs.cc')
-rw-r--r--deps/v8/src/compiler/heap-refs.cc4594
1 files changed, 4594 insertions, 0 deletions
diff --git a/deps/v8/src/compiler/heap-refs.cc b/deps/v8/src/compiler/heap-refs.cc
new file mode 100644
index 0000000000..7931407a7e
--- /dev/null
+++ b/deps/v8/src/compiler/heap-refs.cc
@@ -0,0 +1,4594 @@
+// Copyright 2021 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/heap-refs.h"
+
+#ifdef ENABLE_SLOW_DCHECKS
+#include <algorithm>
+#endif
+
+#include "src/api/api-inl.h"
+#include "src/ast/modules.h"
+#include "src/codegen/code-factory.h"
+#include "src/compiler/graph-reducer.h"
+#include "src/compiler/js-heap-broker.h"
+#include "src/execution/protectors-inl.h"
+#include "src/objects/descriptor-array.h"
+#include "src/objects/heap-number-inl.h"
+#include "src/objects/js-array-buffer-inl.h"
+#include "src/objects/property-cell.h"
+#include "src/objects/template-objects-inl.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+#define TRACE(broker, x) TRACE_BROKER(broker, x)
+#define TRACE_MISSING(broker, x) TRACE_BROKER_MISSING(broker, x)
+
+#define FORWARD_DECL(Name, ...) class Name##Data;
+HEAP_BROKER_OBJECT_LIST(FORWARD_DECL)
+#undef FORWARD_DECL
+
+// There are several 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.
+//
+// kBackgroundSerializedHeapObject: Like kSerializedHeapObject, but
+// allows serialization from the background thread.
+//
+// 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 --concurrent-inlining 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,
+ kBackgroundSerializedHeapObject,
+ kUnserializedHeapObject,
+ kNeverSerializedHeapObject,
+ kUnserializedReadOnlyHeapObject
+};
+
+namespace {
+
+bool IsReadOnlyHeapObjectForCompiler(HeapObject object) {
+ DisallowGarbageCollection no_gc;
+ // TODO(jgruber): Remove this compiler-specific predicate and use the plain
+ // heap predicate instead. This would involve removing the special cases for
+ // builtins.
+ 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)
+#ifdef DEBUG
+ ,
+ broker_(broker)
+#endif // DEBUG
+ {
+ // 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 || kind == kSmi ||
+ kind == kNeverSerializedHeapObject ||
+ kind == kBackgroundSerializedHeapObject);
+ CHECK_IMPLIES(kind == kUnserializedReadOnlyHeapObject,
+ object->IsHeapObject() && IsReadOnlyHeapObjectForCompiler(
+ HeapObject::cast(*object)));
+ }
+
+#define DECLARE_IS(Name, ...) bool Is##Name() const;
+ HEAP_BROKER_OBJECT_LIST(DECLARE_IS)
+#undef DECLARE_IS
+
+#define DECLARE_AS(Name, ...) Name##Data* As##Name();
+ HEAP_BROKER_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;
+ }
+ bool IsNull() const { return object_->IsNull(); }
+
+#ifdef DEBUG
+ enum class Usage{kUnused, kOnlyIdentityUsed, kDataUsed};
+ mutable Usage used_status = Usage::kUnused;
+
+ JSHeapBroker* broker() const { return broker_; }
+#endif // DEBUG
+
+ private:
+ Handle<Object> const object_;
+ ObjectDataKind const kind_;
+#ifdef DEBUG
+ JSHeapBroker* const broker_; // For DCHECKs.
+#endif // DEBUG
+};
+
+namespace {
+
+template <class T>
+constexpr bool IsSerializedRef() {
+ return ref_traits<T>::ref_serialization_kind ==
+ RefSerializationKind::kSerialized;
+}
+
+RefSerializationKind RefSerializationKindOf(ObjectData* const data) {
+ Object o = *data->object();
+ if (o.IsSmi()) {
+ return RefSerializationKind::kNeverSerialized;
+#define DEFINE_REF_SERIALIZATION_KIND(Name, Kind) \
+ } \
+ /* NOLINTNEXTLINE(readability/braces) */ \
+ else if (o.Is##Name()) { \
+ return ref_traits<Name>::ref_serialization_kind;
+ HEAP_BROKER_OBJECT_LIST(DEFINE_REF_SERIALIZATION_KIND)
+#undef DEFINE_REF_SERIALIZATION_KIND
+ }
+ UNREACHABLE();
+}
+
+} // namespace
+
+class HeapObjectData : public ObjectData {
+ public:
+ HeapObjectData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<HeapObject> object,
+ ObjectDataKind kind = ObjectDataKind::kSerializedHeapObject);
+
+ base::Optional<bool> TryGetBooleanValue(JSHeapBroker* broker) const;
+ ObjectData* map() const { return map_; }
+ InstanceType GetMapInstanceType() const;
+
+ private:
+ base::Optional<bool> TryGetBooleanValueImpl(JSHeapBroker* broker) const;
+
+ ObjectData* const map_;
+};
+
+class PropertyCellData : public HeapObjectData {
+ public:
+ PropertyCellData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<PropertyCell> object,
+ ObjectDataKind kind = ObjectDataKind::kSerializedHeapObject);
+
+ bool Serialize(JSHeapBroker* broker);
+
+ PropertyDetails property_details() const {
+ CHECK(serialized());
+ return property_details_;
+ }
+
+ ObjectData* value() const {
+ DCHECK(serialized());
+ return value_;
+ }
+
+ private:
+ PropertyDetails property_details_ = PropertyDetails::Empty();
+ ObjectData* value_ = nullptr;
+
+ bool serialized() const { return 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_; }
+ ZoneVector<Address> c_functions() const { return c_functions_; }
+ ZoneVector<const CFunctionInfo*> c_signatures() const {
+ return c_signatures_;
+ }
+ 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;
+ ZoneVector<Address> c_functions_;
+ ZoneVector<const CFunctionInfo*> c_signatures_;
+ 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;
+};
+
+namespace {
+
+ZoneVector<Address> GetCFunctions(FixedArray function_overloads, Zone* zone) {
+ const int len = function_overloads.length() /
+ FunctionTemplateInfo::kFunctionOverloadEntrySize;
+ ZoneVector<Address> c_functions = ZoneVector<Address>(len, zone);
+ for (int i = 0; i < len; i++) {
+ c_functions[i] = v8::ToCData<Address>(function_overloads.get(
+ FunctionTemplateInfo::kFunctionOverloadEntrySize * i));
+ }
+ return c_functions;
+}
+
+ZoneVector<const CFunctionInfo*> GetCSignatures(FixedArray function_overloads,
+ Zone* zone) {
+ const int len = function_overloads.length() /
+ FunctionTemplateInfo::kFunctionOverloadEntrySize;
+ ZoneVector<const CFunctionInfo*> c_signatures =
+ ZoneVector<const CFunctionInfo*>(len, zone);
+ for (int i = 0; i < len; i++) {
+ c_signatures[i] = v8::ToCData<const CFunctionInfo*>(function_overloads.get(
+ FunctionTemplateInfo::kFunctionOverloadEntrySize * i + 1));
+ }
+ return c_signatures;
+}
+
+} // namespace
+
+FunctionTemplateInfoData::FunctionTemplateInfoData(
+ JSHeapBroker* broker, ObjectData** storage,
+ Handle<FunctionTemplateInfo> object)
+ : HeapObjectData(broker, storage, object),
+ c_functions_(broker->zone()),
+ c_signatures_(broker->zone()),
+ known_receivers_(broker->zone()) {
+ DCHECK(!broker->is_concurrent_inlining());
+
+ auto function_template_info = Handle<FunctionTemplateInfo>::cast(object);
+
+ FixedArray function_overloads_array =
+ FixedArray::cast(function_template_info->GetCFunctionOverloads());
+ c_functions_ = GetCFunctions(function_overloads_array, broker->zone());
+ c_signatures_ = GetCSignatures(function_overloads_array, broker->zone());
+
+ is_signature_undefined_ =
+ function_template_info->signature().IsUndefined(broker->isolate());
+ accept_any_receiver_ = function_template_info->accept_any_receiver();
+
+ CallOptimization call_optimization(broker->local_isolate_or_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(!broker->is_concurrent_inlining());
+}
+
+PropertyCellData::PropertyCellData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<PropertyCell> object,
+ ObjectDataKind kind)
+ : HeapObjectData(broker, storage, object, kind) {}
+
+bool PropertyCellData::Serialize(JSHeapBroker* broker) {
+ if (serialized()) return true;
+
+ TraceScope tracer(broker, this, "PropertyCellData::Serialize");
+ auto cell = Handle<PropertyCell>::cast(object());
+
+ // While this code runs on a background thread, the property cell might
+ // undergo state transitions via calls to PropertyCell::Transition. These
+ // transitions follow a certain protocol on which we rely here to ensure that
+ // we only report success when we can guarantee consistent data. A key
+ // property is that after transitioning from cell type A to B (A != B), there
+ // will never be a transition back to A, unless A is kConstant and the new
+ // value is the hole (i.e. the property cell was invalidated, which is a final
+ // state).
+
+ PropertyDetails property_details = cell->property_details(kAcquireLoad);
+
+ Handle<Object> value =
+ broker->CanonicalPersistentHandle(cell->value(kAcquireLoad));
+ if (broker->ObjectMayBeUninitialized(value)) {
+ DCHECK(!broker->IsMainThread());
+ return false;
+ }
+
+ {
+ PropertyDetails property_details_again =
+ cell->property_details(kAcquireLoad);
+ if (property_details != property_details_again) {
+ DCHECK(!broker->IsMainThread());
+ return false;
+ }
+ }
+
+ if (property_details.cell_type() == PropertyCellType::kConstant) {
+ Handle<Object> value_again =
+ broker->CanonicalPersistentHandle(cell->value(kAcquireLoad));
+ if (*value != *value_again) {
+ DCHECK(!broker->IsMainThread());
+ return false;
+ }
+ }
+
+ ObjectData* value_data = broker->TryGetOrCreateData(value);
+ if (value_data == nullptr) {
+ DCHECK(!broker->IsMainThread());
+ return false;
+ }
+
+ PropertyCell::CheckDataIsCompatible(property_details, *value);
+
+ DCHECK(!serialized());
+ property_details_ = property_details;
+ value_ = value_data;
+ DCHECK(serialized());
+ return true;
+}
+
+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 JSReceiverData : public HeapObjectData {
+ public:
+ JSReceiverData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<JSReceiver> object, ObjectDataKind kind)
+ : HeapObjectData(broker, storage, object, kind) {}
+};
+
+class JSObjectData : public JSReceiverData {
+ public:
+ JSObjectData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<JSObject> object);
+
+ // Recursive serialization of all reachable JSObjects.
+ void SerializeAsBoilerplate(JSHeapBroker* broker);
+ ObjectData* 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* GetOwnFastDataProperty(
+ JSHeapBroker* broker, Representation representation,
+ FieldIndex field_index,
+ SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
+ ObjectData* GetOwnDictionaryProperty(JSHeapBroker* broker,
+ InternalIndex dict_index,
+ SerializationPolicy policy);
+
+ // 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<ObjectData*> 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_.
+ // For fast mode objects, the keys of the map are the property_index() values
+ // of the respective property FieldIndex'es. For slow mode objects, the keys
+ // are the dictionary indicies.
+ 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 MakeRef(broker, it.GetDataValue());
+ }
+ return base::nullopt;
+}
+
+ObjectRef GetOwnFastDataPropertyFromHeap(JSHeapBroker* broker,
+ Handle<JSObject> receiver,
+ Representation representation,
+ FieldIndex field_index) {
+ Handle<Object> constant =
+ JSObject::FastPropertyAt(receiver, representation, field_index);
+ return ObjectRef(broker, constant);
+}
+
+ObjectRef GetOwnDictionaryPropertyFromHeap(JSHeapBroker* broker,
+ Handle<JSObject> receiver,
+ InternalIndex dict_index) {
+ Handle<Object> constant =
+ JSObject::DictionaryPropertyAt(receiver, dict_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::GetOwnFastDataProperty(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 fast property with index "
+ << field_index.property_index() << " on "
+ << this);
+ return nullptr;
+ }
+
+ ObjectRef property = GetOwnFastDataPropertyFromHeap(
+ 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;
+}
+
+ObjectData* JSObjectData::GetOwnDictionaryProperty(JSHeapBroker* broker,
+ InternalIndex dict_index,
+ SerializationPolicy policy) {
+ auto p = own_properties_.find(dict_index.as_int());
+ if (p != own_properties_.end()) return p->second;
+
+ if (policy == SerializationPolicy::kAssumeSerialized) {
+ TRACE_MISSING(broker, "knowledge about dictionary property with index "
+ << dict_index.as_int() << " on " << this);
+ return nullptr;
+ }
+
+ ObjectRef property = GetOwnDictionaryPropertyFromHeap(
+ broker, Handle<JSObject>::cast(object()), dict_index);
+ ObjectData* result(property.data());
+ own_properties_.insert(std::make_pair(dict_index.as_int(), result));
+ return result;
+}
+
+class JSTypedArrayData : public JSObjectData {
+ public:
+ JSTypedArrayData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<JSTypedArray> object)
+ : JSObjectData(broker, storage, object) {}
+
+ // TODO(v8:7790): Once JSObject is no longer serialized, also make
+ // JSTypedArrayRef never-serialized.
+ STATIC_ASSERT(IsSerializedRef<JSObject>());
+
+ void Serialize(JSHeapBroker* broker);
+ bool serialized() const { return serialized_; }
+
+ bool is_on_heap() const { return is_on_heap_; }
+ size_t length() const { return length_; }
+ void* data_ptr() const { return data_ptr_; }
+
+ ObjectData* buffer() const { return buffer_; }
+
+ private:
+ bool serialized_ = false;
+ bool is_on_heap_ = false;
+ size_t length_ = 0;
+ void* data_ptr_ = nullptr;
+ ObjectData* buffer_ = nullptr;
+};
+
+void JSTypedArrayData::Serialize(JSHeapBroker* broker) {
+ if (serialized_) return;
+ serialized_ = true;
+
+ TraceScope tracer(broker, this, "JSTypedArrayData::Serialize");
+ Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(object());
+
+ is_on_heap_ = typed_array->is_on_heap();
+ length_ = typed_array->length();
+ data_ptr_ = typed_array->DataPtr();
+
+ 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(!broker->is_concurrent_inlining());
+ }
+
+ int constants_elements_length() const { return constants_elements_length_; }
+
+ private:
+ int const constants_elements_length_;
+};
+
+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 {
+ DCHECK(!broker()->is_concurrent_inlining());
+ return bound_target_function_;
+ }
+ ObjectData* bound_this() const {
+ DCHECK(!broker()->is_concurrent_inlining());
+ return bound_this_;
+ }
+ ObjectData* bound_arguments() const {
+ DCHECK(!broker()->is_concurrent_inlining());
+ 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 PrototypeRequiresRuntimeLookup() const {
+ return PrototypeRequiresRuntimeLookup_;
+ }
+
+ void Serialize(JSHeapBroker* broker);
+ bool serialized() const { return serialized_; }
+
+ void SerializeCodeAndFeedback(JSHeapBroker* broker);
+ bool serialized_code_and_feedback() const {
+ return serialized_code_and_feedback_;
+ }
+
+ 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 {
+ DCHECK(serialized_code_and_feedback());
+ return feedback_cell_;
+ }
+ ObjectData* feedback_vector() const {
+ DCHECK(serialized_code_and_feedback());
+ return feedback_vector_;
+ }
+ ObjectData* code() const {
+ DCHECK(serialized_code_and_feedback());
+ DCHECK(!broker()->is_concurrent_inlining());
+ 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 PrototypeRequiresRuntimeLookup_;
+
+ bool serialized_ = false;
+ bool serialized_code_and_feedback_ = 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 RegExpBoilerplateDescriptionData : public HeapObjectData {
+ public:
+ RegExpBoilerplateDescriptionData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<RegExpBoilerplateDescription> object)
+ : HeapObjectData(broker, storage, object) {}
+
+ void Serialize(JSHeapBroker* broker);
+ ObjectData* data() const {
+ CHECK(serialized_);
+ return data_;
+ }
+ ObjectData* source() const {
+ CHECK(serialized_);
+ return source_;
+ }
+ int flags() const {
+ CHECK(serialized_);
+ return flags_;
+ }
+
+ private:
+ bool serialized_ = false;
+ ObjectData* data_ = nullptr;
+ ObjectData* source_ = nullptr;
+ int flags_;
+};
+
+class HeapNumberData : public HeapObjectData {
+ public:
+ HeapNumberData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<HeapNumber> object,
+ ObjectDataKind kind = ObjectDataKind::kSerializedHeapObject)
+ : HeapObjectData(broker, storage, object, kind),
+ 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) {
+ DCHECK_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_NE(state_, State::kUnserialized);
+ return function_maps_;
+ }
+
+ ObjectData* scope_info() const {
+ CHECK_NE(state_, State::kUnserialized);
+ return scope_info_;
+ }
+
+ NativeContextData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<NativeContext> object);
+ void Serialize(JSHeapBroker* broker);
+ void SerializeOnBackground(JSHeapBroker* broker);
+
+ private:
+ // After Serialize is called the class is partially serialized and it the
+ // kSerializedOnMainThread state. It then becomes kFullySerialized once
+ // SerializeOnBackground is called.
+ enum class State { kUnserialized, kSerializedOnMainThread, kFullySerialized };
+ State state_;
+
+#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) {
+ DCHECK(!broker->is_concurrent_inlining());
+ }
+};
+
+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* GetCharAsStringOrUndefined(
+ 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(!broker->is_concurrent_inlining());
+ }
+};
+
+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_(TryStringToDouble(broker->local_isolate(), object)),
+ is_external_string_(object->IsExternalString()),
+ is_seq_string_(object->IsSeqString()),
+ chars_as_strings_(broker->zone()) {
+ DCHECK(!broker->is_concurrent_inlining());
+}
+
+class InternalizedStringData : public StringData {
+ public:
+ InternalizedStringData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<InternalizedString> object)
+ : StringData(broker, storage, object) {
+ DCHECK(!broker->is_concurrent_inlining());
+ }
+};
+
+ObjectData* StringData::GetCharAsStringOrUndefined(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;
+}
+
+namespace {
+
+bool IsFastLiteralHelper(Handle<JSObject> boilerplate, int max_depth,
+ int* max_properties) {
+ DCHECK_GE(max_depth, 0);
+ DCHECK_GE(*max_properties, 0);
+
+ // Check for too deep nesting.
+ if (max_depth == 0) return false;
+
+ Isolate* const isolate = boilerplate->GetIsolate();
+
+ // If the boilerplate map has been deprecated, bailout of fast literal
+ // optimization. The map could be deprecated at some point after the line
+ // below, but it's not a correctness issue -- it only means the literal isn't
+ // created with the most up to date map(s).
+ if (boilerplate->map().is_deprecated()) return false;
+
+ // Check the elements.
+ Handle<FixedArrayBase> elements(boilerplate->elements(), isolate);
+ if (elements->length() > 0 &&
+ elements->map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) {
+ if (boilerplate->HasSmiOrObjectElements()) {
+ Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
+ int length = elements->length();
+ for (int i = 0; i < length; i++) {
+ if ((*max_properties)-- == 0) return false;
+ Handle<Object> value(fast_elements->get(i), isolate);
+ if (value->IsJSObject()) {
+ Handle<JSObject> value_object = Handle<JSObject>::cast(value);
+ if (!IsFastLiteralHelper(value_object, max_depth - 1,
+ max_properties)) {
+ return false;
+ }
+ }
+ }
+ } else if (boilerplate->HasDoubleElements()) {
+ if (elements->Size() > kMaxRegularHeapObjectSize) return false;
+ } else {
+ return false;
+ }
+ }
+
+ // TODO(turbofan): Do we want to support out-of-object properties?
+ if (!(boilerplate->HasFastProperties() &&
+ boilerplate->property_array().length() == 0)) {
+ return false;
+ }
+
+ // Check the in-object properties.
+ Handle<DescriptorArray> descriptors(
+ boilerplate->map().instance_descriptors(isolate, 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);
+ 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,
+ ObjectDataKind kind)
+ : HeapObjectData(broker, storage, object, kind),
+ as_uint64_(object->AsUint64(nullptr)) {}
+
+ uint64_t AsUint64() const { return as_uint64_; }
+
+ private:
+ const uint64_t as_uint64_;
+};
+
+struct PropertyDescriptor {
+ ObjectData* key = nullptr;
+ ObjectData* value = nullptr;
+ PropertyDetails details = PropertyDetails::Empty();
+ FieldIndex field_index;
+ ObjectData* field_owner = nullptr;
+ ObjectData* field_type = nullptr;
+};
+
+class MapData : public HeapObjectData {
+ public:
+ MapData(JSHeapBroker* broker, ObjectData** storage, Handle<Map> object,
+ ObjectDataKind kind = ObjectDataKind::kSerializedHeapObject);
+
+ 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).
+ bool TrySerializeOwnDescriptor(JSHeapBroker* broker,
+ InternalIndex descriptor_index);
+ void SerializeOwnDescriptor(JSHeapBroker* broker,
+ InternalIndex descriptor_index) {
+ CHECK(TrySerializeOwnDescriptor(broker, descriptor_index));
+ }
+ void SerializeOwnDescriptors(JSHeapBroker* broker);
+ ObjectData* GetStrongValue(InternalIndex descriptor_index) const;
+ ObjectData* 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_;
+ }
+
+ bool TrySerializePrototype(JSHeapBroker* broker);
+ void SerializePrototype(JSHeapBroker* broker) {
+ CHECK(TrySerializePrototype(broker));
+ }
+ ObjectData* prototype() const {
+ DCHECK_EQ(serialized_prototype_, prototype_ != nullptr);
+ return prototype_;
+ }
+
+ void SerializeForElementLoad(JSHeapBroker* broker);
+
+ void SerializeForElementStore(JSHeapBroker* broker);
+
+ bool has_extra_serialized_data() const {
+ return serialized_elements_kind_generalizations_ ||
+ serialized_own_descriptors_ || serialized_constructor_ ||
+ serialized_backpointer_ || serialized_prototype_ ||
+ serialized_root_map_ || serialized_for_element_load_ ||
+ serialized_for_element_store_;
+ }
+
+ private:
+ // The following fields should be const in principle, but construction
+ // requires locking the MapUpdater lock. For this reason, it's easier to
+ // initialize these inside the constructor body, not in the initializer list.
+
+ // This block of fields will always be serialized.
+ InstanceType instance_type_;
+ int instance_size_;
+ uint32_t bit_field3_;
+ int unused_property_fields_;
+ bool is_abandoned_prototype_map_;
+ int in_object_properties_;
+
+ // These fields will only serialized if we are not concurrent inlining.
+ byte bit_field_;
+ byte bit_field2_;
+ bool can_be_deprecated_;
+ bool can_transition_;
+ int in_object_properties_start_in_words_;
+ int constructor_function_index_;
+ int next_free_property_index_;
+ bool supports_fast_array_iteration_;
+ bool supports_fast_array_resize_;
+
+ // These extra fields still have to be serialized (e.g prototype_) even with
+ // concurrent inling, since those classes have fields themselves which are not
+ // being directly read. This means that, for example, even though we can get
+ // the prototype itself with direct reads, some of its fields require
+ // serialization.
+ bool serialized_elements_kind_generalizations_ = false;
+ ZoneVector<ObjectData*> elements_kind_generalizations_;
+
+ bool serialized_own_descriptors_ = false;
+ ObjectData* 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(!broker->is_concurrent_inlining());
+}
+
+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(kAcquireLoad), 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(kAcquireLoad));
+ 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, ObjectDataKind kind)
+ : ObjectData(broker, storage, object, kind),
+ // 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(kAcquireLoad))) {
+ CHECK_IMPLIES(kind == kSerializedHeapObject,
+ broker->mode() == JSHeapBroker::kSerializing);
+ CHECK_IMPLIES(broker->mode() == JSHeapBroker::kSerialized,
+ kind == kBackgroundSerializedHeapObject);
+}
+
+base::Optional<bool> HeapObjectData::TryGetBooleanValue(
+ JSHeapBroker* broker) const {
+ // Keep in sync with Object::BooleanValue.
+ auto result = TryGetBooleanValueImpl(broker);
+ DCHECK_IMPLIES(broker->IsMainThread() && result.has_value(),
+ result.value() == object()->BooleanValue(broker->isolate()));
+ return result;
+}
+
+base::Optional<bool> HeapObjectData::TryGetBooleanValueImpl(
+ JSHeapBroker* broker) const {
+ DisallowGarbageCollection no_gc;
+ Object o = *object();
+ Isolate* isolate = broker->isolate();
+ const InstanceType t = GetMapInstanceType();
+ if (o.IsTrue(isolate)) {
+ return true;
+ } else if (o.IsFalse(isolate)) {
+ return false;
+ } else if (o.IsNullOrUndefined(isolate)) {
+ return false;
+ } else if (MapRef{broker, map()}.is_undetectable()) {
+ return false; // Undetectable object is false.
+ } else if (InstanceTypeChecker::IsString(t)) {
+ // TODO(jgruber): Implement in possible cases.
+ return {};
+ } else if (InstanceTypeChecker::IsHeapNumber(t)) {
+ return {};
+ } else if (InstanceTypeChecker::IsBigInt(t)) {
+ return {};
+ }
+ return true;
+}
+
+InstanceType HeapObjectData::GetMapInstanceType() const {
+ ObjectData* map_data = map();
+ if (map_data->should_access_heap()) {
+ 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());
+ DescriptorArray descriptors =
+ jsarray_map->instance_descriptors(isolate, kRelaxedLoad);
+ static_assert(
+ JSArray::kLengthOffset == JSObject::kHeaderSize,
+ "The length should be the first property on the descriptor array");
+ InternalIndex offset(0);
+ return descriptors.GetDetails(offset).IsReadOnly();
+}
+
+// Important: this predicate does not check Protectors::IsNoElementsIntact. The
+// compiler checks protectors through the compilation dependency mechanism; it
+// doesn't make sense to do that here as part of every MapData construction.
+// Callers *must* take care to take the correct dependency themselves.
+bool SupportsFastArrayIteration(JSHeapBroker* broker, Handle<Map> map) {
+ return map->instance_type() == JS_ARRAY_TYPE &&
+ IsFastElementsKind(map->elements_kind()) &&
+ map->prototype().IsJSArray() &&
+ broker->IsArrayOrObjectPrototype(broker->CanonicalPersistentHandle(
+ JSArray::cast(map->prototype())));
+}
+
+bool SupportsFastArrayResize(JSHeapBroker* broker, Handle<Map> map) {
+ return SupportsFastArrayIteration(broker, map) && map->is_extensible() &&
+ !map->is_dictionary_map() &&
+ !IsReadOnlyLengthDescriptor(broker->isolate(), map);
+}
+
+} // namespace
+
+MapData::MapData(JSHeapBroker* broker, ObjectData** storage, Handle<Map> object,
+ ObjectDataKind kind)
+ : HeapObjectData(broker, storage, object, kind),
+ elements_kind_generalizations_(broker->zone()) {
+ // This lock ensure that MapData can always be background-serialized, i.e.
+ // while the lock is held the Map object may not be modified (except in
+ // benign ways).
+ // TODO(jgruber): Consider removing this lock by being smrt.
+ JSHeapBroker::MapUpdaterGuardIfNeeded mumd_scope(
+ broker, broker->isolate()->map_updater_access());
+
+ // When background serializing the map, we can perform a lite serialization
+ // since the MapRef will read some of the Map's fields can be read directly.
+
+ // Even though MapRefs can read {instance_type} directly, other classes depend
+ // on {instance_type} being serialized.
+ instance_type_ = object->instance_type();
+ instance_size_ = object->instance_size();
+
+ // Both bit_field3 (and below bit_field) are special fields: Even though most
+ // of the individual bits inside of the bitfield could be read / written
+ // non-atomically, the bitfield itself has to use atomic relaxed accessors
+ // since some fields since can be modified in live objects.
+ // TODO(solanes, v8:7790): Assess if adding the exclusive lock in more places
+ // (e.g for set_has_non_instance_prototype) makes sense. Pros: these fields
+ // can use the non-atomic accessors. Cons: We would be acquiring an exclusive
+ // lock in more places.
+ bit_field3_ = object->relaxed_bit_field3();
+ unused_property_fields_ = object->UnusedPropertyFields();
+ is_abandoned_prototype_map_ = object->is_abandoned_prototype_map();
+ in_object_properties_ =
+ object->IsJSObjectMap() ? object->GetInObjectProperties() : 0;
+
+ // These fields are only needed to be serialized when not concurrent inlining
+ // and thus disabling direct reads.
+ if (!broker->is_concurrent_inlining()) {
+ bit_field_ = object->relaxed_bit_field();
+ bit_field2_ = object->bit_field2();
+ can_be_deprecated_ = object->NumberOfOwnDescriptors() > 0
+ ? object->CanBeDeprecated()
+ : false;
+ can_transition_ = object->CanTransition();
+ in_object_properties_start_in_words_ =
+ object->IsJSObjectMap() ? object->GetInObjectPropertiesStartInWords()
+ : 0;
+ next_free_property_index_ = object->NextFreePropertyIndex();
+ constructor_function_index_ = object->IsPrimitiveMap()
+ ? object->GetConstructorFunctionIndex()
+ : Map::kNoConstructorFunctionIndex;
+ supports_fast_array_iteration_ = SupportsFastArrayIteration(broker, object);
+ supports_fast_array_resize_ = SupportsFastArrayResize(broker, object);
+ }
+}
+
+JSFunctionData::JSFunctionData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<JSFunction> object)
+ : JSObjectData(broker, storage, object),
+ has_feedback_vector_(object->has_feedback_vector()),
+ has_initial_map_(object->has_prototype_slot() &&
+ object->has_initial_map()),
+ has_prototype_(object->has_prototype_slot() && object->has_prototype()),
+ PrototypeRequiresRuntimeLookup_(
+ object->PrototypeRequiresRuntimeLookup()) {}
+
+void JSFunctionData::Serialize(JSHeapBroker* broker) {
+ if (serialized_) return;
+ serialized_ = true;
+
+ TraceScope tracer(broker, this, "JSFunctionData::Serialize");
+ Handle<JSFunction> function = Handle<JSFunction>::cast(object());
+
+ DCHECK_NULL(context_);
+ DCHECK_NULL(native_context_);
+ DCHECK_NULL(initial_map_);
+ DCHECK_NULL(prototype_);
+ DCHECK_NULL(shared_);
+
+ context_ = broker->GetOrCreateData(function->context());
+ native_context_ = broker->GetOrCreateData(function->native_context());
+ shared_ = broker->GetOrCreateData(function->shared());
+
+ 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 JSFunctionData::SerializeCodeAndFeedback(JSHeapBroker* broker) {
+ DCHECK(serialized_);
+ if (serialized_code_and_feedback_) return;
+ serialized_code_and_feedback_ = true;
+
+ TraceScope tracer(broker, this, "JSFunctionData::SerializeCodeAndFeedback");
+ Handle<JSFunction> function = Handle<JSFunction>::cast(object());
+
+ DCHECK_NULL(feedback_cell_);
+ DCHECK_NULL(feedback_vector_);
+ DCHECK_NULL(code_);
+ if (!broker->is_concurrent_inlining()) {
+ // This is conditionalized because Code objects are never serialized now.
+ // We only need to represent the code object in serialized data when
+ // we're unable to perform direct heap accesses.
+ code_ = broker->GetOrCreateData(function->code(kAcquireLoad));
+ }
+ feedback_cell_ = broker->GetOrCreateData(function->raw_feedback_cell());
+ feedback_vector_ = has_feedback_vector()
+ ? broker->GetOrCreateData(function->feedback_vector())
+ : nullptr;
+}
+
+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()) {
+ DCHECK(!broker->is_concurrent_inlining());
+ }
+
+ ObjectData* FindFieldOwner(InternalIndex descriptor_index) const {
+ return contents_.at(descriptor_index.as_int()).field_owner;
+ }
+
+ PropertyDetails GetPropertyDetails(InternalIndex descriptor_index) const {
+ return contents_.at(descriptor_index.as_int()).details;
+ }
+
+ ObjectData* GetPropertyKey(InternalIndex descriptor_index) const {
+ return contents_.at(descriptor_index.as_int()).key;
+ }
+
+ FieldIndex GetFieldIndexFor(InternalIndex descriptor_index) const {
+ return contents_.at(descriptor_index.as_int()).field_index;
+ }
+
+ ObjectData* GetFieldType(InternalIndex descriptor_index) const {
+ return contents_.at(descriptor_index.as_int()).field_type;
+ }
+
+ ObjectData* GetStrongValue(InternalIndex descriptor_index) const {
+ return contents_.at(descriptor_index.as_int()).value;
+ }
+
+ bool serialized_descriptor(InternalIndex descriptor_index) const {
+ return contents_.find(descriptor_index.as_int()) != contents_.end();
+ }
+
+ void SerializeDescriptor(JSHeapBroker* broker, Handle<Map> map,
+ InternalIndex descriptor_index);
+
+ private:
+ ZoneMap<int, PropertyDescriptor> contents_;
+};
+
+void DescriptorArrayData::SerializeDescriptor(JSHeapBroker* broker,
+ Handle<Map> map,
+ InternalIndex descriptor_index) {
+ 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(object());
+ CHECK_EQ(*descriptors, map->instance_descriptors(isolate));
+
+ 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));
+ }
+ 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 "
+ << this << " (" << contents_.size()
+ << " total)");
+}
+
+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_(object->value().IsFeedbackVector()
+ ? broker->GetOrCreateData(object->value())
+ : nullptr) {
+ DCHECK(!broker->is_concurrent_inlining());
+}
+
+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()) {
+ DCHECK(!broker->is_concurrent_inlining());
+}
+
+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, ObjectDataKind kind)
+ : HeapObjectData(broker, storage, object, kind),
+ 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, ObjectDataKind kind)
+ : FixedArrayBaseData(broker, storage, object, kind) {}
+};
+
+class ObjectBoilerplateDescriptionData : public FixedArrayData {
+ public:
+ ObjectBoilerplateDescriptionData(
+ JSHeapBroker* broker, ObjectData** storage,
+ Handle<ObjectBoilerplateDescription> object,
+ ObjectDataKind kind = ObjectDataKind::kSerializedHeapObject)
+ : FixedArrayData(broker, storage, object, kind), size_(object->size()) {
+ DCHECK(!broker->is_concurrent_inlining());
+ }
+
+ int size() const { return size_; }
+
+ private:
+ int const size_;
+};
+
+// Only used in JSNativeContextSpecialization.
+class ScriptContextTableData : public FixedArrayData {
+ public:
+ ScriptContextTableData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<ScriptContextTable> object, ObjectDataKind kind)
+ : FixedArrayData(broker, storage, object, kind) {}
+};
+
+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());
+
+ 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,
+ ObjectDataKind::kSerializedHeapObject),
+ inobject_fields_(broker->zone()),
+ own_constant_elements_(broker->zone()),
+ own_properties_(broker->zone()) {}
+
+class FixedDoubleArrayData : public FixedArrayBaseData {
+ public:
+ FixedDoubleArrayData(
+ JSHeapBroker* broker, ObjectData** storage,
+ Handle<FixedDoubleArray> object,
+ ObjectDataKind kind = ObjectDataKind::kNeverSerializedHeapObject)
+ : FixedArrayBaseData(broker, storage, object, kind) {
+ DCHECK(!broker->is_concurrent_inlining());
+ }
+};
+
+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_;
+ }
+
+ BytecodeArrayData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<BytecodeArray> object)
+ : FixedArrayBaseData(broker, storage, object,
+ ObjectDataKind::kNeverSerializedHeapObject),
+ register_count_(object->register_count()),
+ parameter_count_(object->parameter_count()),
+ incoming_new_target_or_generator_register_(
+ object->incoming_new_target_or_generator_register()) {}
+
+ private:
+ int const register_count_;
+ int const parameter_count_;
+ interpreter::Register const incoming_new_target_or_generator_register_;
+};
+
+class JSArrayData : public JSObjectData {
+ public:
+ JSArrayData(JSHeapBroker* broker, ObjectData** storage,
+ Handle<JSArray> object);
+
+ void Serialize(JSHeapBroker* broker);
+ ObjectData* length() const {
+ CHECK(serialized_);
+ 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) {
+ CHECK(!broker->is_concurrent_inlining());
+
+ 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(!broker->is_concurrent_inlining());
+}
+
+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(broker->isolate()))
+ : nullptr)
+#define INIT_MEMBER(type, name) , name##_(object->name())
+ BROKER_SFI_FIELDS(INIT_MEMBER)
+#undef INIT_MEMBER
+ ,
+ inlineability_(object->GetInlineability(broker->isolate())),
+ 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(kAcquireLoad));
+ TRACE(broker, "Copied import_meta");
+}
+
+class CellData : public HeapObjectData {
+ public:
+ CellData(JSHeapBroker* broker, ObjectData** storage, Handle<Cell> object)
+ : HeapObjectData(broker, storage, object) {
+ DCHECK(!broker->is_concurrent_inlining());
+ }
+};
+
+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 TryMakeRef(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(!broker->is_concurrent_inlining());
+ }
+};
+
+class CodeData : public HeapObjectData {
+ public:
+ CodeData(JSHeapBroker* broker, ObjectData** storage, Handle<Code> object)
+ : HeapObjectData(broker, storage, object),
+ inlined_bytecode_size_(object->inlined_bytecode_size() > 0 &&
+ !object->marked_for_deoptimization()
+ ? object->inlined_bytecode_size()
+ : 0) {
+ DCHECK(!broker->is_concurrent_inlining());
+ }
+
+ 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()) { \
+ 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_OBJECT_LIST(DEFINE_IS)
+#undef DEFINE_IS
+
+#define DEFINE_AS(Name, Kind) \
+ Name##Data* ObjectData::As##Name() { \
+ CHECK(Is##Name()); \
+ CHECK(kind_ == kSerializedHeapObject || \
+ kind_ == kBackgroundSerializedHeapObject); \
+ return static_cast<Name##Data*>(this); \
+ }
+HEAP_BROKER_OBJECT_LIST(DEFINE_AS)
+#undef DEFINE_AS
+
+ObjectData* 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 {
+ CHECK(serialized_elements_);
+ 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());
+}
+
+bool MapData::TrySerializePrototype(JSHeapBroker* broker) {
+ if (serialized_prototype_) return true;
+
+ TraceScope tracer(broker, this, "MapData::SerializePrototype");
+ Handle<Map> map = Handle<Map>::cast(object());
+ DCHECK_NULL(prototype_);
+ prototype_ = broker->TryGetOrCreateData(map->prototype());
+ if (prototype_ == nullptr) return false;
+ serialized_prototype_ = true;
+ return true;
+}
+
+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);
+ }
+}
+
+bool MapData::TrySerializeOwnDescriptor(JSHeapBroker* broker,
+ InternalIndex descriptor_index) {
+ TraceScope tracer(broker, this, "MapData::SerializeOwnDescriptor");
+ Handle<Map> map = Handle<Map>::cast(object());
+ Isolate* isolate = broker->isolate();
+
+ if (instance_descriptors_ == nullptr) {
+ instance_descriptors_ =
+ broker->TryGetOrCreateData(map->instance_descriptors(kAcquireLoad));
+ if (instance_descriptors_ == nullptr) return false;
+ }
+
+ if (instance_descriptors()->should_access_heap()) {
+ // When accessing the fields concurrently, we still have to recurse on the
+ // owner map if it is different than the current map. This is because
+ // {instance_descriptors_} gets set on SerializeOwnDescriptor and otherwise
+ // we risk the field owner having a null {instance_descriptors_}.
+ Handle<DescriptorArray> descriptors = broker->CanonicalPersistentHandle(
+ map->instance_descriptors(kAcquireLoad));
+ if (descriptors->GetDetails(descriptor_index).location() == kField) {
+ Handle<Map> owner = broker->CanonicalPersistentHandle(
+ map->FindFieldOwner(isolate, descriptor_index));
+ if (!owner.equals(map)) {
+ ObjectData* data = broker->TryGetOrCreateData(owner);
+ if (data == nullptr) return false;
+ data->AsMap()->SerializeOwnDescriptor(broker, descriptor_index);
+ }
+ }
+ } else {
+ DescriptorArrayData* descriptors =
+ instance_descriptors()->AsDescriptorArray();
+ descriptors->SerializeDescriptor(broker, map, descriptor_index);
+ }
+
+ return true;
+}
+
+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);
+
+ // 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_);
+ DCHECK(!serialized_elements_);
+ serialized_elements_ = true;
+ 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()) {
+ 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);
+ }
+
+ // TODO(turbofan): Do we want to support out-of-object properties?
+ CHECK(boilerplate->HasFastProperties() &&
+ boilerplate->property_array().length() == 0);
+ CHECK_EQ(inobject_fields_.size(), 0u);
+
+ // Check the in-object properties.
+ Handle<DescriptorArray> descriptors(
+ boilerplate->map().instance_descriptors(isolate), 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()));
+ 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. 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(value_data);
+ }
+ TRACE(broker, "Copied " << inobject_fields_.size() << " in-object fields");
+
+ if (!map()->should_access_heap()) {
+ map()->AsMap()->SerializeOwnDescriptors(broker);
+ }
+
+ if (IsJSArray() && !broker->is_concurrent_inlining()) {
+ AsJSArray()->Serialize(broker);
+ }
+}
+
+void RegExpBoilerplateDescriptionData::Serialize(JSHeapBroker* broker) {
+ if (serialized_) return; // Only serialize once.
+ serialized_ = true;
+
+ TraceScope tracer(broker, this,
+ "RegExpBoilerplateDescriptionData::Serialize");
+ auto boilerplate = Handle<RegExpBoilerplateDescription>::cast(object());
+
+ data_ = broker->GetOrCreateData(boilerplate->data());
+ source_ = broker->GetOrCreateData(boilerplate->source());
+ flags_ = boilerplate->flags();
+}
+
+#ifdef DEBUG
+bool ObjectRef::IsNeverSerializedHeapObject() const {
+ return data_->kind() == ObjectDataKind::kNeverSerializedHeapObject;
+}
+#endif // DEBUG
+
+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
+ // TODO(jgruber): Consider going back to reference-equality on data_ once
+ // ObjectData objects are guaranteed to be canonicalized (see also:
+ // ClearReconstructibleData).
+ return data_->object().is_identical_to(other.data_->object());
+}
+
+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()) {
+ Context current = *object();
+ while (*depth != 0 && current.unchecked_previous().IsContext()) {
+ current = Context::cast(current.unchecked_previous());
+ (*depth)--;
+ }
+ return MakeRef(broker(), 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 {
+ CHECK_LE(0, index);
+ if (data_->should_access_heap()) {
+ if (index >= object()->length()) return {};
+ return TryMakeRef(broker(), object()->get(index));
+ }
+ ObjectData* optional_slot =
+ data()->AsContext()->GetSlot(broker(), index, policy);
+ if (optional_slot == nullptr) return {};
+ return ObjectRef(broker(), optional_slot);
+}
+
+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();
+}
+
+#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::InitializeAndStartSerializing() {
+ TraceScope tracer(this, "JSHeapBroker::InitializeAndStartSerializing");
+
+ CHECK_EQ(mode_, kDisabled);
+ mode_ = kSerializing;
+
+ // Throw away the dummy data that we created while disabled.
+ refs_->Clear();
+ refs_ =
+ zone()->New<RefsMap>(kInitialRefsBucketCount, AddressMatcher(), zone());
+
+ CollectArrayAndObjectPrototypes();
+
+ SetTargetNativeContextRef(target_native_context().object());
+ target_native_context().Serialize();
+ if (!is_concurrent_inlining()) {
+ // Perform full native context serialization now if we can't do it later on
+ // the background thread.
+ target_native_context().SerializeOnBackground();
+ }
+
+ Factory* const f = isolate()->factory();
+ if (!is_concurrent_inlining()) {
+ 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, SaveFPRegsMode::kIgnore,
+ ArgvMode::kStack, true));
+
+ TRACE(this, "Finished serializing standard objects");
+}
+
+namespace {
+
+template <RefSerializationKind Kind, class DataT, class ObjectT>
+struct CreateDataFunctor {
+ bool operator()(JSHeapBroker* broker, RefsMap* refs,
+ Handle<Object> object, RefsMap::Entry** entry_out,
+ ObjectData** object_data_out) {
+ USE(broker, refs, object, entry_out, object_data_out);
+ UNREACHABLE();
+ }
+};
+
+template <class DataT, class ObjectT>
+struct CreateDataFunctor<RefSerializationKind::kSerialized, DataT, ObjectT> {
+ bool operator()(JSHeapBroker* broker, RefsMap* refs,
+ Handle<Object> object, RefsMap::Entry** entry_out,
+ ObjectData** object_data_out) {
+ if (broker->mode() == JSHeapBroker::kSerializing) {
+ RefsMap::Entry* entry = refs->LookupOrInsert(object.address());
+ *object_data_out = broker->zone()->New<DataT>(
+ broker, &entry->value, Handle<ObjectT>::cast(object));
+ *entry_out = entry;
+ return true;
+ }
+ return false;
+ }
+};
+
+template <class DataT, class ObjectT>
+struct CreateDataFunctor<RefSerializationKind::kBackgroundSerialized, DataT,
+ ObjectT> {
+ bool operator()(JSHeapBroker* broker, RefsMap* refs,
+ Handle<Object> object, RefsMap::Entry** entry_out,
+ ObjectData** object_data_out) {
+ if (broker->is_concurrent_inlining()) {
+ RefsMap::Entry* entry = refs->LookupOrInsert(object.address());
+ *object_data_out = broker->zone()->New<DataT>(
+ broker, &entry->value, Handle<ObjectT>::cast(object),
+ kBackgroundSerializedHeapObject);
+ *entry_out = entry;
+ return true;
+ } else if (broker->mode() == JSHeapBroker::kSerializing) {
+ RefsMap::Entry* entry = refs->LookupOrInsert(object.address());
+ *object_data_out = broker->zone()->New<DataT>(
+ broker, &entry->value, Handle<ObjectT>::cast(object),
+ ObjectDataKind::kSerializedHeapObject);
+ *entry_out = entry;
+ return true;
+ }
+ return false;
+ }
+};
+
+template <class DataT, class ObjectT>
+struct CreateDataFunctor<RefSerializationKind::kNeverSerialized, DataT,
+ ObjectT> {
+ bool operator()(JSHeapBroker* broker, RefsMap* refs, Handle<Object> object,
+ RefsMap::Entry** entry_out, ObjectData** object_data_out) {
+ // TODO(solanes, v8:10866): Remove the `(mode() == kSerializing)` case
+ // below when all classes skip serialization. Same for similar spots if we
+ // end up keeping them.
+ if (broker->is_concurrent_inlining()) {
+ RefsMap::Entry* entry = refs->LookupOrInsert(object.address());
+ *object_data_out = broker->zone()->New<ObjectData>(
+ broker, &entry->value, object, kNeverSerializedHeapObject);
+ *entry_out = entry;
+ return true;
+ } else if (broker->mode() == JSHeapBroker::kSerializing) {
+ RefsMap::Entry* entry = refs->LookupOrInsert(object.address());
+ *object_data_out = broker->zone()->New<DataT>(
+ broker, &entry->value, Handle<ObjectT>::cast(object));
+ *entry_out = entry;
+ return true;
+ }
+ return false;
+ }
+};
+
+} // namespace
+
+void JSHeapBroker::ClearReconstructibleData() {
+ RefsMap::Entry* p = refs_->Start();
+ while (p != nullptr) {
+ Address key = p->key;
+ ObjectData* value = p->value;
+ p = refs_->Next(p);
+ const auto kind = RefSerializationKindOf(value);
+ if (kind == RefSerializationKind::kNeverSerialized ||
+ kind == RefSerializationKind::kBackgroundSerialized) {
+ if (value->IsMap() &&
+ value->kind() == ObjectDataKind::kBackgroundSerializedHeapObject &&
+ value->AsMap()->has_extra_serialized_data()) {
+ continue;
+ }
+ // Can be reconstructed from the background thread.
+ CHECK_NOT_NULL(refs_->Remove(key));
+ }
+ }
+}
+
+ObjectData* JSHeapBroker::TryGetOrCreateData(Handle<Object> object,
+ GetOrCreateDataFlags flags) {
+ RefsMap::Entry* entry = refs_->Lookup(object.address());
+ if (entry != nullptr) return entry->value;
+
+ if (mode() == JSHeapBroker::kDisabled) {
+ entry = refs_->LookupOrInsert(object.address());
+ ObjectData** storage = &entry->value;
+ if (*storage == nullptr) {
+ entry->value = zone()->New<ObjectData>(
+ this, storage, object,
+ object->IsSmi() ? kSmi : kUnserializedHeapObject);
+ }
+ return *storage;
+ }
+
+ CHECK(mode() == JSHeapBroker::kSerializing ||
+ mode() == JSHeapBroker::kSerialized);
+
+ ObjectData* object_data;
+ if (object->IsSmi()) {
+ entry = refs_->LookupOrInsert(object.address());
+ return zone()->New<ObjectData>(this, &entry->value, object, kSmi);
+ }
+
+ DCHECK(!object->IsSmi());
+
+ const bool crash_on_error = (flags & kCrashOnError) != 0;
+
+ // TODO(jgruber): Remove this flag check (and the flag) once TSAN failures
+ // are fixed.
+ // See also: crbug.com/v8/11779
+ if (FLAG_turbo_concurrent_inlining_check_ispendingallocation) {
+ if ((flags & kAssumeMemoryFence) == 0 &&
+ ObjectMayBeUninitialized(HeapObject::cast(*object))) {
+ TRACE_BROKER_MISSING(this, "Object may be uninitialized " << *object);
+ CHECK_WITH_MSG(!crash_on_error, "Ref construction failed");
+ return nullptr;
+ }
+ }
+
+ if (IsReadOnlyHeapObjectForCompiler(HeapObject::cast(*object))) {
+ entry = refs_->LookupOrInsert(object.address());
+ return zone()->New<ObjectData>(this, &entry->value, object,
+ kUnserializedReadOnlyHeapObject);
+ }
+
+#define CREATE_DATA(Name, Kind) \
+ if (object->Is##Name()) { \
+ CreateDataFunctor<Kind, Name##Data, Name> f; \
+ if (!f(this, refs_, object, &entry, &object_data)) { \
+ CHECK_WITH_MSG(!crash_on_error, "Ref construction failed"); \
+ return nullptr; \
+ } \
+ /* NOLINTNEXTLINE(readability/braces) */ \
+ } else
+ HEAP_BROKER_OBJECT_LIST(CREATE_DATA)
+#undef CREATE_DATA
+ {
+ 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;
+}
+
+#define DEFINE_IS_AND_AS(Name, ...) \
+ bool ObjectRef::Is##Name() const { return data()->Is##Name(); } \
+ Name##Ref ObjectRef::As##Name() const { \
+ DCHECK(Is##Name()); \
+ return Name##Ref(broker(), data()); \
+ }
+HEAP_BROKER_OBJECT_LIST(DEFINE_IS_AND_AS)
+#undef DEFINE_IS_AND_AS
+
+bool ObjectRef::IsSmi() const { return data()->is_smi(); }
+
+int ObjectRef::AsSmi() const {
+ DCHECK(IsSmi());
+ // Handle-dereference is always allowed for Handle<Smi>.
+ return Handle<Smi>::cast(object())->value();
+}
+
+base::Optional<MapRef> JSObjectRef::GetObjectCreateMap() const {
+ if (data_->should_access_heap()) {
+ Handle<Map> instance_map;
+ if (Map::TryGetObjectCreateMap(broker()->isolate(), object())
+ .ToHandle(&instance_map)) {
+ return MakeRef(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 MakeRef(broker(), Handle<Map>::cast(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()) {
+ return MakeRef(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());
+}
+
+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");
+ // TODO(solanes, v8:7790): This should use MapData methods rather than
+ // constructing MapRefs, but it involves non-trivial refactoring and this
+ // method should go away anyway once the compiler is fully concurrent.
+ MapRef map(broker, this);
+ do {
+ map.SerializePrototype();
+ map = map.prototype().value().map();
+ } while (map.IsJSObjectMap() && map.is_stable() &&
+ IsFastElementsKind(map.elements_kind()));
+}
+
+bool MapRef::HasOnlyStablePrototypesWithFastElements(
+ ZoneVector<MapRef>* prototype_maps) {
+ DCHECK_NOT_NULL(prototype_maps);
+ MapRef prototype_map = prototype().value().map();
+ while (prototype_map.oddball_type() != OddballType::kNull) {
+ if (!prototype_map.IsJSObjectMap() || !prototype_map.is_stable() ||
+ !IsFastElementsKind(prototype_map.elements_kind())) {
+ return false;
+ }
+ prototype_maps->push_back(prototype_map);
+ prototype_map = prototype_map.prototype().value().map();
+ }
+ return true;
+}
+
+bool MapRef::supports_fast_array_iteration() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ return SupportsFastArrayIteration(broker(), object());
+ }
+ return data()->AsMap()->supports_fast_array_iteration();
+}
+
+bool MapRef::supports_fast_array_resize() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ return SupportsFastArrayResize(broker(), object());
+ }
+ return data()->AsMap()->supports_fast_array_resize();
+}
+
+int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const {
+ if (data_->should_access_heap()) {
+ 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(MakeRef(broker(), f->undefined_map()))) {
+ return OddballType::kUndefined;
+ }
+ if (equals(MakeRef(broker(), f->null_map()))) {
+ return OddballType::kNull;
+ }
+ if (equals(MakeRef(broker(), f->boolean_map()))) {
+ return OddballType::kBoolean;
+ }
+ if (equals(MakeRef(broker(), f->the_hole_map()))) {
+ return OddballType::kHole;
+ }
+ if (equals(MakeRef(broker(), f->uninitialized_map()))) {
+ return OddballType::kUninitialized;
+ }
+ DCHECK(equals(MakeRef(broker(), f->termination_exception_map())) ||
+ equals(MakeRef(broker(), f->arguments_marker_map())) ||
+ equals(MakeRef(broker(), f->optimized_out_map())) ||
+ equals(MakeRef(broker(), f->stale_register_map())));
+ return OddballType::kOther;
+}
+
+FeedbackCellRef FeedbackVectorRef::GetClosureFeedbackCell(int index) const {
+ if (data_->should_access_heap()) {
+ // These should all be available because we request the cell for each
+ // CreateClosure bytecode.
+ return MakeRef(broker(), object()->closure_feedback_cell(index));
+ }
+
+ return FeedbackCellRef(
+ broker(),
+ data()->AsFeedbackVector()->GetClosureFeedbackCell(broker(), index));
+}
+
+ObjectRef JSObjectRef::RawFastPropertyAt(FieldIndex index) const {
+ CHECK(index.is_inobject());
+ if (data_->should_access_heap()) {
+ return MakeRef(broker(), object()->RawFastPropertyAt(index));
+ }
+ JSObjectData* object_data = data()->AsJSObject();
+ return ObjectRef(broker(),
+ object_data->GetInobjectField(index.property_index()));
+}
+
+bool AllocationSiteRef::IsFastLiteral() const {
+ if (data_->should_access_heap()) {
+ CHECK_NE(data_->kind(), ObjectDataKind::kNeverSerializedHeapObject);
+ return IsInlinableFastLiteral(
+ handle(object()->boilerplate(kAcquireLoad), 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()) {
+ Handle<FixedArrayBase> object_elements = elements().value().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 {
+ CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ FieldIndex result = FieldIndex::ForDescriptor(*object(), descriptor_index);
+ DCHECK(result.is_inobject());
+ return result;
+ }
+ DescriptorArrayData* descriptors =
+ data()->AsMap()->instance_descriptors()->AsDescriptorArray();
+ FieldIndex result = descriptors->GetFieldIndexFor(descriptor_index);
+ DCHECK(result.is_inobject());
+ return result;
+}
+
+int MapRef::GetInObjectPropertyOffset(int i) const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ return object()->GetInObjectPropertyOffset(i);
+ }
+ return (GetInObjectPropertiesStartInWords() + i) * kTaggedSize;
+}
+
+PropertyDetails MapRef::GetPropertyDetails(
+ InternalIndex descriptor_index) const {
+ CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
+ return instance_descriptors().GetPropertyDetails(descriptor_index);
+}
+
+NameRef MapRef::GetPropertyKey(InternalIndex descriptor_index) const {
+ CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
+ return instance_descriptors().GetPropertyKey(descriptor_index);
+}
+
+bool MapRef::IsFixedCowArrayMap() const {
+ Handle<Map> fixed_cow_array_map =
+ ReadOnlyRoots(broker()->isolate()).fixed_cow_array_map_handle();
+ return equals(MakeRef(broker(), fixed_cow_array_map));
+}
+
+bool MapRef::IsPrimitiveMap() const {
+ return instance_type() <= LAST_PRIMITIVE_HEAP_OBJECT_TYPE;
+}
+
+MapRef MapRef::FindFieldOwner(InternalIndex descriptor_index) const {
+ CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // TODO(solanes, v8:7790): Consider caching the result of the field owner on
+ // the descriptor array. It would be useful for same map as well as any
+ // other map sharing that descriptor array.
+ return MapRef(broker(), broker()->GetOrCreateData(object()->FindFieldOwner(
+ broker()->isolate(), descriptor_index)));
+ }
+ DescriptorArrayData* descriptors =
+ data()->AsMap()->instance_descriptors()->AsDescriptorArray();
+ return MapRef(broker(), descriptors->FindFieldOwner(descriptor_index));
+}
+
+ObjectRef MapRef::GetFieldType(InternalIndex descriptor_index) const {
+ CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
+ return instance_descriptors().GetFieldType(descriptor_index);
+}
+
+base::Optional<ObjectRef> StringRef::GetCharAsStringOrUndefined(
+ uint32_t index, SerializationPolicy policy) const {
+ if (data_->should_access_heap()) {
+ // TODO(solanes, neis, v8:7790, v8:11012): Re-enable this optimization for
+ // concurrent inlining when we have the infrastructure to safely do so.
+ if (broker()->is_concurrent_inlining()) return base::nullopt;
+ CHECK_EQ(data_->kind(), ObjectDataKind::kUnserializedHeapObject);
+ return GetOwnElementFromHeap(broker(), object(), index, true);
+ }
+ ObjectData* element =
+ data()->AsString()->GetCharAsStringOrUndefined(broker(), index, policy);
+ if (element == nullptr) return base::nullopt;
+ return ObjectRef(broker(), element);
+}
+
+base::Optional<int> StringRef::length() const {
+ if (data_->should_access_heap()) {
+ if (data_->kind() == kNeverSerializedHeapObject &&
+ !this->IsInternalizedString()) {
+ TRACE_BROKER_MISSING(
+ broker(),
+ "length for kNeverSerialized non-internalized string " << *this);
+ return base::nullopt;
+ } else {
+ return object()->length(kAcquireLoad);
+ }
+ }
+ return data()->AsString()->length();
+}
+
+base::Optional<uint16_t> StringRef::GetFirstChar() {
+ if (data_->should_access_heap()) {
+ if (data_->kind() == kNeverSerializedHeapObject &&
+ !this->IsInternalizedString()) {
+ TRACE_BROKER_MISSING(
+ broker(),
+ "first char for kNeverSerialized non-internalized string " << *this);
+ return base::nullopt;
+ }
+
+ if (!broker()->IsMainThread()) {
+ return object()->Get(0, broker()->local_isolate());
+ } else {
+ // TODO(solanes, v8:7790): Remove this case once the inlining phase is
+ // done concurrently all the time.
+ return object()->Get(0);
+ }
+ }
+ return data()->AsString()->first_char();
+}
+
+base::Optional<double> StringRef::ToNumber() {
+ if (data_->should_access_heap()) {
+ if (data_->kind() == kNeverSerializedHeapObject &&
+ !this->IsInternalizedString()) {
+ TRACE_BROKER_MISSING(
+ broker(),
+ "number for kNeverSerialized non-internalized string " << *this);
+ return base::nullopt;
+ }
+
+ return TryStringToDouble(broker()->local_isolate(), object());
+ }
+ return data()->AsString()->to_number();
+}
+
+int ArrayBoilerplateDescriptionRef::constants_elements_length() const {
+ if (data_->should_access_heap()) {
+ return object()->constant_elements().length();
+ }
+ return data()->AsArrayBoilerplateDescription()->constants_elements_length();
+}
+
+ObjectRef FixedArrayRef::get(int i) const { return TryGet(i).value(); }
+
+base::Optional<ObjectRef> FixedArrayRef::TryGet(int i) const {
+ return TryMakeRef(broker(), object()->get(i, kRelaxedLoad));
+}
+
+Float64 FixedDoubleArrayRef::GetFromImmutableFixedDoubleArray(int i) const {
+ STATIC_ASSERT(ref_traits<FixedDoubleArray>::ref_serialization_kind ==
+ RefSerializationKind::kNeverSerialized);
+ return Float64::FromBits(object()->get_representation(i));
+}
+
+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()) { \
+ return object()->name(); \
+ }
+
+#define IF_ACCESS_FROM_HEAP(result, name) \
+ if (data_->should_access_heap()) { \
+ return MakeRef(broker(), result::cast(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() || broker()->is_concurrent_inlining()) { \
+ return MakeRef(broker(), result::cast(object()->name())); \
+ }
+#define IF_ACCESS_FROM_HEAP_WITH_FLAG_C(name) \
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) { \
+ return object()->name(); \
+ }
+
+// Like BIMODAL_ACCESSOR[_C] except that we force a direct heap access if
+// broker()->is_concurrent_inlining() 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(); \
+ }
+#define BIMODAL_ACCESSOR_WITH_FLAG_B(holder, field, name, BitField) \
+ typename BitField::FieldType holder##Ref::name() const { \
+ IF_ACCESS_FROM_HEAP_WITH_FLAG_C(name); \
+ return BitField::decode(ObjectRef::data()->As##holder()->field()); \
+ }
+
+BIMODAL_ACCESSOR(AllocationSite, Object, nested_site)
+BIMODAL_ACCESSOR_C(AllocationSite, bool, CanInlineCall)
+BIMODAL_ACCESSOR_C(AllocationSite, bool, PointsToLiteral)
+BIMODAL_ACCESSOR_C(AllocationSite, ElementsKind, GetElementsKind)
+BIMODAL_ACCESSOR_C(AllocationSite, AllocationType, GetAllocationType)
+
+BIMODAL_ACCESSOR_C(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)
+
+// These JSBoundFunction fields are immutable after initialization. Moreover,
+// as long as JSObjects are still serialized on the main thread, all
+// JSBoundFunctionRefs are created at a time when the underlying objects are
+// guaranteed to be fully initialized.
+BIMODAL_ACCESSOR_WITH_FLAG(JSBoundFunction, JSReceiver, bound_target_function)
+BIMODAL_ACCESSOR_WITH_FLAG(JSBoundFunction, Object, bound_this)
+BIMODAL_ACCESSOR_WITH_FLAG(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, 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_C(JSGlobalObject, bool, IsDetached)
+
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field2, elements_kind,
+ Map::Bits2::ElementsKindBits)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field3, is_dictionary_map,
+ Map::Bits3::IsDictionaryMapBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field3, is_deprecated,
+ Map::Bits3::IsDeprecatedBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field3, NumberOfOwnDescriptors,
+ Map::Bits3::NumberOfOwnDescriptorsBits)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field3, is_migration_target,
+ Map::Bits3::IsMigrationTargetBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field, has_prototype_slot,
+ Map::Bits1::HasPrototypeSlotBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field, is_access_check_needed,
+ Map::Bits1::IsAccessCheckNeededBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field, is_callable,
+ Map::Bits1::IsCallableBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field, has_indexed_interceptor,
+ Map::Bits1::HasIndexedInterceptorBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field, is_constructor,
+ Map::Bits1::IsConstructorBit)
+BIMODAL_ACCESSOR_WITH_FLAG_B(Map, bit_field, is_undetectable,
+ Map::Bits1::IsUndetectableBit)
+BIMODAL_ACCESSOR_C(Map, int, instance_size)
+BIMODAL_ACCESSOR_WITH_FLAG_C(Map, int, NextFreePropertyIndex)
+BIMODAL_ACCESSOR_C(Map, int, UnusedPropertyFields)
+BIMODAL_ACCESSOR_WITH_FLAG_C(Map, InstanceType, instance_type)
+BIMODAL_ACCESSOR_WITH_FLAG(Map, Object, GetConstructor)
+BIMODAL_ACCESSOR_WITH_FLAG(Map, HeapObject, GetBackPointer)
+BIMODAL_ACCESSOR_C(Map, bool, is_abandoned_prototype_map)
+
+#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)
+
+BIMODAL_ACCESSOR(RegExpBoilerplateDescription, FixedArray, data)
+BIMODAL_ACCESSOR(RegExpBoilerplateDescription, String, source)
+BIMODAL_ACCESSOR_C(RegExpBoilerplateDescription, int, flags)
+
+base::Optional<CallHandlerInfoRef> FunctionTemplateInfoRef::call_code() const {
+ if (data_->should_access_heap()) {
+ HeapObject call_code = object()->call_code(kAcquireLoad);
+ if (call_code.IsUndefined()) return base::nullopt;
+ return TryMakeRef(broker(), CallHandlerInfo::cast(call_code));
+ }
+ 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()) {
+ return object()->signature().IsUndefined(broker()->isolate());
+ }
+ return data()->AsFunctionTemplateInfo()->is_signature_undefined();
+}
+
+bool FunctionTemplateInfoRef::has_call_code() const {
+ if (data_->should_access_heap()) {
+ HeapObject call_code = object()->call_code(kAcquireLoad);
+ return !call_code.IsUndefined();
+ }
+ return data()->AsFunctionTemplateInfo()->has_call_code();
+}
+
+bool FunctionTemplateInfoRef ::accept_any_receiver() const {
+ if (data_->should_access_heap()) {
+ return object()->accept_any_receiver();
+ }
+ return ObjectRef ::data()->AsFunctionTemplateInfo()->accept_any_receiver();
+}
+
+HolderLookupResult FunctionTemplateInfoRef::LookupHolderOfExpectedType(
+ MapRef receiver_map, SerializationPolicy policy) {
+ const HolderLookupResult not_found;
+
+ if (data_->should_access_heap()) {
+ // There are currently two ways we can see a FunctionTemplateInfo on the
+ // background thread: 1.) As part of a SharedFunctionInfo and 2.) in an
+ // AccessorPair. In both cases, the FTI is fully constructed on the main
+ // thread before.
+ // TODO(nicohartmann@, v8:7790): Once the above no longer holds, we might
+ // have to use the GC predicate to check whether objects are fully
+ // initialized and safe to read.
+ if (!receiver_map.IsJSReceiverMap() ||
+ (receiver_map.is_access_check_needed() &&
+ !object()->accept_any_receiver())) {
+ return not_found;
+ }
+
+ if (!receiver_map.IsJSObjectMap()) return not_found;
+
+ DCHECK(has_call_code());
+
+ DisallowGarbageCollection no_gc;
+ HeapObject signature = object()->signature();
+ if (signature.IsUndefined()) {
+ return HolderLookupResult(CallOptimization::kHolderIsReceiver);
+ }
+ auto expected_receiver_type = FunctionTemplateInfo::cast(signature);
+ if (expected_receiver_type.IsTemplateFor(*receiver_map.object())) {
+ return HolderLookupResult(CallOptimization::kHolderIsReceiver);
+ }
+
+ if (!receiver_map.IsJSGlobalProxyMap()) return not_found;
+ if (policy == SerializationPolicy::kSerializeIfNeeded) {
+ receiver_map.SerializePrototype();
+ }
+ base::Optional<HeapObjectRef> prototype = receiver_map.prototype();
+ if (!prototype.has_value()) return not_found;
+ if (prototype->IsNull()) return not_found;
+
+ JSObject raw_prototype = JSObject::cast(*prototype->object());
+ if (!expected_receiver_type.IsTemplateFor(raw_prototype.map())) {
+ return not_found;
+ }
+ return HolderLookupResult(CallOptimization::kHolderFound,
+ prototype->AsJSObject());
+ }
+
+ 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()->local_isolate_or_isolate(),
+ object());
+ Handle<JSObject> holder = broker()->CanonicalPersistentHandle(
+ call_optimization.LookupHolderOfExpectedType(
+ broker()->local_isolate_or_isolate(), receiver_map.object(),
+ &result.lookup));
+
+ switch (result.lookup) {
+ case CallOptimization::kHolderFound: {
+ result.holder = MakeRef(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)
+BytecodeArrayRef SharedFunctionInfoRef::GetBytecodeArray() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ BytecodeArray bytecode_array;
+ if (!broker()->IsMainThread()) {
+ bytecode_array = object()->GetBytecodeArray(broker()->local_isolate());
+ } else {
+ bytecode_array = object()->GetBytecodeArray(broker()->isolate());
+ }
+ return MakeRef(broker(), bytecode_array);
+ }
+ return BytecodeArrayRef(
+ broker(), ObjectRef ::data()->AsSharedFunctionInfo()->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
+SharedFunctionInfo::Inlineability SharedFunctionInfoRef::GetInlineability()
+ const {
+ if (data_->should_access_heap()) {
+ if (!broker()->IsMainThread()) {
+ return object()->GetInlineability(broker()->local_isolate());
+ } else {
+ return object()->GetInlineability(broker()->isolate());
+ }
+ }
+ return ObjectRef ::data()->AsSharedFunctionInfo()->GetInlineability();
+}
+
+base::Optional<FeedbackVectorRef> FeedbackCellRef::value() const {
+ if (data_->should_access_heap()) {
+ // Note that we use the synchronized accessor.
+ Object value = object()->value(kAcquireLoad);
+ if (!value.IsFeedbackVector()) return base::nullopt;
+ return TryMakeRef(broker(), FeedbackVector::cast(value));
+ }
+ ObjectData* vector = ObjectRef::data()->AsFeedbackCell()->value();
+ return FeedbackVectorRef(broker(), vector->AsFeedbackVector());
+}
+
+base::Optional<ObjectRef> MapRef::GetStrongValue(
+ InternalIndex descriptor_index) const {
+ CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
+ return instance_descriptors().GetStrongValue(descriptor_index);
+}
+
+DescriptorArrayRef MapRef::instance_descriptors() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ return MakeRefAssumeMemoryFence(
+ broker(),
+ object()->instance_descriptors(broker()->isolate(), kAcquireLoad));
+ }
+
+ return DescriptorArrayRef(broker(), data()->AsMap()->instance_descriptors());
+}
+
+base::Optional<HeapObjectRef> MapRef::prototype() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ return TryMakeRef(broker(), HeapObject::cast(object()->prototype()));
+ }
+ ObjectData* prototype_data = data()->AsMap()->prototype();
+ if (prototype_data == nullptr) {
+ TRACE_BROKER_MISSING(broker(), "prototype for map " << *this);
+ return {};
+ }
+ return HeapObjectRef(broker(), prototype_data);
+}
+
+void MapRef::SerializeRootMap() {
+ if (data_->should_access_heap()) return;
+ CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
+ data()->AsMap()->SerializeRootMap(broker());
+}
+
+// TODO(solanes, v8:7790): Remove base::Optional from the return type when
+// deleting serialization.
+base::Optional<MapRef> MapRef::FindRootMap() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // TODO(solanes): Change TryMakeRef to MakeRef when Map is moved to
+ // kNeverSerialized.
+ // TODO(solanes, v8:7790): Consider caching the result of the root map.
+ return TryMakeRef(broker(), 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;
+}
+
+bool JSTypedArrayRef::is_on_heap() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // Safe to read concurrently because:
+ // - host object seen by serializer.
+ // - underlying field written 1. during initialization or 2. with
+ // release-store.
+ return object()->is_on_heap(kAcquireLoad);
+ }
+ return data()->AsJSTypedArray()->data_ptr();
+}
+
+size_t JSTypedArrayRef::length() const {
+ CHECK(!is_on_heap());
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // Safe to read concurrently because:
+ // - immutable after initialization.
+ // - host object seen by serializer.
+ return object()->length();
+ }
+ return data()->AsJSTypedArray()->length();
+}
+
+HeapObjectRef JSTypedArrayRef::buffer() const {
+ CHECK(!is_on_heap());
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // Safe to read concurrently because:
+ // - immutable after initialization.
+ // - host object seen by serializer.
+ return MakeRef<HeapObject>(broker(), object()->buffer());
+ }
+ return HeapObjectRef{broker(), data()->AsJSTypedArray()->buffer()};
+}
+
+void* JSTypedArrayRef::data_ptr() const {
+ CHECK(!is_on_heap());
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // Safe to read concurrently because:
+ // - host object seen by serializer.
+ // - underlying field written 1. during initialization or 2. protected by
+ // the is_on_heap release/acquire semantics (external_pointer store
+ // happens-before base_pointer store, and this external_pointer load
+ // happens-after base_pointer load).
+ STATIC_ASSERT(JSTypedArray::kOffHeapDataPtrEqualsExternalPointer);
+ return object()->DataPtr();
+ }
+ return data()->AsJSTypedArray()->data_ptr();
+}
+
+bool MapRef::IsInobjectSlackTrackingInProgress() const {
+ IF_ACCESS_FROM_HEAP_WITH_FLAG_C(IsInobjectSlackTrackingInProgress);
+ return Map::Bits3::ConstructionCounterBits::decode(
+ data()->AsMap()->bit_field3()) != Map::kNoSlackTracking;
+}
+
+int MapRef::constructor_function_index() const {
+ IF_ACCESS_FROM_HEAP_WITH_FLAG_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_WITH_FLAG_C(CanBeDeprecated);
+ CHECK_GT(NumberOfOwnDescriptors(), 0);
+ return data()->AsMap()->can_be_deprecated();
+}
+
+bool MapRef::CanTransition() const {
+ IF_ACCESS_FROM_HEAP_WITH_FLAG_C(CanTransition);
+ return data()->AsMap()->can_transition();
+}
+
+int MapRef::GetInObjectPropertiesStartInWords() const {
+ IF_ACCESS_FROM_HEAP_WITH_FLAG_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();
+}
+
+ZoneVector<Address> FunctionTemplateInfoRef::c_functions() const {
+ if (data_->should_access_heap()) {
+ return GetCFunctions(FixedArray::cast(object()->GetCFunctionOverloads()),
+ broker()->zone());
+ }
+ return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_functions();
+}
+
+ZoneVector<const CFunctionInfo*> FunctionTemplateInfoRef::c_signatures() const {
+ if (data_->should_access_heap()) {
+ return GetCSignatures(FixedArray::cast(object()->GetCFunctionOverloads()),
+ broker()->zone());
+ }
+ return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_signatures();
+}
+
+bool StringRef::IsSeqString() const {
+ IF_ACCESS_FROM_HEAP_C(IsSeqString);
+ return data()->AsString()->is_seq_string();
+}
+
+bool NativeContextRef::is_unserialized_heap_object() const {
+ return data_->kind() == kUnserializedHeapObject;
+}
+
+ScopeInfoRef NativeContextRef::scope_info() const {
+ if (data_->should_access_heap()) {
+ // The scope_info is immutable after initialization.
+ return MakeRefAssumeMemoryFence(broker(), object()->scope_info());
+ }
+ return ScopeInfoRef(broker(), data()->AsNativeContext()->scope_info());
+}
+
+SharedFunctionInfoRef FeedbackVectorRef::shared_function_info() const {
+ if (data_->should_access_heap()) {
+ return MakeRef(broker(), 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()) {
+ CHECK_LT(index, object()->length());
+ return MakeRefAssumeMemoryFence(broker(),
+ object()->get(index, kAcquireLoad))
+ .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::IsNull() const { return object()->IsNull(); }
+
+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;
+}
+
+base::Optional<bool> ObjectRef::TryGetBooleanValue() const {
+ if (data_->should_access_heap()) {
+ return object()->BooleanValue(broker()->isolate());
+ }
+ if (IsSmi()) return AsSmi() != 0;
+ return data()->AsHeapObject()->TryGetBooleanValue(broker());
+}
+
+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> JSObjectRef::GetOwnConstantElement(
+ uint32_t index, SerializationPolicy policy) const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // `elements` are currently still serialized as members of JSObjectRef.
+ // TODO(jgruber,v8:7790): Once JSObject is no longer serialized, we must
+ // guarantee consistency between `object`, `elements_kind` and `elements`
+ // through other means (store/load order? locks? storing elements_kind in
+ // elements.map?).
+ STATIC_ASSERT(IsSerializedRef<JSObject>());
+
+ base::Optional<FixedArrayBaseRef> maybe_elements_ref = elements();
+ if (!maybe_elements_ref.has_value()) {
+ TRACE_BROKER_MISSING(broker(), "JSObject::elements" << *this);
+ return {};
+ }
+
+ FixedArrayBaseRef elements_ref = maybe_elements_ref.value();
+ ElementsKind elements_kind = GetElementsKind();
+
+ DCHECK_LE(index, JSObject::kMaxElementIndex);
+
+ // See also ElementsAccessorBase::GetMaxIndex.
+ if (IsJSArray()) {
+ // For JSArrays we additionally need to check against JSArray::length.
+ // Length_unsafe is safe to use in this case since:
+ // - GetOwnConstantElement only detects a constant for JSArray holders if
+ // the array is frozen/sealed.
+ // - Frozen/sealed arrays can't change length.
+ // - We've already seen a map with frozen/sealed elements_kinds (above);
+ // - The release-load of that map ensures we read the newest value
+ // of `length` below.
+ uint32_t array_length;
+ if (!AsJSArray().length_unsafe().object()->ToArrayLength(&array_length)) {
+ return {};
+ }
+ if (index >= array_length) return {};
+ }
+
+ Object maybe_element;
+ auto result = ConcurrentLookupIterator::TryGetOwnConstantElement(
+ &maybe_element, broker()->isolate(), broker()->local_isolate(),
+ *object(), *elements_ref.object(), elements_kind, index);
+
+ if (result == ConcurrentLookupIterator::kGaveUp) {
+ TRACE_BROKER_MISSING(broker(), "JSObject::GetOwnConstantElement on "
+ << *this << " at index " << index);
+ return {};
+ } else if (result == ConcurrentLookupIterator::kNotPresent) {
+ return {};
+ }
+
+ DCHECK_EQ(result, ConcurrentLookupIterator::kPresent);
+ return MakeRef(broker(), maybe_element);
+ } else {
+ ObjectData* element =
+ data()->AsJSObject()->GetOwnConstantElement(broker(), index, policy);
+ if (element == nullptr) return base::nullopt;
+ return ObjectRef(broker(), element);
+ }
+}
+
+base::Optional<ObjectRef> JSObjectRef::GetOwnFastDataProperty(
+ Representation field_representation, FieldIndex index,
+ SerializationPolicy policy) const {
+ if (data_->should_access_heap()) {
+ return GetOwnFastDataPropertyFromHeap(broker(),
+ Handle<JSObject>::cast(object()),
+ field_representation, index);
+ }
+ ObjectData* property = data()->AsJSObject()->GetOwnFastDataProperty(
+ broker(), field_representation, index, policy);
+ if (property == nullptr) return base::nullopt;
+ return ObjectRef(broker(), property);
+}
+
+ObjectRef JSObjectRef::GetOwnDictionaryProperty(
+ InternalIndex index, SerializationPolicy policy) const {
+ CHECK(index.is_found());
+ if (data_->should_access_heap()) {
+ return GetOwnDictionaryPropertyFromHeap(
+ broker(), Handle<JSObject>::cast(object()), index);
+ }
+ ObjectData* property =
+ data()->AsJSObject()->GetOwnDictionaryProperty(broker(), index, policy);
+ CHECK_NE(property, nullptr);
+ return ObjectRef(broker(), property);
+}
+
+ObjectRef JSArrayRef::GetBoilerplateLength() const {
+ // Safe to read concurrently because:
+ // - boilerplates are immutable after initialization.
+ // - boilerplates are published into the feedback vector.
+ return length_unsafe();
+}
+
+ObjectRef JSArrayRef::length_unsafe() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ return MakeRef(broker(),
+ object()->length(broker()->isolate(), kRelaxedLoad));
+ } else {
+ return ObjectRef{broker(), data()->AsJSArray()->length()};
+ }
+}
+
+base::Optional<ObjectRef> JSArrayRef::GetOwnCowElement(
+ FixedArrayBaseRef elements_ref, uint32_t index,
+ SerializationPolicy policy) const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // `elements` are currently still serialized as members of JSObjectRef.
+ // TODO(jgruber,v8:7790): Remove the elements equality DCHECK below once
+ // JSObject is no longer serialized.
+ static_assert(std::is_base_of<JSObject, JSArray>::value, "");
+ STATIC_ASSERT(IsSerializedRef<JSObject>());
+
+ // The elements_ref is passed in by callers to make explicit that it is
+ // also used outside of this function, and must match the `elements` used
+ // inside this function.
+ DCHECK(elements_ref.equals(elements().value()));
+
+ // Due to concurrency, the kind read here may not be consistent with
+ // `elements_ref`. But consistency is guaranteed at runtime due to the
+ // `elements` equality check in the caller.
+ ElementsKind elements_kind = GetElementsKind();
+
+ // We only inspect fixed COW arrays, which may only occur for fast
+ // smi/objects elements kinds.
+ if (!IsSmiOrObjectElementsKind(elements_kind)) return {};
+ DCHECK(IsFastElementsKind(elements_kind));
+ if (!elements_ref.map().IsFixedCowArrayMap()) return {};
+
+ // As the name says, the `length` read here is unsafe and may not match
+ // `elements`. We rely on the invariant that any `length` change will
+ // also result in an `elements` change to make this safe. The `elements`
+ // equality check in the caller thus also guards the value of `length`.
+ ObjectRef length_ref = length_unsafe();
+
+ // Likewise we only deal with smi lengths.
+ if (!length_ref.IsSmi()) return {};
+
+ base::Optional<Object> result =
+ ConcurrentLookupIterator::TryGetOwnCowElement(
+ broker()->isolate(), *elements_ref.AsFixedArray().object(),
+ elements_kind, length_ref.AsSmi(), index);
+ if (!result.has_value()) return {};
+
+ return MakeRef(broker(), result.value());
+ } else {
+ DCHECK(!data_->should_access_heap());
+ DCHECK(!broker()->is_concurrent_inlining());
+
+ // Just to clarify that `elements_ref` is not used on this path.
+ // GetOwnElement accesses the serialized `elements` field on its own.
+ USE(elements_ref);
+
+ if (!elements().value().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()) {
+ return TryMakeRef(broker(), object()->GetCell(cell_index));
+ }
+ ObjectData* cell =
+ data()->AsSourceTextModule()->GetCell(broker(), cell_index);
+ if (cell == nullptr) return base::nullopt;
+ return CellRef(broker(), cell);
+}
+
+base::Optional<ObjectRef> SourceTextModuleRef::import_meta() const {
+ if (data_->should_access_heap()) {
+ return TryMakeRef(broker(), object()->import_meta(kAcquireLoad));
+ }
+ return ObjectRef(broker(),
+ data()->AsSourceTextModule()->GetImportMeta(broker()));
+}
+
+ObjectRef::ObjectRef(JSHeapBroker* broker, Handle<Object> object,
+ bool check_type)
+ : broker_(broker) {
+ CHECK_NE(broker->mode(), JSHeapBroker::kRetired);
+ data_ = broker->GetOrCreateData(object);
+}
+
+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()) {
+ 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()) {
+ return TryMakeRef(broker(), object()->boilerplate(kAcquireLoad));
+ }
+ ObjectData* boilerplate = data()->AsAllocationSite()->boilerplate();
+ if (boilerplate) {
+ return JSObjectRef(broker(), boilerplate);
+ } else {
+ return base::nullopt;
+ }
+}
+
+ElementsKind JSObjectRef::GetElementsKind() const {
+ return map().elements_kind();
+}
+
+base::Optional<FixedArrayBaseRef> JSObjectRef::elements() const {
+ if (data_->should_access_heap()) {
+ return TryMakeRef(broker(), object()->elements());
+ }
+ const JSObjectData* d = data()->AsJSObject();
+ if (!d->serialized_elements()) {
+ TRACE(broker(), "'elements' on " << this);
+ return base::nullopt;
+ }
+ return FixedArrayBaseRef(broker(), d->elements());
+}
+
+int FixedArrayBaseRef::length() const {
+ IF_ACCESS_FROM_HEAP_C(length);
+ return data()->AsFixedArrayBase()->length();
+}
+
+PropertyDetails DescriptorArrayRef::GetPropertyDetails(
+ InternalIndex descriptor_index) const {
+ if (data_->should_access_heap()) {
+ return object()->GetDetails(descriptor_index);
+ }
+ return data()->AsDescriptorArray()->GetPropertyDetails(descriptor_index);
+}
+
+NameRef DescriptorArrayRef::GetPropertyKey(
+ InternalIndex descriptor_index) const {
+ if (data_->should_access_heap()) {
+ NameRef result = MakeRef(broker(), object()->GetKey(descriptor_index));
+ CHECK(result.IsUniqueName());
+ return result;
+ }
+ return NameRef(broker(),
+ data()->AsDescriptorArray()->GetPropertyKey(descriptor_index));
+}
+
+ObjectRef DescriptorArrayRef::GetFieldType(
+ InternalIndex descriptor_index) const {
+ if (data_->should_access_heap()) {
+ return MakeRef<Object>(broker(), object()->GetFieldType(descriptor_index));
+ }
+ return ObjectRef(broker(),
+ data()->AsDescriptorArray()->GetFieldType(descriptor_index));
+}
+
+base::Optional<ObjectRef> DescriptorArrayRef::GetStrongValue(
+ InternalIndex descriptor_index) const {
+ if (data_->should_access_heap()) {
+ HeapObject heap_object;
+ if (!object()
+ ->GetValue(descriptor_index)
+ .GetHeapObjectIfStrong(&heap_object)) {
+ return {};
+ }
+ // Since the descriptors in the descriptor array can be changed in-place
+ // via DescriptorArray::Replace, we might get a value that we haven't seen
+ // before.
+ return TryMakeRef(broker(), heap_object);
+ }
+ ObjectData* value =
+ data()->AsDescriptorArray()->GetStrongValue(descriptor_index);
+ if (!value) return base::nullopt;
+ return ObjectRef(broker(), value);
+}
+
+base::Optional<SharedFunctionInfoRef> FeedbackCellRef::shared_function_info()
+ const {
+ if (value()) {
+ FeedbackVectorRef vector = *value();
+ if (vector.serialized()) {
+ return vector.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();
+}
+
+void RegExpBoilerplateDescriptionRef::Serialize() {
+ if (data_->should_access_heap()) return;
+ CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
+ HeapObjectRef::data()->AsRegExpBoilerplateDescription()->Serialize(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_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),
+ state_(State::kUnserialized),
+ function_maps_(broker->zone()) {}
+
+void NativeContextData::Serialize(JSHeapBroker* broker) {
+ if (state_ != State::kUnserialized) return;
+ state_ = State::kSerializedOnMainThread;
+
+ 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##_->IsMap() && \
+ !InstanceTypeChecker::IsContext(name##_->AsMap()->instance_type())) { \
+ name##_->AsMap()->SerializeConstructor(broker); \
+ } \
+ if (name##_->IsJSFunction()) { \
+ name##_->AsJSFunction()->Serialize(broker); \
+ } \
+ }
+ BROKER_COMPULSORY_NATIVE_CONTEXT_FIELDS(SERIALIZE_MEMBER)
+ if (!broker->is_isolate_bootstrapping()) {
+ 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);
+ }
+
+ scope_info_ = broker->GetOrCreateData(context->scope_info());
+}
+
+void NativeContextData::SerializeOnBackground(JSHeapBroker* broker) {
+ if (state_ == State::kFullySerialized) return;
+ DCHECK_EQ(state_, State::kSerializedOnMainThread);
+ state_ = State::kSerializedOnMainThread;
+
+ UnparkedScopeIfNeeded unparked_scope(broker);
+ TraceScope tracer(broker, this, "NativeContextData::SerializeOnBackground");
+ Handle<NativeContext> context = Handle<NativeContext>::cast(object());
+
+#define SERIALIZE_MEMBER(type, name) \
+ DCHECK_NULL(name##_); \
+ name##_ = broker->GetOrCreateData(context->name(kAcquireLoad), \
+ kAssumeMemoryFence); \
+ if (!name##_->should_access_heap()) { \
+ DCHECK(!name##_->IsJSFunction()); \
+ }
+ BROKER_COMPULSORY_BACKGROUND_NATIVE_CONTEXT_FIELDS(SERIALIZE_MEMBER)
+ if (!broker->is_isolate_bootstrapping()) {
+ BROKER_OPTIONAL_BACKGROUND_NATIVE_CONTEXT_FIELDS(SERIALIZE_MEMBER)
+ }
+#undef SERIALIZE_MEMBER
+
+ DCHECK(function_maps_.empty());
+ int const first = Context::FIRST_FUNCTION_MAP_INDEX;
+ int const last = Context::LAST_FUNCTION_MAP_INDEX;
+ function_maps_.reserve(last + 1 - first);
+ for (int i = first; i <= last; ++i) {
+ function_maps_.push_back(broker->GetOrCreateData(
+ context->get(i, kAcquireLoad), kAssumeMemoryFence));
+ }
+}
+
+void JSFunctionRef::Serialize() {
+ if (data_->should_access_heap()) return;
+ CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
+ data()->AsJSFunction()->Serialize(broker());
+}
+
+void JSFunctionRef::SerializeCodeAndFeedback() {
+ if (data_->should_access_heap()) return;
+ CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
+ data()->AsJSFunction()->SerializeCodeAndFeedback(broker());
+}
+
+bool JSBoundFunctionRef::serialized() const {
+ if (data_->should_access_heap()) return true;
+ if (data_->AsJSBoundFunction()->serialized()) return true;
+ TRACE_BROKER_MISSING(broker(), "data for JSBoundFunction " << this);
+ return false;
+}
+
+bool JSFunctionRef::serialized() const {
+ if (data_->should_access_heap()) return true;
+ if (data_->AsJSFunction()->serialized()) return true;
+ TRACE_BROKER_MISSING(broker(), "data for JSFunction " << this);
+ return false;
+}
+
+bool JSFunctionRef::serialized_code_and_feedback() const {
+ if (data_->should_access_heap()) return true;
+ return data()->AsJSFunction()->serialized_code_and_feedback();
+}
+
+CodeRef JSFunctionRef::code() const {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ return MakeRefAssumeMemoryFence(broker(), object()->code(kAcquireLoad));
+ }
+
+ return CodeRef(broker(), ObjectRef::data()->AsJSFunction()->code());
+}
+
+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 {};
+ return TryMakeRef(broker(), FunctionTemplateInfo::cast(
+ object()->function_data(kAcquireLoad)));
+ }
+ 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()) {
+ return MakeRef(broker(), object()->scope_info());
+ }
+ return ScopeInfoRef(broker(), data()->AsSharedFunctionInfo()->scope_info());
+}
+
+void JSObjectRef::SerializeObjectCreateMap() {
+ if (data_->should_access_heap()) return;
+ CHECK_IMPLIES(!FLAG_turbo_concurrent_get_property_access_info,
+ broker()->mode() == JSHeapBroker::kSerializing);
+ data()->AsJSObject()->SerializeObjectCreateMap(broker());
+}
+
+bool MapRef::TrySerializeOwnDescriptor(InternalIndex descriptor_index) {
+ CHECK_LT(descriptor_index.as_int(), NumberOfOwnDescriptors());
+ if (data_->should_access_heap()) return true;
+ CHECK_IMPLIES(!FLAG_turbo_concurrent_get_property_access_info,
+ broker()->mode() == JSHeapBroker::kSerializing);
+ return data()->AsMap()->TrySerializeOwnDescriptor(broker(), descriptor_index);
+}
+
+void MapRef::SerializeOwnDescriptor(InternalIndex descriptor_index) {
+ CHECK(TrySerializeOwnDescriptor(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;
+ ObjectData* maybe_desc_array_data = data()->AsMap()->instance_descriptors();
+ if (!maybe_desc_array_data) return false;
+ if (maybe_desc_array_data->should_access_heap()) return true;
+ DescriptorArrayData* desc_array_data =
+ maybe_desc_array_data->AsDescriptorArray();
+ return desc_array_data->serialized_descriptor(descriptor_index);
+}
+
+void MapRef::SerializeBackPointer() {
+ if (data_->should_access_heap()) return;
+ CHECK_IMPLIES(!FLAG_turbo_concurrent_get_property_access_info,
+ broker()->mode() == JSHeapBroker::kSerializing);
+ data()->AsMap()->SerializeBackPointer(broker());
+}
+
+bool MapRef::TrySerializePrototype() {
+ if (data_->should_access_heap()) return true;
+ CHECK_IMPLIES(!FLAG_turbo_concurrent_get_property_access_info,
+ broker()->mode() == JSHeapBroker::kSerializing);
+ return data()->AsMap()->TrySerializePrototype(broker());
+}
+
+void MapRef::SerializePrototype() { CHECK(TrySerializePrototype()); }
+
+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 NativeContextRef::SerializeOnBackground() {
+ if (data_->should_access_heap()) return;
+ CHECK(broker()->mode() == JSHeapBroker::kSerializing ||
+ broker()->mode() == JSHeapBroker::kSerialized);
+ data()->AsNativeContext()->SerializeOnBackground(broker());
+}
+
+void JSTypedArrayRef::Serialize() {
+ if (data_->should_access_heap() || broker()->is_concurrent_inlining()) {
+ // Even if the typed array object itself is no longer serialized (besides
+ // the JSObject parts), the `buffer` field still is and thus we need to
+ // make sure to visit it.
+ // TODO(jgruber,v8:7790): Remove once JSObject is no longer serialized.
+ static_assert(
+ std::is_base_of<JSObject, decltype(object()->buffer())>::value, "");
+ STATIC_ASSERT(IsSerializedRef<JSObject>());
+ MakeRef<JSObject>(broker(), object()->buffer());
+ } else {
+ CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
+ data()->AsJSTypedArray()->Serialize(broker());
+ }
+}
+
+bool JSTypedArrayRef::serialized() const {
+ if (data_->should_access_heap()) return true;
+ if (broker()->is_concurrent_inlining()) return true;
+ if (data_->AsJSTypedArray()->serialized()) return true;
+ TRACE_BROKER_MISSING(broker(), "data for JSTypedArray " << this);
+ return false;
+}
+
+bool JSBoundFunctionRef::Serialize() {
+ if (data_->should_access_heap()) return true;
+ CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
+ return data()->AsJSBoundFunction()->Serialize(broker());
+}
+
+bool PropertyCellRef::Serialize() const {
+ if (data_->should_access_heap()) return true;
+ CHECK(broker()->mode() == JSHeapBroker::kSerializing ||
+ broker()->mode() == JSHeapBroker::kSerialized);
+ return data()->AsPropertyCell()->Serialize(broker());
+}
+
+void FunctionTemplateInfoRef::SerializeCallCode() {
+ if (data_->should_access_heap()) {
+ CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
+ // CallHandlerInfo::data may still hold a serialized heap object, so we
+ // have to make the broker aware of it.
+ // TODO(v8:7790): Remove this case once ObjectRef is never serialized.
+ Handle<HeapObject> call_code(object()->call_code(kAcquireLoad),
+ broker()->isolate());
+ if (call_code->IsCallHandlerInfo()) {
+ broker()->GetOrCreateData(
+ Handle<CallHandlerInfo>::cast(call_code)->data());
+ }
+ 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);
+}
+
+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()) {
+ return os << ref.data() << " {" << ref.object() << "}";
+ } else {
+ return os << ref.data();
+ }
+}
+
+unsigned CodeRef::GetInlinedBytecodeSize() const {
+ if (data_->should_access_heap()) {
+ unsigned value = object()->inlined_bytecode_size();
+ if (value > 0) {
+ // Don't report inlined bytecode size if the code object was already
+ // deoptimized.
+ value = object()->marked_for_deoptimization() ? 0 : value;
+ }
+ return value;
+ }
+
+ return ObjectRef::data()->AsCode()->inlined_bytecode_size();
+}
+
+#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