Commit 364cec25 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[compiler] Make Map a background-serialized-object

.. by locking the MapUpdater lock during MapData construction.

Note this only applies to basic MapRef/MapData construction. Some
methods, in particular MapRef::SerializeFoo methods, are not yet
background-serializable in general and require more work.

Bug: v8:7790
Change-Id: I473e78c82012ab6abc5a0633a4d34c4a40a3fb77
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2839553
Auto-Submit: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarSantiago Aboy Solanes <solanes@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74164}
parent eb57c722
...@@ -722,9 +722,9 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -722,9 +722,9 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
Handle<Map> map, Handle<Name> name, AccessMode access_mode) const { Handle<Map> map, Handle<Name> name, AccessMode access_mode) const {
CHECK(name->IsUniqueName()); CHECK(name->IsUniqueName());
JSHeapBroker::MapUpdaterMutexDepthScope mumd_scope(broker());
base::SharedMutexGuardIf<base::kShared> mutex_guard( base::SharedMutexGuardIf<base::kShared> mutex_guard(
isolate()->map_updater_access(), should_lock_mutex()); isolate()->map_updater_access(), mumd_scope.should_lock());
MapUpdaterMutexDepthScope mumd_scope(this);
if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) { if (access_mode == AccessMode::kHas && !map->IsJSReceiverMap()) {
return Invalid(); return Invalid();
...@@ -864,8 +864,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo( ...@@ -864,8 +864,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
} }
// Walk up the prototype chain. // Walk up the prototype chain.
ObjectData* data = broker()->TryGetOrCreateData( ObjectData* data = broker()->TryGetOrCreateData(map, false);
map, false, ObjectRef::BackgroundSerialization::kAllowed);
if (data == nullptr) return Invalid(); if (data == nullptr) return Invalid();
if (!MapRef(broker(), data).TrySerializePrototype()) { if (!MapRef(broker(), data).TrySerializePrototype()) {
......
...@@ -317,28 +317,6 @@ class AccessInfoFactory final { ...@@ -317,28 +317,6 @@ class AccessInfoFactory final {
Handle<Name> name, InternalIndex* index_out, Handle<Name> name, InternalIndex* index_out,
PropertyDetails* details_out) const; PropertyDetails* details_out) const;
bool should_lock_mutex() const { return map_updater_mutex_depth_ == 0; }
class MapUpdaterMutexDepthScope final {
public:
explicit MapUpdaterMutexDepthScope(const AccessInfoFactory* ptr)
: ptr_(ptr),
initial_map_updater_mutex_depth_(ptr->map_updater_mutex_depth_) {
ptr_->map_updater_mutex_depth_++;
}
~MapUpdaterMutexDepthScope() {
ptr_->map_updater_mutex_depth_--;
DCHECK_EQ(initial_map_updater_mutex_depth_,
ptr_->map_updater_mutex_depth_);
USE(initial_map_updater_mutex_depth_);
}
private:
const AccessInfoFactory* const ptr_;
const int initial_map_updater_mutex_depth_;
};
CompilationDependencies* dependencies() const { return dependencies_; } CompilationDependencies* dependencies() const { return dependencies_; }
JSHeapBroker* broker() const { return broker_; } JSHeapBroker* broker() const { return broker_; }
Isolate* isolate() const; Isolate* isolate() const;
...@@ -349,12 +327,6 @@ class AccessInfoFactory final { ...@@ -349,12 +327,6 @@ class AccessInfoFactory final {
TypeCache const* const type_cache_; TypeCache const* const type_cache_;
Zone* const zone_; Zone* const zone_;
// ComputePropertyAccessInfo can be called recursively, thus we need to
// emulate a recursive mutex. This field holds the locking depth, i.e. how
// many times the mutex has been recursively locked. Only the outermost
// locker actually locks underneath.
mutable int map_updater_mutex_depth_ = 0;
// TODO(nicohartmann@): Move to public // TODO(nicohartmann@): Move to public
AccessInfoFactory(const AccessInfoFactory&) = delete; AccessInfoFactory(const AccessInfoFactory&) = delete;
AccessInfoFactory& operator=(const AccessInfoFactory&) = delete; AccessInfoFactory& operator=(const AccessInfoFactory&) = delete;
......
...@@ -1184,21 +1184,24 @@ class MapData : public HeapObjectData { ...@@ -1184,21 +1184,24 @@ class MapData : public HeapObjectData {
void SerializeForElementStore(JSHeapBroker* broker); void SerializeForElementStore(JSHeapBroker* broker);
private: private:
InstanceType const instance_type_; // The following fields should be const in principle, but construction
int const instance_size_; // requires locking the MapUpdater lock. For this reason, it's easier to
byte const bit_field_; // initialize these inside the constructor body, not in the initializer list.
byte const bit_field2_; InstanceType instance_type_;
uint32_t const bit_field3_; int instance_size_;
bool const can_be_deprecated_; byte bit_field_;
bool const can_transition_; byte bit_field2_;
int const in_object_properties_start_in_words_; uint32_t bit_field3_;
int const in_object_properties_; bool can_be_deprecated_;
int const constructor_function_index_; bool can_transition_;
int const next_free_property_index_; int in_object_properties_start_in_words_;
int const unused_property_fields_; int in_object_properties_;
bool const supports_fast_array_iteration_; int constructor_function_index_;
bool const supports_fast_array_resize_; int next_free_property_index_;
bool const is_abandoned_prototype_map_; int unused_property_fields_;
bool supports_fast_array_iteration_;
bool supports_fast_array_resize_;
bool is_abandoned_prototype_map_;
bool serialized_elements_kind_generalizations_ = false; bool serialized_elements_kind_generalizations_ = false;
ZoneVector<ObjectData*> elements_kind_generalizations_; ZoneVector<ObjectData*> elements_kind_generalizations_;
...@@ -1274,7 +1277,7 @@ HeapObjectData::HeapObjectData(JSHeapBroker* broker, ObjectData** storage, ...@@ -1274,7 +1277,7 @@ HeapObjectData::HeapObjectData(JSHeapBroker* broker, ObjectData** storage,
// instance_type_ member. In the case of constructing the MapData for 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 // meta map (whose map is itself), this member has not yet been
// initialized. // initialized.
map_(broker->GetOrCreateData(object->map())) { map_(broker->GetOrCreateData(object->synchronized_map())) {
CHECK_IMPLIES(kind == kSerializedHeapObject, CHECK_IMPLIES(kind == kSerializedHeapObject,
broker->mode() == JSHeapBroker::kSerializing); broker->mode() == JSHeapBroker::kSerializing);
CHECK_IMPLIES(broker->mode() == JSHeapBroker::kSerialized, CHECK_IMPLIES(broker->mode() == JSHeapBroker::kSerialized,
...@@ -1302,17 +1305,22 @@ bool IsReadOnlyLengthDescriptor(Isolate* isolate, Handle<Map> jsarray_map) { ...@@ -1302,17 +1305,22 @@ bool IsReadOnlyLengthDescriptor(Isolate* isolate, Handle<Map> jsarray_map) {
return descriptors.GetDetails(offset).IsReadOnly(); return descriptors.GetDetails(offset).IsReadOnly();
} }
bool SupportsFastArrayIteration(Isolate* isolate, Handle<Map> map) { // 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 && return map->instance_type() == JS_ARRAY_TYPE &&
IsFastElementsKind(map->elements_kind()) && IsFastElementsKind(map->elements_kind()) &&
map->prototype().IsJSArray() && map->prototype().IsJSArray() &&
isolate->IsAnyInitialArrayPrototype(JSArray::cast(map->prototype())) && broker->IsArrayOrObjectPrototype(broker->CanonicalPersistentHandle(
Protectors::IsNoElementsIntact(isolate); JSArray::cast(map->prototype())));
} }
bool SupportsFastArrayResize(Isolate* isolate, Handle<Map> map) { bool SupportsFastArrayResize(JSHeapBroker* broker, Handle<Map> map) {
return SupportsFastArrayIteration(isolate, map) && map->is_extensible() && return SupportsFastArrayIteration(broker, map) && map->is_extensible() &&
!map->is_dictionary_map() && !IsReadOnlyLengthDescriptor(isolate, map); !map->is_dictionary_map() &&
!IsReadOnlyLengthDescriptor(broker->isolate(), map);
} }
} // namespace } // namespace
...@@ -1320,37 +1328,42 @@ bool SupportsFastArrayResize(Isolate* isolate, Handle<Map> map) { ...@@ -1320,37 +1328,42 @@ bool SupportsFastArrayResize(Isolate* isolate, Handle<Map> map) {
MapData::MapData(JSHeapBroker* broker, ObjectData** storage, Handle<Map> object, MapData::MapData(JSHeapBroker* broker, ObjectData** storage, Handle<Map> object,
ObjectDataKind kind) ObjectDataKind kind)
: HeapObjectData(broker, storage, object, kind), : HeapObjectData(broker, storage, object, kind),
instance_type_(object->instance_type()), elements_kind_generalizations_(broker->zone()) {
instance_size_(object->instance_size()), // 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::MapUpdaterMutexDepthScope mumd_scope(broker);
base::SharedMutexGuardIf<base::kShared> mutex_guard(
broker->isolate()->map_updater_access(), mumd_scope.should_lock());
instance_type_ = object->instance_type();
instance_size_ = object->instance_size();
// We read the bit_field as relaxed since `has_non_instance_prototype` can // We read the bit_field as relaxed since `has_non_instance_prototype` can
// be modified in live objects, and because we serialize some maps on the // be modified in live objects, and because we serialize some maps on the
// background. Those background-serialized maps are the native context's // background. Those background-serialized maps are the native context's
// maps for which this bit is "set" but it doesn't change value (i.e. it // maps for which this bit is "set" but it doesn't change value (i.e. it
// is set to false when it was already false). // is set to false when it was already false).
bit_field_(object->relaxed_bit_field()), bit_field_ = object->relaxed_bit_field();
bit_field2_(object->bit_field2()), bit_field2_ = object->bit_field2();
// Similar to the bit_field comment above. // Similar to the bit_field comment above.
bit_field3_(object->relaxed_bit_field3()), bit_field3_ = object->relaxed_bit_field3();
can_be_deprecated_(object->NumberOfOwnDescriptors() > 0 can_be_deprecated_ =
? object->CanBeDeprecated() object->NumberOfOwnDescriptors() > 0 ? object->CanBeDeprecated() : false;
: false), can_transition_ = object->CanTransition();
can_transition_(object->CanTransition()), in_object_properties_start_in_words_ =
in_object_properties_start_in_words_( object->IsJSObjectMap() ? object->GetInObjectPropertiesStartInWords() : 0;
object->IsJSObjectMap() ? object->GetInObjectPropertiesStartInWords() in_object_properties_ =
: 0), object->IsJSObjectMap() ? object->GetInObjectProperties() : 0;
in_object_properties_( constructor_function_index_ = object->IsPrimitiveMap()
object->IsJSObjectMap() ? object->GetInObjectProperties() : 0),
constructor_function_index_(object->IsPrimitiveMap()
? object->GetConstructorFunctionIndex() ? object->GetConstructorFunctionIndex()
: Map::kNoConstructorFunctionIndex), : Map::kNoConstructorFunctionIndex;
next_free_property_index_(object->NextFreePropertyIndex()), next_free_property_index_ = object->NextFreePropertyIndex();
unused_property_fields_(object->UnusedPropertyFields()), unused_property_fields_ = object->UnusedPropertyFields();
supports_fast_array_iteration_( supports_fast_array_iteration_ = SupportsFastArrayIteration(broker, object);
SupportsFastArrayIteration(broker->isolate(), object)), supports_fast_array_resize_ = SupportsFastArrayResize(broker, object);
supports_fast_array_resize_( is_abandoned_prototype_map_ = object->is_abandoned_prototype_map();
SupportsFastArrayResize(broker->isolate(), object)), }
is_abandoned_prototype_map_(object->is_abandoned_prototype_map()),
elements_kind_generalizations_(broker->zone()) {}
JSFunctionData::JSFunctionData(JSHeapBroker* broker, ObjectData** storage, JSFunctionData::JSFunctionData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSFunction> object) Handle<JSFunction> object)
...@@ -2293,7 +2306,7 @@ bool MapData::TrySerializeOwnDescriptor(JSHeapBroker* broker, ...@@ -2293,7 +2306,7 @@ bool MapData::TrySerializeOwnDescriptor(JSHeapBroker* broker,
if (instance_descriptors_ == nullptr) { if (instance_descriptors_ == nullptr) {
instance_descriptors_ = instance_descriptors_ =
broker->TryGetOrCreateData(map->instance_descriptors(isolate)); broker->TryGetOrCreateData(map->instance_descriptors(kAcquireLoad));
if (instance_descriptors_ == nullptr) return false; if (instance_descriptors_ == nullptr) return false;
} }
...@@ -2628,6 +2641,8 @@ void JSHeapBroker::InitializeAndStartSerializing( ...@@ -2628,6 +2641,8 @@ void JSHeapBroker::InitializeAndStartSerializing(
refs_ = refs_ =
zone()->New<RefsMap>(kInitialRefsBucketCount, AddressMatcher(), zone()); zone()->New<RefsMap>(kInitialRefsBucketCount, AddressMatcher(), zone());
CollectArrayAndObjectPrototypes();
SetTargetNativeContextRef(native_context); SetTargetNativeContextRef(native_context);
target_native_context().Serialize(); target_native_context().Serialize();
if (!FLAG_turbo_direct_heap_access) { if (!FLAG_turbo_direct_heap_access) {
...@@ -2636,8 +2651,6 @@ void JSHeapBroker::InitializeAndStartSerializing( ...@@ -2636,8 +2651,6 @@ void JSHeapBroker::InitializeAndStartSerializing(
target_native_context().SerializeOnBackground(); target_native_context().SerializeOnBackground();
} }
CollectArrayAndObjectPrototypes();
Factory* const f = isolate()->factory(); Factory* const f = isolate()->factory();
if (!FLAG_turbo_direct_heap_access) { if (!FLAG_turbo_direct_heap_access) {
ObjectData* data; ObjectData* data;
...@@ -2895,14 +2908,14 @@ bool MapRef::HasOnlyStablePrototypesWithFastElements( ...@@ -2895,14 +2908,14 @@ bool MapRef::HasOnlyStablePrototypesWithFastElements(
bool MapRef::supports_fast_array_iteration() const { bool MapRef::supports_fast_array_iteration() const {
if (data_->should_access_heap()) { if (data_->should_access_heap()) {
return SupportsFastArrayIteration(broker()->isolate(), object()); return SupportsFastArrayIteration(broker(), object());
} }
return data()->AsMap()->supports_fast_array_iteration(); return data()->AsMap()->supports_fast_array_iteration();
} }
bool MapRef::supports_fast_array_resize() const { bool MapRef::supports_fast_array_resize() const {
if (data_->should_access_heap()) { if (data_->should_access_heap()) {
return SupportsFastArrayResize(broker()->isolate(), object()); return SupportsFastArrayResize(broker(), object());
} }
return data()->AsMap()->supports_fast_array_resize(); return data()->AsMap()->supports_fast_array_resize();
} }
......
...@@ -105,14 +105,14 @@ enum class OddballType : uint8_t { ...@@ -105,14 +105,14 @@ enum class OddballType : uint8_t {
#define HEAP_BROKER_POSSIBLY_BACKGROUND_SERIALIZED_OBJECT_LIST(V) \ #define HEAP_BROKER_POSSIBLY_BACKGROUND_SERIALIZED_OBJECT_LIST(V) \
/* Subtypes of HeapObject */ \ /* Subtypes of HeapObject */ \
V(BigInt) \ V(BigInt) \
V(HeapNumber) \ V(HeapNumber)
V(Map)
// This list is sorted such that subtypes appear before their supertypes. // This list is sorted such that subtypes appear before their supertypes.
// DO NOT VIOLATE THIS PROPERTY! // DO NOT VIOLATE THIS PROPERTY!
// Types in this list can be serialized on demand from the background thread. // Types in this list can be serialized on demand from the background thread.
#define HEAP_BROKER_BACKGROUND_SERIALIZED_OBJECT_LIST(V) \ #define HEAP_BROKER_BACKGROUND_SERIALIZED_OBJECT_LIST(V) \
/* Subtypes of HeapObject */ \ /* Subtypes of HeapObject */ \
V(Map) \
V(PropertyCell) V(PropertyCell)
// This list is sorted such that subtypes appear before their supertypes. // This list is sorted such that subtypes appear before their supertypes.
......
...@@ -5717,15 +5717,17 @@ Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) { ...@@ -5717,15 +5717,17 @@ Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) {
bool can_be_holey = false; bool can_be_holey = false;
for (Handle<Map> map : receiver_maps) { for (Handle<Map> map : receiver_maps) {
MapRef receiver_map(broker(), map); MapRef receiver_map(broker(), map);
if (!receiver_map.supports_fast_array_iteration()) if (!receiver_map.supports_fast_array_iteration()) {
return inference.NoChange(); return inference.NoChange();
}
if (IsHoleyElementsKind(receiver_map.elements_kind())) { if (IsHoleyElementsKind(receiver_map.elements_kind())) {
can_be_holey = true; can_be_holey = true;
} }
} }
if (!dependencies()->DependOnArraySpeciesProtector()) if (!dependencies()->DependOnArraySpeciesProtector()) {
return inference.NoChange(); return inference.NoChange();
}
if (can_be_holey && !dependencies()->DependOnNoElementsProtector()) { if (can_be_holey && !dependencies()->DependOnNoElementsProtector()) {
return inference.NoChange(); return inference.NoChange();
} }
......
...@@ -213,14 +213,18 @@ bool JSHeapBroker::IsSerializedForCompilation( ...@@ -213,14 +213,18 @@ bool JSHeapBroker::IsSerializedForCompilation(
} }
bool JSHeapBroker::IsArrayOrObjectPrototype(const JSObjectRef& object) const { bool JSHeapBroker::IsArrayOrObjectPrototype(const JSObjectRef& object) const {
return IsArrayOrObjectPrototype(object.object());
}
bool JSHeapBroker::IsArrayOrObjectPrototype(Handle<JSObject> object) const {
if (mode() == kDisabled) { if (mode() == kDisabled) {
return isolate()->IsInAnyContext(*object.object(), return isolate()->IsInAnyContext(*object,
Context::INITIAL_ARRAY_PROTOTYPE_INDEX) || Context::INITIAL_ARRAY_PROTOTYPE_INDEX) ||
isolate()->IsInAnyContext(*object.object(), isolate()->IsInAnyContext(*object,
Context::INITIAL_OBJECT_PROTOTYPE_INDEX); Context::INITIAL_OBJECT_PROTOTYPE_INDEX);
} }
CHECK(!array_and_object_prototypes_.empty()); CHECK(!array_and_object_prototypes_.empty());
return array_and_object_prototypes_.find(object.object()) != return array_and_object_prototypes_.find(object) !=
array_and_object_prototypes_.end(); array_and_object_prototypes_.end();
} }
......
...@@ -173,6 +173,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -173,6 +173,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
// Check if {object} is any native context's %ArrayPrototype% or // Check if {object} is any native context's %ArrayPrototype% or
// %ObjectPrototype%. // %ObjectPrototype%.
bool IsArrayOrObjectPrototype(const JSObjectRef& object) const; bool IsArrayOrObjectPrototype(const JSObjectRef& object) const;
bool IsArrayOrObjectPrototype(Handle<JSObject> object) const;
bool HasFeedback(FeedbackSource const& source) const; bool HasFeedback(FeedbackSource const& source) const;
void SetFeedback(FeedbackSource const& source, void SetFeedback(FeedbackSource const& source,
...@@ -342,6 +343,29 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -342,6 +343,29 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
RootIndexMap const& root_index_map() { return root_index_map_; } RootIndexMap const& root_index_map() { return root_index_map_; }
class MapUpdaterMutexDepthScope final {
public:
explicit MapUpdaterMutexDepthScope(JSHeapBroker* ptr)
: ptr_(ptr),
initial_map_updater_mutex_depth_(ptr->map_updater_mutex_depth_) {
ptr_->map_updater_mutex_depth_++;
}
~MapUpdaterMutexDepthScope() {
ptr_->map_updater_mutex_depth_--;
DCHECK_EQ(initial_map_updater_mutex_depth_,
ptr_->map_updater_mutex_depth_);
}
// Whether the MapUpdater mutex should be physically locked (if not, we
// already hold the lock).
bool should_lock() const { return initial_map_updater_mutex_depth_ == 0; }
private:
JSHeapBroker* const ptr_;
const int initial_map_updater_mutex_depth_;
};
private: private:
friend class HeapObjectRef; friend class HeapObjectRef;
friend class ObjectRef; friend class ObjectRef;
...@@ -456,9 +480,19 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -456,9 +480,19 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
}; };
ZoneMultimap<SerializedFunction, HintsVector> serialized_functions_; ZoneMultimap<SerializedFunction, HintsVector> serialized_functions_;
static const size_t kMaxSerializedFunctionsCacheSize = 200; // The MapUpdater mutex is used in recursive patterns; for example,
static const uint32_t kMinimalRefsBucketCount = 8; // must be power of 2 // ComputePropertyAccessInfo may call itself recursively. Thus we need to
static const uint32_t kInitialRefsBucketCount = 1024; // must be power of 2 // emulate a recursive mutex, which we do by checking if this heap broker
// instance already holds the mutex when a lock is requested. This field
// holds the locking depth, i.e. how many times the mutex has been
// recursively locked. Only the outermost locker actually locks underneath.
int map_updater_mutex_depth_ = 0;
static constexpr size_t kMaxSerializedFunctionsCacheSize = 200;
static constexpr uint32_t kMinimalRefsBucketCount = 8;
STATIC_ASSERT(base::bits::IsPowerOfTwo(kMinimalRefsBucketCount));
static constexpr uint32_t kInitialRefsBucketCount = 1024;
STATIC_ASSERT(base::bits::IsPowerOfTwo(kInitialRefsBucketCount));
}; };
class V8_NODISCARD TraceScope { class V8_NODISCARD TraceScope {
......
...@@ -1141,7 +1141,7 @@ Handle<Map> Map::AsElementsKind(Isolate* isolate, Handle<Map> map, ...@@ -1141,7 +1141,7 @@ Handle<Map> Map::AsElementsKind(Isolate* isolate, Handle<Map> map,
int Map::NumberOfEnumerableProperties() const { int Map::NumberOfEnumerableProperties() const {
int result = 0; int result = 0;
DescriptorArray descs = instance_descriptors(kRelaxedLoad); DescriptorArray descs = instance_descriptors(kAcquireLoad);
for (InternalIndex i : IterateOwnDescriptors()) { for (InternalIndex i : IterateOwnDescriptors()) {
if ((descs.GetDetails(i).attributes() & ONLY_ENUMERABLE) == 0 && if ((descs.GetDetails(i).attributes() & ONLY_ENUMERABLE) == 0 &&
!descs.GetKey(i).FilterKey(ENUMERABLE_STRINGS)) { !descs.GetKey(i).FilterKey(ENUMERABLE_STRINGS)) {
...@@ -1153,7 +1153,7 @@ int Map::NumberOfEnumerableProperties() const { ...@@ -1153,7 +1153,7 @@ int Map::NumberOfEnumerableProperties() const {
int Map::NextFreePropertyIndex() const { int Map::NextFreePropertyIndex() const {
int number_of_own_descriptors = NumberOfOwnDescriptors(); int number_of_own_descriptors = NumberOfOwnDescriptors();
DescriptorArray descs = instance_descriptors(); DescriptorArray descs = instance_descriptors(kAcquireLoad);
// Search properties backwards to find the last field. // Search properties backwards to find the last field.
for (int i = number_of_own_descriptors - 1; i >= 0; --i) { for (int i = number_of_own_descriptors - 1; i >= 0; --i) {
PropertyDetails details = descs.GetDetails(InternalIndex(i)); PropertyDetails details = descs.GetDetails(InternalIndex(i));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment