// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/compiler/js-heap-broker.h" #include "src/compiler/graph-reducer.h" #include "src/objects-inl.h" #include "src/objects/js-array-inl.h" #include "src/objects/js-regexp-inl.h" #include "src/objects/module-inl.h" #include "src/utils.h" namespace v8 { namespace internal { namespace compiler { #define FORWARD_DECL(Name) class Name##Data; HEAP_BROKER_OBJECT_LIST(FORWARD_DECL) #undef FORWARD_DECL // TODO(neis): It would be nice to share the serialized data for read-only // objects. class ObjectData : public ZoneObject { public: static ObjectData* Serialize(JSHeapBroker* broker, Handle<Object> object); ObjectData(JSHeapBroker* broker_, Handle<Object> object_, bool is_smi_) : broker(broker_), object(object_), is_smi(is_smi_) { broker->AddData(object, this); } #define DECLARE_IS_AND_AS(Name) \ bool Is##Name() const; \ Name##Data* As##Name(); HEAP_BROKER_OBJECT_LIST(DECLARE_IS_AND_AS) #undef DECLARE_IS_AND_AS JSHeapBroker* const broker; Handle<Object> const object; bool const is_smi; }; // TODO(neis): Perhaps add a boolean that indicates whether serialization of an // object has completed. That could be used to add safety checks. #define GET_OR_CREATE(name) \ broker->GetOrCreateData(handle(object_->name(), broker->isolate())) class HeapObjectData : public ObjectData { public: static HeapObjectData* Serialize(JSHeapBroker* broker, Handle<HeapObject> object); HeapObjectType const type; MapData* const map; HeapObjectData(JSHeapBroker* broker_, Handle<HeapObject> object_, HeapObjectType type_) : ObjectData(broker_, object_, false), type(type_), map(GET_OR_CREATE(map)->AsMap()) { CHECK(broker_->SerializingAllowed()); } }; class PropertyCellData : public HeapObjectData { public: PropertyCellData(JSHeapBroker* broker_, Handle<PropertyCell> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class JSObjectData : public HeapObjectData { public: JSObjectData(JSHeapBroker* broker_, Handle<JSObject> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class JSFunctionData : public JSObjectData { public: JSGlobalProxyData* const global_proxy; MapData* const initial_map; // Can be nullptr. bool const has_prototype; ObjectData* const prototype; // Can be nullptr. bool const PrototypeRequiresRuntimeLookup; SharedFunctionInfoData* const shared; JSFunctionData(JSHeapBroker* broker_, Handle<JSFunction> object_, HeapObjectType type_); }; class JSRegExpData : public JSObjectData { public: JSRegExpData(JSHeapBroker* broker_, Handle<JSRegExp> object_, HeapObjectType type_) : JSObjectData(broker_, object_, type_) {} }; class HeapNumberData : public HeapObjectData { public: HeapNumberData(JSHeapBroker* broker_, Handle<HeapNumber> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class MutableHeapNumberData : public HeapObjectData { public: MutableHeapNumberData(JSHeapBroker* broker_, Handle<MutableHeapNumber> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class ContextData : public HeapObjectData { public: ContextData(JSHeapBroker* broker_, Handle<Context> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class NativeContextData : public ContextData { public: #define DECL_MEMBER(type, name) type##Data* const name; BROKER_NATIVE_CONTEXT_FIELDS(DECL_MEMBER) #undef DECL_MEMBER NativeContextData(JSHeapBroker* broker_, Handle<NativeContext> object_, HeapObjectType type_) : ContextData(broker_, object_, type_) #define INIT_MEMBER(type, name) , name(GET_OR_CREATE(name)->As##type()) BROKER_NATIVE_CONTEXT_FIELDS(INIT_MEMBER) #undef INIT_MEMBER { } }; class NameData : public HeapObjectData { public: NameData(JSHeapBroker* broker, Handle<Name> object, HeapObjectType type) : HeapObjectData(broker, object, type) {} }; class StringData : public NameData { public: StringData(JSHeapBroker* broker, Handle<String> object, HeapObjectType type) : NameData(broker, object, type), length(object->length()), first_char(length > 0 ? object->Get(0) : 0) { int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY; if (length <= kMaxLengthForDoubleConversion) { to_number = StringToDouble( broker->isolate(), broker->isolate()->unicode_cache(), object, flags); } } int const length; uint16_t const first_char; base::Optional<double> to_number; private: static constexpr int kMaxLengthForDoubleConversion = 23; }; class InternalizedStringData : public StringData { public: InternalizedStringData(JSHeapBroker* broker, Handle<InternalizedString> object, HeapObjectType type) : StringData(broker, object, type) {} }; namespace { bool IsFastLiteralHelper(Handle<JSObject> boilerplate, int max_depth, int* max_properties) { DCHECK_GE(max_depth, 0); DCHECK_GE(*max_properties, 0); // Make sure the boilerplate map is not deprecated. if (!JSObject::TryMigrateInstance(boilerplate)) return false; // Check for too deep nesting. if (max_depth == 0) return false; // Check the elements. Isolate* const isolate = boilerplate->GetIsolate(); Handle<FixedArrayBase> elements(boilerplate->elements(), isolate); if (elements->length() > 0 && elements->map() != ReadOnlyRoots(isolate).fixed_cow_array_map()) { if (boilerplate->HasSmiOrObjectElements()) { Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements); int length = elements->length(); for (int i = 0; i < length; i++) { if ((*max_properties)-- == 0) return false; Handle<Object> value(fast_elements->get(i), isolate); if (value->IsJSObject()) { Handle<JSObject> value_object = Handle<JSObject>::cast(value); if (!IsFastLiteralHelper(value_object, max_depth - 1, max_properties)) { return false; } } } } else if (boilerplate->HasDoubleElements()) { if (elements->Size() > kMaxRegularHeapObjectSize) return false; } else { return false; } } // TODO(turbofan): Do we want to support out-of-object properties? if (!(boilerplate->HasFastProperties() && boilerplate->property_array()->length() == 0)) { return false; } // Check the in-object properties. Handle<DescriptorArray> descriptors( boilerplate->map()->instance_descriptors(), isolate); int limit = boilerplate->map()->NumberOfOwnDescriptors(); for (int i = 0; i < limit; i++) { PropertyDetails details = descriptors->GetDetails(i); if (details.location() != kField) continue; DCHECK_EQ(kData, details.kind()); if ((*max_properties)-- == 0) return false; FieldIndex field_index = FieldIndex::ForDescriptor(boilerplate->map(), i); if (boilerplate->IsUnboxedDoubleField(field_index)) continue; Handle<Object> value(boilerplate->RawFastPropertyAt(field_index), isolate); if (value->IsJSObject()) { Handle<JSObject> value_object = Handle<JSObject>::cast(value); if (!IsFastLiteralHelper(value_object, max_depth - 1, max_properties)) { return false; } } } return true; } // Maximum depth and total number of elements and properties for literal // graphs to be considered for fast deep-copying. The limit is chosen to // match the maximum number of inobject properties, to ensure that the // performance of using object literals is not worse than using constructor // functions, see crbug.com/v8/6211 for details. const int kMaxFastLiteralDepth = 3; const int kMaxFastLiteralProperties = JSObject::kMaxInObjectProperties; // Determines whether the given array or object literal boilerplate satisfies // all limits to be considered for fast deep-copying and computes the total // size of all objects that are part of the graph. bool IsInlinableFastLiteral(Handle<JSObject> boilerplate) { int max_properties = kMaxFastLiteralProperties; return IsFastLiteralHelper(boilerplate, kMaxFastLiteralDepth, &max_properties); } } // namespace class AllocationSiteData : public HeapObjectData { public: AllocationSiteData(JSHeapBroker* broker, Handle<AllocationSite> object_, HeapObjectType type_) : HeapObjectData(broker, object_, type_), PointsToLiteral(object_->PointsToLiteral()), GetPretenureMode(object_->GetPretenureMode()), nested_site(GET_OR_CREATE(nested_site)) { if (PointsToLiteral) { if (IsInlinableFastLiteral( handle(object_->boilerplate(), broker->isolate()))) { boilerplate = GET_OR_CREATE(boilerplate)->AsJSObject(); } } else { GetElementsKind = object_->GetElementsKind(); CanInlineCall = object_->CanInlineCall(); } } bool const PointsToLiteral; PretenureFlag const GetPretenureMode; ObjectData* const nested_site; JSObjectData* boilerplate = nullptr; // These are only valid if PointsToLiteral is false. ElementsKind GetElementsKind = NO_ELEMENTS; bool CanInlineCall = false; }; // Only used in JSNativeContextSpecialization. class ScriptContextTableData : public HeapObjectData { public: ScriptContextTableData(JSHeapBroker* broker_, Handle<ScriptContextTable> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class MapData : public HeapObjectData { public: InstanceType const instance_type; int const instance_size; byte const bit_field; byte const bit_field2; uint32_t const bit_field3; MapData(JSHeapBroker* broker_, Handle<Map> object_, HeapObjectType type_); // Extra information. void SerializeElementsKindGeneralizations(); const ZoneVector<MapData*>& elements_kind_generalizations() { return elements_kind_generalizations_; } private: ZoneVector<MapData*> elements_kind_generalizations_; }; MapData::MapData(JSHeapBroker* broker_, Handle<Map> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_), instance_type(object_->instance_type()), instance_size(object_->instance_size()), bit_field(object_->bit_field()), bit_field2(object_->bit_field2()), bit_field3(object_->bit_field3()), elements_kind_generalizations_(broker->zone()) {} JSFunctionData::JSFunctionData(JSHeapBroker* broker_, Handle<JSFunction> object_, HeapObjectType type_) : JSObjectData(broker_, object_, type_), global_proxy(GET_OR_CREATE(global_proxy)->AsJSGlobalProxy()), initial_map(object_->has_prototype_slot() && object_->has_initial_map() ? GET_OR_CREATE(initial_map)->AsMap() : nullptr), has_prototype(object_->has_prototype_slot() && object_->has_prototype()), prototype(has_prototype ? GET_OR_CREATE(prototype) : nullptr), PrototypeRequiresRuntimeLookup(object_->PrototypeRequiresRuntimeLookup()), shared(GET_OR_CREATE(shared)->AsSharedFunctionInfo()) { if (initial_map != nullptr && initial_map->instance_type == JS_ARRAY_TYPE) { initial_map->SerializeElementsKindGeneralizations(); } } void MapData::SerializeElementsKindGeneralizations() { broker->Trace("Computing ElementsKind generalizations of %p.\n", *object); DCHECK_EQ(instance_type, JS_ARRAY_TYPE); MapRef self(this); ElementsKind from_kind = self.elements_kind(); 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<Map>(), to_kind); elements_kind_generalizations_.push_back( broker->GetOrCreateData(target)->AsMap()); } } } class FeedbackVectorData : public HeapObjectData { public: const ZoneVector<ObjectData*>& feedback() { return feedback_; } FeedbackVectorData(JSHeapBroker* broker_, Handle<FeedbackVector> object_, HeapObjectType type_); private: ZoneVector<ObjectData*> feedback_; }; FeedbackVectorData::FeedbackVectorData(JSHeapBroker* broker_, Handle<FeedbackVector> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_), feedback_(broker_->zone()) { feedback_.reserve(object_->length()); for (int i = 0; i < object_->length(); ++i) { MaybeObject* value = object_->get(i); feedback_.push_back(value->IsObject() ? broker->GetOrCreateData( handle(value->ToObject(), broker->isolate())) : nullptr); } DCHECK_EQ(object_->length(), feedback_.size()); } class FixedArrayBaseData : public HeapObjectData { public: int const length; FixedArrayBaseData(JSHeapBroker* broker_, Handle<FixedArrayBase> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_), length(object_->length()) {} }; class FixedArrayData : public FixedArrayBaseData { public: FixedArrayData(JSHeapBroker* broker_, Handle<FixedArray> object_, HeapObjectType type_) : FixedArrayBaseData(broker_, object_, type_) {} }; class FixedDoubleArrayData : public FixedArrayBaseData { public: FixedDoubleArrayData(JSHeapBroker* broker_, Handle<FixedDoubleArray> object_, HeapObjectType type_) : FixedArrayBaseData(broker_, object_, type_) {} }; class BytecodeArrayData : public FixedArrayBaseData { public: int const register_count; BytecodeArrayData(JSHeapBroker* broker_, Handle<BytecodeArray> object_, HeapObjectType type_) : FixedArrayBaseData(broker_, object_, type_), register_count(object_->register_count()) {} }; class JSArrayData : public JSObjectData { public: JSArrayData(JSHeapBroker* broker_, Handle<JSArray> object_, HeapObjectType type_) : JSObjectData(broker_, object_, type_) {} }; class ScopeInfoData : public HeapObjectData { public: ScopeInfoData(JSHeapBroker* broker_, Handle<ScopeInfo> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class SharedFunctionInfoData : public HeapObjectData { public: int const builtin_id; BytecodeArrayData* const GetBytecodeArray; // Can be nullptr. #define DECL_MEMBER(type, name) type const name; BROKER_SFI_FIELDS(DECL_MEMBER) #undef DECL_MEMBER SharedFunctionInfoData(JSHeapBroker* broker_, Handle<SharedFunctionInfo> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_), builtin_id(object_->HasBuiltinId() ? object_->builtin_id() : Builtins::kNoBuiltinId), GetBytecodeArray( object_->HasBytecodeArray() ? GET_OR_CREATE(GetBytecodeArray)->AsBytecodeArray() : nullptr) #define INIT_MEMBER(type, name) , name(object_->name()) BROKER_SFI_FIELDS(INIT_MEMBER) #undef INIT_MEMBER { DCHECK_EQ(HasBuiltinId, builtin_id != Builtins::kNoBuiltinId); DCHECK_EQ(HasBytecodeArray, GetBytecodeArray != nullptr); } }; class ModuleData : public HeapObjectData { public: ModuleData(JSHeapBroker* broker_, Handle<Module> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class CellData : public HeapObjectData { public: CellData(JSHeapBroker* broker_, Handle<Cell> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; class JSGlobalProxyData : public JSObjectData { public: JSGlobalProxyData(JSHeapBroker* broker_, Handle<JSGlobalProxy> object_, HeapObjectType type_) : JSObjectData(broker_, object_, type_) {} }; class CodeData : public HeapObjectData { public: CodeData(JSHeapBroker* broker_, Handle<Code> object_, HeapObjectType type_) : HeapObjectData(broker_, object_, type_) {} }; #define DEFINE_IS_AND_AS(Name) \ bool ObjectData::Is##Name() const { \ if (broker->mode() == JSHeapBroker::kDisabled) { \ AllowHandleDereference allow_handle_dereference; \ return object->Is##Name(); \ } \ if (is_smi) return false; \ InstanceType instance_type = \ static_cast<const HeapObjectData*>(this)->type.instance_type(); \ return InstanceTypeChecker::Is##Name(instance_type); \ } \ Name##Data* ObjectData::As##Name() { \ CHECK_NE(broker->mode(), JSHeapBroker::kDisabled); \ CHECK(Is##Name()); \ return static_cast<Name##Data*>(this); \ } HEAP_BROKER_OBJECT_LIST(DEFINE_IS_AND_AS) #undef DEFINE_IS_AND_AS ObjectData* ObjectData::Serialize(JSHeapBroker* broker, Handle<Object> object) { CHECK(broker->SerializingAllowed()); return object->IsSmi() ? new (broker->zone()) ObjectData(broker, object, true) : HeapObjectData::Serialize( broker, Handle<HeapObject>::cast(object)); } HeapObjectData* HeapObjectData::Serialize(JSHeapBroker* broker, Handle<HeapObject> object) { CHECK(broker->SerializingAllowed()); Handle<Map> map(object->map(), broker->isolate()); HeapObjectType type = broker->HeapObjectTypeFromMap(map); #define RETURN_CREATE_DATA_IF_MATCH(name) \ if (object->Is##name()) { \ return new (broker->zone()) \ name##Data(broker, Handle<name>::cast(object), type); \ } HEAP_BROKER_OBJECT_LIST(RETURN_CREATE_DATA_IF_MATCH) #undef RETURN_CREATE_DATA_IF_MATCH UNREACHABLE(); } bool ObjectRef::equals(const ObjectRef& other) const { return data_ == other.data_; } StringRef ObjectRef::TypeOf() const { AllowHandleAllocation handle_allocation; AllowHandleDereference handle_dereference; return StringRef(broker(), Object::TypeOf(broker()->isolate(), object<Object>())); } Isolate* ObjectRef::isolate() const { return broker()->isolate(); } base::Optional<ContextRef> ContextRef::previous() const { AllowHandleAllocation handle_allocation; AllowHandleDereference handle_dereference; Context* previous = object<Context>()->previous(); if (previous == nullptr) return base::Optional<ContextRef>(); return ContextRef(broker(), handle(previous, broker()->isolate())); } ObjectRef ContextRef::get(int index) const { AllowHandleAllocation handle_allocation; AllowHandleDereference handle_dereference; Handle<Object> value(object<Context>()->get(index), broker()->isolate()); return ObjectRef(broker(), value); } JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* zone) : isolate_(isolate), zone_(zone), refs_(zone), mode_(FLAG_concurrent_compiler_frontend ? kSerializing : kDisabled) { Trace("%s", "Constructing heap broker.\n"); } void JSHeapBroker::Trace(const char* format, ...) const { if (FLAG_trace_heap_broker) { PrintF("[%p] ", this); va_list arguments; va_start(arguments, format); base::OS::VPrint(format, arguments); va_end(arguments); } } bool JSHeapBroker::SerializingAllowed() const { return mode() == kSerializing || (!FLAG_strict_heap_broker && mode() == kSerialized); } void JSHeapBroker::SerializeStandardObjects() { Trace("Serializing standard objects.\n"); Builtins* const b = isolate()->builtins(); Factory* const f = isolate()->factory(); // Stuff used by JSGraph: GetOrCreateData(f->empty_fixed_array()); // Stuff used by JSCreateLowering: GetOrCreateData(f->block_context_map()); GetOrCreateData(f->catch_context_map()); GetOrCreateData(f->eval_context_map()); GetOrCreateData(f->fixed_array_map()); GetOrCreateData(f->fixed_double_array_map()); GetOrCreateData(f->function_context_map()); GetOrCreateData(f->many_closures_cell_map()); GetOrCreateData(f->mutable_heap_number_map()); GetOrCreateData(f->name_dictionary_map()); GetOrCreateData(f->one_pointer_filler_map()); GetOrCreateData(f->sloppy_arguments_elements_map()); GetOrCreateData(f->with_context_map()); // Stuff used by TypedOptimization: // Strings produced by typeof: GetOrCreateData(f->boolean_string()); GetOrCreateData(f->number_string()); GetOrCreateData(f->string_string()); GetOrCreateData(f->bigint_string()); GetOrCreateData(f->symbol_string()); GetOrCreateData(f->undefined_string()); GetOrCreateData(f->object_string()); GetOrCreateData(f->function_string()); // Stuff used by JSTypedLowering: GetOrCreateData(f->length_string()); Builtins::Name builtins[] = { Builtins::kArgumentsAdaptorTrampoline, Builtins::kCallFunctionForwardVarargs, Builtins::kStringAdd_CheckNone_NotTenured, Builtins::kStringAdd_CheckNone_Tenured, Builtins::kStringAdd_ConvertLeft_NotTenured, Builtins::kStringAdd_ConvertRight_NotTenured, }; for (auto id : builtins) { GetOrCreateData(b->builtin_handle(id)); } for (int32_t id = 0; id < Builtins::builtin_count; ++id) { if (Builtins::KindOf(id) == Builtins::TFJ) { GetOrCreateData(b->builtin_handle(id)); } } Trace("Finished serializing standard objects.\n"); } HeapObjectType JSHeapBroker::HeapObjectTypeFromMap(Map* map) const { AllowHandleDereference allow_handle_dereference; OddballType oddball_type = OddballType::kNone; if (map->instance_type() == ODDBALL_TYPE) { ReadOnlyRoots roots(isolate_); if (map == roots.undefined_map()) { oddball_type = OddballType::kUndefined; } else if (map == roots.null_map()) { oddball_type = OddballType::kNull; } else if (map == roots.boolean_map()) { oddball_type = OddballType::kBoolean; } else if (map == roots.the_hole_map()) { oddball_type = OddballType::kHole; } else if (map == roots.uninitialized_map()) { oddball_type = OddballType::kUninitialized; } else { oddball_type = OddballType::kOther; DCHECK(map == roots.termination_exception_map() || map == roots.arguments_marker_map() || map == roots.optimized_out_map() || map == roots.stale_register_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, oddball_type); } ObjectData* JSHeapBroker::GetData(Handle<Object> object) const { auto it = refs_.find(object.address()); return it != refs_.end() ? it->second : nullptr; } ObjectData* JSHeapBroker::GetOrCreateData(Handle<Object> object) { CHECK(SerializingAllowed()); ObjectData* data = GetData(object); if (data == nullptr) { // TODO(neis): Remove these Allow* once we serialize everything upfront. AllowHandleAllocation handle_allocation; AllowHandleDereference handle_dereference; data = ObjectData::Serialize(this, object); } CHECK_NOT_NULL(data); return data; } void JSHeapBroker::AddData(Handle<Object> object, ObjectData* data) { Trace("Creating data %p for handle %" V8PRIuPTR " (", data, object.address()); if (FLAG_trace_heap_broker) { object->ShortPrint(); PrintF(")\n"); } CHECK_NOT_NULL(isolate()->handle_scope_data()->canonical_scope); CHECK(refs_.insert({object.address(), data}).second); } #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(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 object<Smi>()->value(); } HeapObjectType HeapObjectRef::type() const { if (broker()->mode() == JSHeapBroker::kDisabled) { AllowHandleDereference allow_handle_dereference; return broker()->HeapObjectTypeFromMap(object<HeapObject>()->map()); } else { return data()->AsHeapObject()->type; } } base::Optional<MapRef> HeapObjectRef::TryGetObjectCreateMap() const { AllowHandleAllocation handle_allocation; AllowHandleDereference allow_handle_dereference; Handle<Map> instance_map; if (Map::TryGetObjectCreateMap(broker()->isolate(), object<HeapObject>()) .ToHandle(&instance_map)) { return MapRef(broker(), instance_map); } else { return base::Optional<MapRef>(); } } base::Optional<MapRef> MapRef::AsElementsKind(ElementsKind kind) const { if (broker()->mode() == JSHeapBroker::kDisabled) { AllowHandleAllocation handle_allocation; AllowHeapAllocation heap_allocation; AllowHandleDereference allow_handle_dereference; return MapRef(broker(), Map::AsElementsKind(broker()->isolate(), object<Map>(), kind)); } else { if (kind == elements_kind()) return *this; const ZoneVector<MapData*>& elements_kind_generalizations = data()->AsMap()->elements_kind_generalizations(); for (auto data : elements_kind_generalizations) { MapRef map(data); if (map.elements_kind() == kind) return map; } return base::Optional<MapRef>(); } } int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const { AllowHandleDereference allow_handle_dereference; AllowHandleAllocation handle_allocation; return object<JSFunction>()->ComputeInstanceSizeWithMinSlack( broker()->isolate()); } base::Optional<ScriptContextTableRef::LookupResult> ScriptContextTableRef::lookup(const NameRef& name) const { AllowHandleAllocation handle_allocation; AllowHandleDereference handle_dereference; if (!name.IsString()) return {}; ScriptContextTable::LookupResult lookup_result; auto table = object<ScriptContextTable>(); if (!ScriptContextTable::Lookup(broker()->isolate(), table, name.object<String>(), &lookup_result)) { return {}; } Handle<Context> script_context = ScriptContextTable::GetContext( broker()->isolate(), table, lookup_result.context_index); LookupResult result{ContextRef(broker(), script_context), lookup_result.mode == VariableMode::kConst, lookup_result.slot_index}; return result; } OddballType ObjectRef::oddball_type() const { return IsSmi() ? OddballType::kNone : AsHeapObject().type().oddball_type(); } ObjectRef FeedbackVectorRef::get(FeedbackSlot slot) const { if (broker()->mode() == JSHeapBroker::kDisabled) { AllowHandleAllocation handle_allocation; AllowHandleDereference handle_dereference; Handle<Object> value(object<FeedbackVector>()->Get(slot)->ToObject(), broker()->isolate()); return ObjectRef(broker(), value); } int i = FeedbackVector::GetIndex(slot); return ObjectRef(data()->AsFeedbackVector()->feedback().at(i)); } bool JSObjectRef::IsUnboxedDoubleField(FieldIndex index) const { AllowHandleDereference handle_dereference; return object<JSObject>()->IsUnboxedDoubleField(index); } double JSObjectRef::RawFastDoublePropertyAt(FieldIndex index) const { AllowHandleDereference handle_dereference; return object<JSObject>()->RawFastDoublePropertyAt(index); } ObjectRef JSObjectRef::RawFastPropertyAt(FieldIndex index) const { AllowHandleAllocation handle_allocation; AllowHandleDereference handle_dereference; return ObjectRef(broker(), handle(object<JSObject>()->RawFastPropertyAt(index), broker()->isolate())); } bool AllocationSiteRef::IsFastLiteral() const { if (broker()->mode() == JSHeapBroker::kDisabled) { AllowHeapAllocation allow_heap_allocation; // This is needed for TryMigrateInstance. AllowHandleAllocation allow_handle_allocation; AllowHandleDereference allow_handle_dereference; return IsInlinableFastLiteral( handle(object<AllocationSite>()->boilerplate(), broker()->isolate())); } else { return data()->AsAllocationSite()->boilerplate != nullptr; } } void JSObjectRef::EnsureElementsTenured() { // TODO(jarin) Eventually, we will pretenure the boilerplates before // the compilation job starts. AllowHandleAllocation allow_handle_allocation; AllowHandleDereference allow_handle_dereference; AllowHeapAllocation allow_heap_allocation; Handle<FixedArrayBase> object_elements = elements().object<FixedArrayBase>(); if (Heap::InNewSpace(*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<JSObject>()->set_elements(*object_elements); } } FieldIndex MapRef::GetFieldIndexFor(int i) const { AllowHandleDereference allow_handle_dereference; return FieldIndex::ForDescriptor(*object<Map>(), i); } int MapRef::GetInObjectPropertyOffset(int i) const { AllowHandleDereference allow_handle_dereference; return object<Map>()->GetInObjectPropertyOffset(i); } PropertyDetails MapRef::GetPropertyDetails(int i) const { AllowHandleDereference allow_handle_dereference; return object<Map>()->instance_descriptors()->GetDetails(i); } NameRef MapRef::GetPropertyKey(int i) const { AllowHandleAllocation handle_allocation; AllowHandleDereference allow_handle_dereference; return NameRef(broker(), handle(object<Map>()->instance_descriptors()->GetKey(i), broker()->isolate())); } bool MapRef::IsFixedCowArrayMap() const { AllowHandleDereference allow_handle_dereference; return *object<Map>() == ReadOnlyRoots(broker()->isolate()).fixed_cow_array_map(); } MapRef MapRef::FindFieldOwner(int descriptor) const { AllowHandleAllocation handle_allocation; AllowHandleDereference allow_handle_dereference; Handle<Map> owner( object<Map>()->FindFieldOwner(broker()->isolate(), descriptor), broker()->isolate()); return MapRef(broker(), owner); } ObjectRef MapRef::GetFieldType(int descriptor) const { AllowHandleAllocation handle_allocation; AllowHandleDereference allow_handle_dereference; Handle<FieldType> field_type( object<Map>()->instance_descriptors()->GetFieldType(descriptor), broker()->isolate()); return ObjectRef(broker(), field_type); } uint16_t StringRef::GetFirstChar() { if (broker()->mode() == JSHeapBroker::kDisabled) { AllowHandleDereference allow_handle_dereference; return object<String>()->Get(0); } else { return data()->AsString()->first_char; } } base::Optional<double> StringRef::ToNumber() { if (broker()->mode() == JSHeapBroker::kDisabled) { AllowHandleDereference allow_handle_dereference; AllowHandleAllocation allow_handle_allocation; AllowHeapAllocation allow_heap_allocation; int flags = ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY; return StringToDouble(broker()->isolate(), broker()->isolate()->unicode_cache(), object<String>(), flags); } else { return data()->AsString()->to_number; } } bool FixedArrayRef::is_the_hole(int i) const { AllowHandleDereference allow_handle_dereference; return object<FixedArray>()->is_the_hole(broker()->isolate(), i); } ObjectRef FixedArrayRef::get(int i) const { AllowHandleAllocation handle_allocation; AllowHandleDereference allow_handle_dereference; return ObjectRef(broker(), handle(object<FixedArray>()->get(i), broker()->isolate())); } bool FixedDoubleArrayRef::is_the_hole(int i) const { AllowHandleDereference allow_handle_dereference; return object<FixedDoubleArray>()->is_the_hole(i); } double FixedDoubleArrayRef::get_scalar(int i) const { AllowHandleDereference allow_handle_dereference; return object<FixedDoubleArray>()->get_scalar(i); } #define IF_BROKER_DISABLED_ACCESS_HANDLE_C(holder, name) \ if (broker()->mode() == JSHeapBroker::kDisabled) { \ AllowHandleAllocation handle_allocation; \ AllowHandleDereference allow_handle_dereference; \ return object<holder>()->name(); \ } // Macros for definining a const getter that, depending on the broker mode, // either looks into the handle or into the serialized data. The first one is // used for the rare case of a XYZRef class that does not have a corresponding // XYZ class in objects.h. The second one is used otherwise. #define BIMODAL_ACCESSOR(holder, result, name) \ result##Ref holder##Ref::name() const { \ if (broker()->mode() == JSHeapBroker::kDisabled) { \ AllowHandleAllocation handle_allocation; \ AllowHandleDereference allow_handle_dereference; \ return result##Ref( \ broker(), handle(object<holder>()->name(), broker()->isolate())); \ } else { \ return result##Ref(data()->As##holder()->name); \ } \ } // Like HANDLE_ACCESSOR except that the result type is not an XYZRef. #define BIMODAL_ACCESSOR_C(holder, result, name) \ result holder##Ref::name() const { \ IF_BROKER_DISABLED_ACCESS_HANDLE_C(holder, name); \ return data()->As##holder()->name; \ } // Like HANDLE_ACCESSOR_C but for BitFields. #define BIMODAL_ACCESSOR_B(holder, field, name, BitField) \ typename BitField::FieldType holder##Ref::name() const { \ IF_BROKER_DISABLED_ACCESS_HANDLE_C(holder, name); \ return BitField::decode(data()->As##holder()->field); \ } // Macros for definining a const getter that always looks into the handle. // (These will go away once we serialize everything.) The first one is used for // the rare case of a XYZRef class that does not have a corresponding XYZ class // in objects.h. The second one is used otherwise. #define HANDLE_ACCESSOR(holder, result, name) \ result##Ref holder##Ref::name() const { \ AllowHandleAllocation handle_allocation; \ AllowHandleDereference allow_handle_dereference; \ return result##Ref(broker(), \ handle(object<holder>()->name(), broker()->isolate())); \ } // Like HANDLE_ACCESSOR except that the result type is not an XYZRef. #define HANDLE_ACCESSOR_C(holder, result, name) \ result holder##Ref::name() const { \ AllowHandleAllocation handle_allocation; \ AllowHandleDereference allow_handle_dereference; \ return object<holder>()->name(); \ } BIMODAL_ACCESSOR(AllocationSite, Object, nested_site) BIMODAL_ACCESSOR_C(AllocationSite, bool, CanInlineCall) BIMODAL_ACCESSOR_C(AllocationSite, bool, PointsToLiteral) BIMODAL_ACCESSOR_C(AllocationSite, ElementsKind, GetElementsKind) BIMODAL_ACCESSOR_C(AllocationSite, PretenureFlag, GetPretenureMode) BIMODAL_ACCESSOR_C(BytecodeArray, int, register_count) BIMODAL_ACCESSOR_C(FixedArrayBase, int, length) BIMODAL_ACCESSOR(HeapObject, Map, map) HANDLE_ACCESSOR_C(HeapObject, bool, IsExternalString) HANDLE_ACCESSOR_C(HeapObject, bool, IsSeqString) HANDLE_ACCESSOR_C(HeapNumber, double, value) HANDLE_ACCESSOR(JSArray, Object, length) BIMODAL_ACCESSOR_C(JSFunction, bool, has_prototype) BIMODAL_ACCESSOR_C(JSFunction, bool, PrototypeRequiresRuntimeLookup) BIMODAL_ACCESSOR(JSFunction, Map, initial_map) BIMODAL_ACCESSOR(JSFunction, Object, prototype) HANDLE_ACCESSOR_C(JSFunction, bool, IsConstructor) HANDLE_ACCESSOR(JSFunction, JSGlobalProxy, global_proxy) HANDLE_ACCESSOR(JSFunction, SharedFunctionInfo, shared) HANDLE_ACCESSOR(JSObject, FixedArrayBase, elements) HANDLE_ACCESSOR(JSRegExp, Object, data) HANDLE_ACCESSOR(JSRegExp, Object, flags) HANDLE_ACCESSOR(JSRegExp, Object, last_index) HANDLE_ACCESSOR(JSRegExp, Object, raw_properties_or_hash) HANDLE_ACCESSOR(JSRegExp, Object, source) BIMODAL_ACCESSOR_B(Map, bit_field2, elements_kind, Map::ElementsKindBits) BIMODAL_ACCESSOR_B(Map, bit_field3, is_deprecated, Map::IsDeprecatedBit) BIMODAL_ACCESSOR_B(Map, bit_field3, is_dictionary_map, Map::IsDictionaryMapBit) BIMODAL_ACCESSOR_B(Map, bit_field, has_prototype_slot, Map::HasPrototypeSlotBit) BIMODAL_ACCESSOR_C(Map, int, instance_size) HANDLE_ACCESSOR_C(Map, bool, CanBeDeprecated) HANDLE_ACCESSOR_C(Map, bool, CanTransition) HANDLE_ACCESSOR_C(Map, bool, IsInobjectSlackTrackingInProgress) HANDLE_ACCESSOR_C(Map, bool, IsJSArrayMap) HANDLE_ACCESSOR_C(Map, bool, is_stable) HANDLE_ACCESSOR_C(Map, InstanceType, instance_type) HANDLE_ACCESSOR_C(Map, int, GetInObjectProperties) HANDLE_ACCESSOR_C(Map, int, GetInObjectPropertiesStartInWords) HANDLE_ACCESSOR_C(Map, int, NumberOfOwnDescriptors) HANDLE_ACCESSOR(Map, Object, constructor_or_backpointer) HANDLE_ACCESSOR_C(MutableHeapNumber, double, value) #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 HANDLE_ACCESSOR(PropertyCell, Object, value) HANDLE_ACCESSOR_C(PropertyCell, PropertyDetails, property_details) HANDLE_ACCESSOR_C(ScopeInfo, int, ContextLength) BIMODAL_ACCESSOR_C(SharedFunctionInfo, int, builtin_id) BIMODAL_ACCESSOR(SharedFunctionInfo, BytecodeArray, GetBytecodeArray) #define DEF_SFI_ACCESSOR(type, name) \ BIMODAL_ACCESSOR_C(SharedFunctionInfo, type, name) BROKER_SFI_FIELDS(DEF_SFI_ACCESSOR) #undef DEF_SFI_ACCESSOR BIMODAL_ACCESSOR_C(String, int, length) // TODO(neis): Provide StringShape() on StringRef. bool JSFunctionRef::has_initial_map() const { IF_BROKER_DISABLED_ACCESS_HANDLE_C(JSFunction, has_initial_map); return data()->AsJSFunction()->initial_map != nullptr; } MapRef NativeContextRef::GetFunctionMapFromIndex(int index) const { DCHECK_LE(index, Context::LAST_FUNCTION_MAP_INDEX); DCHECK_GE(index, Context::FIRST_FUNCTION_MAP_INDEX); return get(index).AsMap(); } 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(); } } bool ObjectRef::BooleanValue() { AllowHandleDereference allow_handle_dereference; return object<Object>()->BooleanValue(broker()->isolate()); } double ObjectRef::OddballToNumber() const { OddballType type = oddball_type(); switch (type) { case OddballType::kBoolean: { ObjectRef true_ref(broker(), broker()->isolate()->factory()->true_value()); return this->equals(true_ref) ? 1 : 0; break; } case OddballType::kUndefined: { return std::numeric_limits<double>::quiet_NaN(); break; } case OddballType::kNull: { return 0; break; } default: { UNREACHABLE(); break; } } } CellRef ModuleRef::GetCell(int cell_index) { AllowHandleAllocation handle_allocation; AllowHandleDereference allow_handle_dereference; return CellRef(broker(), handle(object<Module>()->GetCell(cell_index), broker()->isolate())); } ObjectRef::ObjectRef(JSHeapBroker* broker, Handle<Object> object) { switch (broker->mode()) { case JSHeapBroker::kSerialized: data_ = FLAG_strict_heap_broker ? broker->GetData(object) : broker->GetOrCreateData(object); break; case JSHeapBroker::kSerializing: data_ = broker->GetOrCreateData(object); break; case JSHeapBroker::kDisabled: data_ = broker->GetData(object); if (data_ == nullptr) { AllowHandleDereference handle_dereference; data_ = new (broker->zone()) ObjectData(broker, object, object->IsSmi()); } break; } CHECK_NOT_NULL(data_); } base::Optional<JSObjectRef> AllocationSiteRef::boilerplate() const { if (broker()->mode() == JSHeapBroker::kDisabled) { AllowHandleAllocation handle_allocation; AllowHandleDereference allow_handle_dereference; return JSObjectRef(broker(), handle(object<AllocationSite>()->boilerplate(), broker()->isolate())); } else { JSObjectData* boilerplate = data()->AsAllocationSite()->boilerplate; if (boilerplate) { return JSObjectRef(boilerplate); } else { return base::nullopt; } } } ElementsKind JSObjectRef::GetElementsKind() const { return map().elements_kind(); } Handle<Object> ObjectRef::object() const { return data_->object; } JSHeapBroker* ObjectRef::broker() const { return data_->broker; } ObjectData* ObjectRef::data() const { return data_; } Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker, const char* function, int line) { if (FLAG_trace_heap_broker) { PrintF("[%p] Skipping optimization in %s at line %d due to missing data\n", broker, function, line); } return AdvancedReducer::NoChange(); } #undef BIMODAL_ACCESSOR #undef BIMODAL_ACCESSOR_B #undef BIMODAL_ACCESSOR_C #undef GET_OR_CREATE #undef HANDLE_ACCESSOR #undef HANDLE_ACCESSOR_C #undef IF_BROKER_DISABLED_ACCESS_HANDLE_C } // namespace compiler } // namespace internal } // namespace v8