Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(725)

Unified Diff: src/objects.cc

Issue 10238005: Use map transitions when defining accessor properties. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/objects.cc
diff --git a/src/objects.cc b/src/objects.cc
index 41f2ab6d8823f2e85ee1ec636e0016a495273fa6..6d026ae3c19216bcec6f74983e09fb007f25040b 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -4413,7 +4413,11 @@ 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.
Object* obj = result.GetCallbackObject();
if (obj->IsAccessorPair()) {
return AccessorPair::cast(obj)->CopyWithoutTransitions();
@@ -4427,6 +4431,26 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name,
Object* getter,
Object* setter,
PropertyAttributes attributes) {
+ bool only_attribute_changes = getter->IsNull() && setter->IsNull();
+ if (HasFastProperties() && !only_attribute_changes) {
+ MaybeObject* getterOk = GetHeap()->undefined_value();
Michael Starzinger 2012/05/02 13:30:14 Can we move the result of "GetHeap()" into a local
Sven Panne 2012/05/03 09:39:02 Done for the occurrences where it is actually used
+ if (!getter->IsNull()) {
+ getterOk = DefineFastAccessor(name, ACCESSOR_GETTER, getter, attributes);
+ if (getterOk->IsFailure()) return getterOk;
+ }
+
+ MaybeObject* setterOk = GetHeap()->undefined_value();
+ if (getterOk != GetHeap()->null_value() && !setter->IsNull()) {
+ setterOk = DefineFastAccessor(name, ACCESSOR_SETTER, setter, attributes);
+ if (setterOk->IsFailure()) return setterOk;
+ }
+
+ if (getterOk != GetHeap()->null_value() &&
+ setterOk != GetHeap()->null_value()) {
+ return GetHeap()->undefined_value();
+ }
+ }
+
AccessorPair* accessors;
{ MaybeObject* maybe_accessors = CreateAccessorPairFor(name);
if (!maybe_accessors->To(&accessors)) return maybe_accessors;
@@ -4576,6 +4600,148 @@ MaybeObject* JSObject::DefineAccessor(String* name,
}
+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;
+}
+
+
+MaybeObject* JSObject::DefineFastAccessor(String* name,
+ AccessorComponent component,
+ Object* accessor,
+ PropertyAttributes attributes) {
+ 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(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.GetValue();
+ 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 NewCallbackTrans(name, component, accessor, attributes, accessors);
Michael Starzinger 2012/05/02 13:30:14 Can we add an assertion to the top of this method
Sven Panne 2012/05/03 09:39:02 I'll add a slightly stronger assertion, checking t
+ }
+
+ // Nothing from the above worked, so we have to fall back to the slow case.
+ return GetHeap()->null_value();
+}
+
+
+MaybeObject* JSObject::CreateFreshAccessor(String* name,
Michael Starzinger 2012/05/02 13:30:14 Can we turn this method into a static helper that
Sven Panne 2012/05/03 09:39:02 As discussed offline, let's just simply pass 'this
+ AccessorComponent component,
+ Object* accessor,
+ PropertyAttributes attributes) {
+ // step 1: create a new getter/setter pair with only the accessor in it
+ AccessorPair* accessors2;
+ { MaybeObject* maybe_accessors2 = GetHeap()->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 = 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 = GetHeap()->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
+ set_map(map2);
+ map1->set_instance_descriptors(descriptors1);
+ return this;
+}
+
+
+MaybeObject* JSObject::NewCallbackTrans(String* name,
Michael Starzinger 2012/05/02 13:30:14 Likewise. Also the full name "NewCallbackTransitio
Sven Panne 2012/05/03 09:39:02 It was because of our 80-column limit. :-) Done.
+ 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 = 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
+ set_map(map3);
+ accessors2->set(component, map3);
+ return this;
+}
+
+
MaybeObject* JSObject::DefineAccessor(AccessorInfo* info) {
Isolate* isolate = GetIsolate();
String* name = String::cast(info->name());
@@ -5939,8 +6105,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;
}
« src/objects.h ('K') | « src/objects.h ('k') | test/mjsunit/accessor-map-sharing.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698