| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index 8cf7a58256c5b3b65b1dc1e811b6fd15fc83f457..431952811b36f8e4496905f49791786f6460a437 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -4433,7 +4433,12 @@ MaybeObject* JSObject::CreateAccessorPairFor(String* name) {
|
| LookupResult result(GetHeap()->isolate());
|
| LocalLookupRealNamedProperty(name, &result);
|
| if (result.IsProperty() && result.type() == CALLBACKS) {
|
| - ASSERT(!result.IsDontDelete());
|
| + // 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::cast(obj)->CopyWithoutTransitions();
|
| @@ -4447,6 +4452,29 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name,
|
| Object* getter,
|
| 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.
|
| + Heap* heap = GetHeap();
|
| + bool only_attribute_changes = getter->IsNull() && setter->IsNull();
|
| + if (FLAG_fast_accessor_properties &&
|
| + HasFastProperties() && !only_attribute_changes) {
|
| + MaybeObject* getterOk = heap->undefined_value();
|
| + if (!getter->IsNull()) {
|
| + getterOk = DefineFastAccessor(name, ACCESSOR_GETTER, getter, attributes);
|
| + if (getterOk->IsFailure()) return getterOk;
|
| + }
|
| +
|
| + MaybeObject* setterOk = heap->undefined_value();
|
| + if (getterOk != heap->null_value() && !setter->IsNull()) {
|
| + setterOk = DefineFastAccessor(name, ACCESSOR_SETTER, setter, attributes);
|
| + if (setterOk->IsFailure()) return setterOk;
|
| + }
|
| +
|
| + if (getterOk != heap->null_value() && setterOk != heap->null_value()) {
|
| + return heap->undefined_value();
|
| + }
|
| + }
|
| +
|
| AccessorPair* accessors;
|
| { MaybeObject* maybe_accessors = CreateAccessorPairFor(name);
|
| if (!maybe_accessors->To(&accessors)) return maybe_accessors;
|
| @@ -4596,6 +4624,159 @@ MaybeObject* JSObject::DefineAccessor(String* name,
|
| }
|
|
|
|
|
| +static MaybeObject* CreateFreshAccessor(JSObject* obj,
|
| + String* name,
|
| + AccessorComponent component,
|
| + Object* accessor,
|
| + PropertyAttributes attributes) {
|
| + // step 1: create a new getter/setter pair with only the accessor in it
|
| + Heap* heap = obj->GetHeap();
|
| + AccessorPair* accessors2;
|
| + { MaybeObject* maybe_accessors2 = heap->AllocateAccessorPair();
|
| + if (!maybe_accessors2->To(&accessors2)) return maybe_accessors2;
|
| + }
|
| + accessors2->set(component, accessor);
|
| +
|
| + // step 2: create a copy of the descriptors, incl. the new getter/setter pair
|
| + Map* map1 = obj->map();
|
| + CallbacksDescriptor callbacks_descr2(name, accessors2, attributes);
|
| + DescriptorArray* descriptors2;
|
| + { MaybeObject* maybe_descriptors2 =
|
| + map1->instance_descriptors()->CopyInsert(&callbacks_descr2,
|
| + REMOVE_TRANSITIONS);
|
| + if (!maybe_descriptors2->To(&descriptors2)) return maybe_descriptors2;
|
| + }
|
| +
|
| + // step 3: create a new map with the new descriptors
|
| + Map* map2;
|
| + { MaybeObject* maybe_map2 = map1->CopyDropDescriptors();
|
| + if (!maybe_map2->To(&map2)) return maybe_map2;
|
| + }
|
| + map2->set_instance_descriptors(descriptors2);
|
| +
|
| + // step 4: create a new getter/setter pair with a transition to the new map
|
| + AccessorPair* accessors1;
|
| + { MaybeObject* maybe_accessors1 = heap->AllocateAccessorPair();
|
| + if (!maybe_accessors1->To(&accessors1)) return maybe_accessors1;
|
| + }
|
| + accessors1->set(component, map2);
|
| +
|
| + // step 5: create a copy of the descriptors, incl. the new getter/setter pair
|
| + // with the transition
|
| + CallbacksDescriptor callbacks_descr1(name, accessors1, attributes);
|
| + DescriptorArray* descriptors1;
|
| + { MaybeObject* maybe_descriptors1 =
|
| + map1->instance_descriptors()->CopyInsert(&callbacks_descr1,
|
| + KEEP_TRANSITIONS);
|
| + if (!maybe_descriptors1->To(&descriptors1)) return maybe_descriptors1;
|
| + }
|
| +
|
| + // step 6: everything went well so far, so we make our changes visible
|
| + obj->set_map(map2);
|
| + map1->set_instance_descriptors(descriptors1);
|
| + map2->SetBackPointer(map1);
|
| + return obj;
|
| +}
|
| +
|
| +
|
| +static bool TransitionToSameAccessor(Object* map,
|
| + String* name,
|
| + AccessorComponent component,
|
| + Object* accessor,
|
| + PropertyAttributes attributes ) {
|
| + DescriptorArray* descs = Map::cast(map)->instance_descriptors();
|
| + int number = descs->SearchWithCache(name);
|
| + ASSERT(number != DescriptorArray::kNotFound);
|
| + Object* target_accessor =
|
| + AccessorPair::cast(descs->GetCallbacksObject(number))->get(component);
|
| + PropertyAttributes target_attributes = descs->GetDetails(number).attributes();
|
| + return target_accessor == accessor && target_attributes == attributes;
|
| +}
|
| +
|
| +
|
| +static MaybeObject* NewCallbackTransition(JSObject* obj,
|
| + String* name,
|
| + AccessorComponent component,
|
| + Object* accessor,
|
| + PropertyAttributes attributes,
|
| + AccessorPair* accessors2) {
|
| + // step 1: copy the old getter/setter pair and set the new accessor
|
| + AccessorPair* accessors3;
|
| + { MaybeObject* maybe_accessors3 = accessors2->CopyWithoutTransitions();
|
| + if (!maybe_accessors3->To(&accessors3)) return maybe_accessors3;
|
| + }
|
| + accessors3->set(component, accessor);
|
| +
|
| + // step 2: create a copy of the descriptors, incl. the new getter/setter pair
|
| + Map* map2 = obj->map();
|
| + CallbacksDescriptor callbacks_descr3(name, accessors3, attributes);
|
| + DescriptorArray* descriptors3;
|
| + { MaybeObject* maybe_descriptors3 =
|
| + map2->instance_descriptors()->CopyInsert(&callbacks_descr3,
|
| + REMOVE_TRANSITIONS);
|
| + if (!maybe_descriptors3->To(&descriptors3)) return maybe_descriptors3;
|
| + }
|
| +
|
| + // step 3: create a new map with the new descriptors
|
| + Map* map3;
|
| + { MaybeObject* maybe_map3 = map2->CopyDropDescriptors();
|
| + if (!maybe_map3->To(&map3)) return maybe_map3;
|
| + }
|
| + map3->set_instance_descriptors(descriptors3);
|
| +
|
| + // step 4: everything went well so far, so we make our changes visible
|
| + obj->set_map(map3);
|
| + accessors2->set(component, map3);
|
| + map3->SetBackPointer(map2);
|
| + return obj;
|
| +}
|
| +
|
| +
|
| +MaybeObject* JSObject::DefineFastAccessor(String* name,
|
| + AccessorComponent component,
|
| + Object* accessor,
|
| + PropertyAttributes attributes) {
|
| + ASSERT(accessor->IsSpecFunction() || accessor->IsUndefined());
|
| + LookupResult result(GetIsolate());
|
| + LocalLookup(name, &result);
|
| +
|
| + // If we have a new property, create a fresh accessor plus a transition to it.
|
| + if (!result.IsFound()) {
|
| + return CreateFreshAccessor(this, name, component, accessor, attributes);
|
| + }
|
| +
|
| + // If the property is not a JavaScript accessor, fall back to the slow case.
|
| + if (result.type() != CALLBACKS) return GetHeap()->null_value();
|
| + Object* callback_value = result.GetCallbackObject();
|
| + if (!callback_value->IsAccessorPair()) return GetHeap()->null_value();
|
| + AccessorPair* accessors = AccessorPair::cast(callback_value);
|
| +
|
| + // Follow a callback transition, if there is a fitting one.
|
| + Object* entry = accessors->get(component);
|
| + if (entry->IsMap() &&
|
| + TransitionToSameAccessor(entry, name, component, accessor, attributes)) {
|
| + set_map(Map::cast(entry));
|
| + return this;
|
| + }
|
| +
|
| + // When we re-add the same accessor again, there is nothing to do.
|
| + if (entry == accessor && result.GetAttributes() == attributes) return this;
|
| +
|
| + // Only the other accessor has been set so far, create a new transition.
|
| + if (entry->IsTheHole()) {
|
| + return NewCallbackTransition(this,
|
| + name,
|
| + component,
|
| + accessor,
|
| + attributes,
|
| + accessors);
|
| + }
|
| +
|
| + // Nothing from the above worked, so we have to fall back to the slow case.
|
| + return GetHeap()->null_value();
|
| +}
|
| +
|
| +
|
| MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
|
| Isolate* isolate = GetIsolate();
|
| String* name = String::cast(info->name());
|
| @@ -5754,21 +5935,15 @@ MaybeObject* DescriptorArray::CopyInsert(Descriptor* descriptor,
|
| int index = Search(descriptor->GetKey());
|
| const bool replacing = (index != kNotFound);
|
| bool keep_enumeration_index = false;
|
| - if (replacing) {
|
| - // We are replacing an existing descriptor. We keep the enumeration
|
| - // index of a visible property.
|
| - PropertyType t = GetDetails(index).type();
|
| - if (t == CONSTANT_FUNCTION ||
|
| - t == FIELD ||
|
| - t == CALLBACKS ||
|
| - t == INTERCEPTOR) {
|
| - keep_enumeration_index = true;
|
| - } else if (remove_transitions) {
|
| - // Replaced descriptor has been counted as removed if it is
|
| - // a transition that will be replaced. Adjust count in this case.
|
| - ++new_size;
|
| - }
|
| - } else {
|
| + if (!replacing) {
|
| + ++new_size;
|
| + } else if (!IsTransitionOnly(index)) {
|
| + // We are replacing an existing descriptor. We keep the enumeration index
|
| + // of a visible property.
|
| + keep_enumeration_index = true;
|
| + } else if (remove_transitions) {
|
| + // Replaced descriptor has been counted as removed if it is a transition
|
| + // that will be replaced. Adjust count in this case.
|
| ++new_size;
|
| }
|
|
|
| @@ -5966,8 +6141,8 @@ MaybeObject* AccessorPair::CopyWithoutTransitions() {
|
|
|
|
|
| Object* AccessorPair::GetComponent(AccessorComponent component) {
|
| - Object* accessor = (component == ACCESSOR_GETTER) ? getter() : setter();
|
| - return accessor->IsTheHole() ? GetHeap()->undefined_value() : accessor;
|
| + Object* accessor = get(component);
|
| + return accessor->IsTheHole() ? GetHeap()->undefined_value() : accessor;
|
| }
|
|
|
|
|
|
|