Index: src/objects.cc |
diff --git a/src/objects.cc b/src/objects.cc |
index ac4982d4fe4088e7fcd90c286b9f9c4c4689d405..e8c38385ea29e2330345bf8cabc115f6518432f0 100644 |
--- a/src/objects.cc |
+++ b/src/objects.cc |
@@ -6102,51 +6102,6 @@ void JSObject::DefineElementAccessor(Handle<JSObject> object, |
} |
-Handle<AccessorPair> JSObject::CreateAccessorPairFor(Handle<JSObject> object, |
- Handle<Name> name) { |
- Isolate* isolate = object->GetIsolate(); |
- LookupResult result(isolate); |
- object->LookupOwnRealNamedProperty(name, &result); |
- if (result.IsPropertyCallbacks()) { |
- // Note that the result can actually have IsDontDelete() == true when we |
- // e.g. have to fall back to the slow case while adding a setter after |
- // successfully reusing a map transition for a getter. Nevertheless, this is |
- // OK, because the assertion only holds for the whole addition of both |
- // accessors, not for the addition of each part. See first comment in |
- // DefinePropertyAccessor below. |
- Object* obj = result.GetCallbackObject(); |
- if (obj->IsAccessorPair()) { |
- return AccessorPair::Copy(handle(AccessorPair::cast(obj), isolate)); |
- } |
- } |
- return isolate->factory()->NewAccessorPair(); |
-} |
- |
- |
-void JSObject::DefinePropertyAccessor(Handle<JSObject> object, |
- Handle<Name> name, |
- Handle<Object> getter, |
- Handle<Object> setter, |
- PropertyAttributes attributes) { |
- // We could assert that the property is configurable here, but we would need |
- // to do a lookup, which seems to be a bit of overkill. |
- bool only_attribute_changes = getter->IsNull() && setter->IsNull(); |
- if (object->HasFastProperties() && !only_attribute_changes && |
- (object->map()->NumberOfOwnDescriptors() <= kMaxNumberOfDescriptors)) { |
- bool getterOk = getter->IsNull() || |
- DefineFastAccessor(object, name, ACCESSOR_GETTER, getter, attributes); |
- bool setterOk = !getterOk || setter->IsNull() || |
- DefineFastAccessor(object, name, ACCESSOR_SETTER, setter, attributes); |
- if (getterOk && setterOk) return; |
- } |
- |
- Handle<AccessorPair> accessors = CreateAccessorPairFor(object, name); |
- accessors->SetComponents(*getter, *setter); |
- |
- SetPropertyCallback(object, name, accessors, attributes); |
-} |
- |
- |
bool Map::DictionaryElementsInPrototypeChainOnly() { |
if (IsDictionaryElementsKind(elements_kind())) { |
return false; |
@@ -6305,7 +6260,19 @@ MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object, |
if (is_element) { |
DefineElementAccessor(object, index, getter, setter, attributes); |
} else { |
- DefinePropertyAccessor(object, name, getter, setter, attributes); |
+ DCHECK(getter->IsSpecFunction() || getter->IsUndefined() || |
+ getter->IsNull()); |
+ DCHECK(setter->IsSpecFunction() || setter->IsUndefined() || |
+ setter->IsNull()); |
+ // At least one of the accessors needs to be a new value. |
+ DCHECK(!getter->IsNull() || !setter->IsNull()); |
+ LookupIterator it(object, name, LookupIterator::CHECK_PROPERTY); |
+ if (!getter->IsNull()) { |
+ it.TransitionToAccessorProperty(ACCESSOR_GETTER, getter, attributes); |
+ } |
+ if (!setter->IsNull()) { |
+ it.TransitionToAccessorProperty(ACCESSOR_SETTER, setter, attributes); |
+ } |
} |
if (is_observed) { |
@@ -6317,111 +6284,6 @@ MaybeHandle<Object> JSObject::DefineAccessor(Handle<JSObject> object, |
} |
-static bool TryAccessorTransition(Handle<JSObject> self, |
- Handle<Map> transitioned_map, |
- int target_descriptor, |
- AccessorComponent component, |
- Handle<Object> accessor, |
- PropertyAttributes attributes) { |
- DescriptorArray* descs = transitioned_map->instance_descriptors(); |
- PropertyDetails details = descs->GetDetails(target_descriptor); |
- |
- // If the transition target was not callbacks, fall back to the slow case. |
- if (details.type() != CALLBACKS) return false; |
- Object* descriptor = descs->GetCallbacksObject(target_descriptor); |
- if (!descriptor->IsAccessorPair()) return false; |
- |
- Object* target_accessor = AccessorPair::cast(descriptor)->get(component); |
- PropertyAttributes target_attributes = details.attributes(); |
- |
- // Reuse transition if adding same accessor with same attributes. |
- if (target_accessor == *accessor && target_attributes == attributes) { |
- JSObject::MigrateToMap(self, transitioned_map); |
- return true; |
- } |
- |
- // If either not the same accessor, or not the same attributes, fall back to |
- // the slow case. |
- return false; |
-} |
- |
- |
-bool JSObject::DefineFastAccessor(Handle<JSObject> object, |
- Handle<Name> name, |
- AccessorComponent component, |
- Handle<Object> accessor, |
- PropertyAttributes attributes) { |
- DCHECK(accessor->IsSpecFunction() || accessor->IsUndefined()); |
- Isolate* isolate = object->GetIsolate(); |
- LookupResult result(isolate); |
- object->LookupOwn(name, &result); |
- |
- if (result.IsFound() && !result.IsPropertyCallbacks()) { |
- return false; |
- } |
- |
- // Return success if the same accessor with the same attributes already exist. |
- AccessorPair* source_accessors = NULL; |
- if (result.IsPropertyCallbacks()) { |
- Object* callback_value = result.GetCallbackObject(); |
- if (callback_value->IsAccessorPair()) { |
- source_accessors = AccessorPair::cast(callback_value); |
- Object* entry = source_accessors->get(component); |
- if (entry == *accessor && result.GetAttributes() == attributes) { |
- return true; |
- } |
- } else { |
- return false; |
- } |
- |
- int descriptor_number = result.GetDescriptorIndex(); |
- |
- object->map()->LookupTransition(*object, *name, &result); |
- |
- if (result.IsFound()) { |
- Handle<Map> target(result.GetTransitionTarget()); |
- DCHECK(target->NumberOfOwnDescriptors() == |
- object->map()->NumberOfOwnDescriptors()); |
- // This works since descriptors are sorted in order of addition. |
- DCHECK(Name::Equals( |
- handle(object->map()->instance_descriptors()->GetKey( |
- descriptor_number)), |
- name)); |
- return TryAccessorTransition(object, target, descriptor_number, |
- component, accessor, attributes); |
- } |
- } else { |
- // If not, lookup a transition. |
- object->map()->LookupTransition(*object, *name, &result); |
- |
- // If there is a transition, try to follow it. |
- if (result.IsFound()) { |
- Handle<Map> target(result.GetTransitionTarget()); |
- int descriptor_number = target->LastAdded(); |
- DCHECK(Name::Equals(name, |
- handle(target->instance_descriptors()->GetKey(descriptor_number)))); |
- return TryAccessorTransition(object, target, descriptor_number, |
- component, accessor, attributes); |
- } |
- } |
- |
- // If there is no transition yet, add a transition to the a new accessor pair |
- // containing the accessor. Allocate a new pair if there were no source |
- // accessors. Otherwise, copy the pair and modify the accessor. |
- Handle<AccessorPair> accessors = source_accessors != NULL |
- ? AccessorPair::Copy(Handle<AccessorPair>(source_accessors)) |
- : isolate->factory()->NewAccessorPair(); |
- accessors->set(component, *accessor); |
- |
- CallbacksDescriptor new_accessors_desc(name, accessors, attributes); |
- Handle<Map> new_map = Map::CopyInsertDescriptor( |
- handle(object->map()), &new_accessors_desc, INSERT_TRANSITION); |
- |
- JSObject::MigrateToMap(object, new_map); |
- return true; |
-} |
- |
- |
MaybeHandle<Object> JSObject::SetAccessor(Handle<JSObject> object, |
Handle<AccessorInfo> info) { |
Isolate* isolate = object->GetIsolate(); |
@@ -7062,6 +6924,101 @@ Handle<Map> Map::ReconfigureDataProperty(Handle<Map> map, int descriptor, |
} |
+Handle<Map> Map::TransitionToAccessorProperty(Handle<Map> map, |
+ Handle<Name> name, |
+ AccessorComponent component, |
+ Handle<Object> accessor, |
+ PropertyAttributes attributes) { |
+ Isolate* isolate = name->GetIsolate(); |
+ |
+ // Dictionary maps can always have additional data properties. |
+ if (map->is_dictionary_map()) { |
+ // For global objects, property cells are inlined. We need to change the |
+ // map. |
+ if (map->IsGlobalObjectMap()) return Copy(map); |
+ return map; |
+ } |
+ |
+ // Migrate to the newest map before transitioning to the new property. |
+ if (map->is_deprecated()) map = Update(map); |
+ |
+ PropertyNormalizationMode mode = map->is_prototype_map() |
+ ? KEEP_INOBJECT_PROPERTIES |
+ : CLEAR_INOBJECT_PROPERTIES; |
+ |
+ int index = map->SearchTransition(*name); |
+ if (index != TransitionArray::kNotFound) { |
+ Handle<Map> transition(map->GetTransition(index)); |
+ DescriptorArray* descriptors = transition->instance_descriptors(); |
+ // Fast path, assume that we're modifying the last added descriptor. |
+ int descriptor = transition->LastAdded(); |
+ if (descriptors->GetKey(descriptor) != *name) { |
+ // If not, search for the descriptor. |
+ descriptor = descriptors->SearchWithCache(*name, *transition); |
+ } |
+ |
+ if (descriptors->GetDetails(descriptor).type() != CALLBACKS) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ // TODO(verwaest): Handle attributes better. |
+ if (descriptors->GetDetails(descriptor).attributes() != attributes) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate); |
+ if (!maybe_pair->IsAccessorPair()) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ Handle<AccessorPair> pair = Handle<AccessorPair>::cast(maybe_pair); |
+ if (pair->get(component) != *accessor) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ return transition; |
+ } |
+ |
+ Handle<AccessorPair> pair; |
+ DescriptorArray* old_descriptors = map->instance_descriptors(); |
+ int descriptor = old_descriptors->SearchWithCache(*name, *map); |
+ if (descriptor != DescriptorArray::kNotFound) { |
+ PropertyDetails old_details = old_descriptors->GetDetails(descriptor); |
+ if (old_details.type() != CALLBACKS) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ if (old_details.attributes() != attributes) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ Handle<Object> maybe_pair(old_descriptors->GetValue(descriptor), isolate); |
+ if (!maybe_pair->IsAccessorPair()) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ Object* current = Handle<AccessorPair>::cast(maybe_pair)->get(component); |
+ if (current == *accessor) return map; |
+ |
+ if (!current->IsTheHole()) { |
+ return Map::Normalize(map, mode); |
+ } |
+ |
+ pair = AccessorPair::Copy(Handle<AccessorPair>::cast(maybe_pair)); |
+ } else if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors || |
+ map->TooManyFastProperties(CERTAINLY_NOT_STORE_FROM_KEYED)) { |
+ return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); |
+ } else { |
+ pair = isolate->factory()->NewAccessorPair(); |
+ } |
+ |
+ pair->set(component, *accessor); |
+ TransitionFlag flag = INSERT_TRANSITION; |
+ CallbacksDescriptor new_desc(name, pair, attributes); |
+ return Map::CopyInsertDescriptor(map, &new_desc, flag); |
+} |
+ |
+ |
Handle<Map> Map::CopyAddDescriptor(Handle<Map> map, |
Descriptor* descriptor, |
TransitionFlag flag) { |