Ensure prototypes always stay fast by turning them fast again after an operation...
authorverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 4 Aug 2014 15:06:28 +0000 (15:06 +0000)
committerverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 4 Aug 2014 15:06:28 +0000 (15:06 +0000)
BUG=
R=ishell@chromium.org

Review URL: https://codereview.chromium.org/439243005

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22827 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/objects.cc
src/objects.h
src/runtime.cc
test/mjsunit/dictionary-properties.js

index ebf1844..592955d 100644 (file)
@@ -4067,10 +4067,13 @@ void JSObject::ConvertAndSetOwnProperty(LookupResult* lookup,
   Handle<JSObject> object(lookup->holder());
   if (object->map()->TooManyFastProperties(Object::MAY_BE_STORE_FROM_KEYED)) {
     JSObject::NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
+  } else if (object->map()->is_prototype_map()) {
+    JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0);
   }
 
   if (!object->HasFastProperties()) {
     ReplaceSlowProperty(object, name, value, attributes);
+    ReoptimizeIfPrototype(object);
     return;
   }
 
@@ -5151,17 +5154,22 @@ Handle<Object> JSObject::SetHiddenPropertiesHashTable(Handle<JSObject> object,
 
 Handle<Object> JSObject::DeletePropertyPostInterceptor(Handle<JSObject> object,
                                                        Handle<Name> name,
-                                                       DeleteMode mode) {
+                                                       DeleteMode delete_mode) {
   // Check own property, ignore interceptor.
   Isolate* isolate = object->GetIsolate();
-  LookupResult result(isolate);
-  object->LookupOwnRealNamedProperty(name, &result);
-  if (!result.IsFound()) return isolate->factory()->true_value();
+  LookupResult lookup(isolate);
+  object->LookupOwnRealNamedProperty(name, &lookup);
+  if (!lookup.IsFound()) return isolate->factory()->true_value();
 
+  PropertyNormalizationMode mode = object->map()->is_prototype_map()
+                                       ? KEEP_INOBJECT_PROPERTIES
+                                       : CLEAR_INOBJECT_PROPERTIES;
   // Normalize object if needed.
-  NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
+  NormalizeProperties(object, mode, 0);
 
-  return DeleteNormalizedProperty(object, name, mode);
+  Handle<Object> result = DeleteNormalizedProperty(object, name, delete_mode);
+  ReoptimizeIfPrototype(object);
+  return result;
 }
 
 
@@ -5308,7 +5316,7 @@ MaybeHandle<Object> JSObject::DeleteElement(Handle<JSObject> object,
 
 MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
                                              Handle<Name> name,
-                                             DeleteMode mode) {
+                                             DeleteMode delete_mode) {
   Isolate* isolate = object->GetIsolate();
   // ECMA-262, 3rd, 8.6.2.5
   DCHECK(name->IsName());
@@ -5327,20 +5335,20 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
     DCHECK(PrototypeIterator::GetCurrent(iter)->IsJSGlobalObject());
     return JSGlobalObject::DeleteProperty(
         Handle<JSGlobalObject>::cast(PrototypeIterator::GetCurrent(iter)), name,
-        mode);
+        delete_mode);
   }
 
   uint32_t index = 0;
   if (name->AsArrayIndex(&index)) {
-    return DeleteElement(object, index, mode);
+    return DeleteElement(object, index, delete_mode);
   }
 
   LookupResult lookup(isolate);
   object->LookupOwn(name, &lookup, true);
   if (!lookup.IsFound()) return isolate->factory()->true_value();
   // Ignore attributes if forcing a deletion.
-  if (lookup.IsDontDelete() && mode != FORCE_DELETION) {
-    if (mode == STRICT_DELETION) {
+  if (lookup.IsDontDelete() && delete_mode != FORCE_DELETION) {
+    if (delete_mode == STRICT_DELETION) {
       // Deleting a non-configurable property in strict mode.
       Handle<Object> args[2] = { name, object };
       Handle<Object> error = isolate->factory()->NewTypeError(
@@ -5362,8 +5370,8 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
   // Check for interceptor.
   if (lookup.IsInterceptor()) {
     // Skip interceptor if forcing a deletion.
-    if (mode == FORCE_DELETION) {
-      result = DeletePropertyPostInterceptor(object, name, mode);
+    if (delete_mode == FORCE_DELETION) {
+      result = DeletePropertyPostInterceptor(object, name, delete_mode);
     } else {
       ASSIGN_RETURN_ON_EXCEPTION(
           isolate, result,
@@ -5371,10 +5379,14 @@ MaybeHandle<Object> JSObject::DeleteProperty(Handle<JSObject> object,
           Object);
     }
   } else {
+    PropertyNormalizationMode mode = object->map()->is_prototype_map()
+                                         ? KEEP_INOBJECT_PROPERTIES
+                                         : CLEAR_INOBJECT_PROPERTIES;
     // Normalize object if needed.
-    NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
+    NormalizeProperties(object, mode, 0);
     // Make sure the properties are normalized before removing the entry.
-    result = DeleteNormalizedProperty(object, name, mode);
+    result = DeleteNormalizedProperty(object, name, delete_mode);
+    ReoptimizeIfPrototype(object);
   }
 
   if (is_observed) {
@@ -5696,6 +5708,7 @@ MaybeHandle<Object> JSObject::Freeze(Handle<JSObject> object) {
     Handle<Map> new_map = Map::CopyForFreeze(old_map);
     JSObject::MigrateToMap(object, new_map);
   } else {
+    DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map());
     // Slow path: need to normalize properties for safety
     NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
 
@@ -6594,8 +6607,11 @@ void JSObject::SetPropertyCallback(Handle<JSObject> object,
                                    Handle<Name> name,
                                    Handle<Object> structure,
                                    PropertyAttributes attributes) {
+  PropertyNormalizationMode mode = object->map()->is_prototype_map()
+                                       ? KEEP_INOBJECT_PROPERTIES
+                                       : CLEAR_INOBJECT_PROPERTIES;
   // Normalize object to make this operation simple.
-  NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
+  NormalizeProperties(object, mode, 0);
 
   // For the global object allocate a new map to invalidate the global inline
   // caches which have a global property cell reference directly in the code.
@@ -6613,6 +6629,8 @@ void JSObject::SetPropertyCallback(Handle<JSObject> object,
   // Update the dictionary with the new CALLBACKS property.
   PropertyDetails details = PropertyDetails(attributes, CALLBACKS, 0);
   SetNormalizedProperty(object, name, structure, details);
+
+  ReoptimizeIfPrototype(object);
 }
 
 
@@ -9970,6 +9988,12 @@ void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
 }
 
 
+void JSObject::ReoptimizeIfPrototype(Handle<JSObject> object) {
+  if (!object->map()->is_prototype_map()) return;
+  OptimizeAsPrototype(object);
+}
+
+
 Handle<Object> CacheInitialJSArrayMaps(
     Handle<Context> native_context, Handle<Map> initial_map) {
   // Replace all of the cached initial array maps in the native context with
@@ -10132,9 +10156,6 @@ void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
   Handle<Object> prototype;
   if (function->has_instance_prototype()) {
     prototype = handle(function->instance_prototype(), isolate);
-    // TODO(verwaest): Remove once "delete" keeps objects marked as prototypes
-    // fast as well.
-    JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(prototype));
   } else {
     prototype = isolate->factory()->NewFunctionPrototype(function);
   }
index c86b3d3..a035c72 100644 (file)
@@ -2197,6 +2197,7 @@ class JSObject: public JSReceiver {
                                     PropertyDetails details);
 
   static void OptimizeAsPrototype(Handle<JSObject> object);
+  static void ReoptimizeIfPrototype(Handle<JSObject> object);
 
   // Retrieve interceptors.
   InterceptorInfo* GetNamedInterceptor();
index 02f2aa4..fa304e7 100644 (file)
@@ -5009,20 +5009,6 @@ RUNTIME_FUNCTION(Runtime_DefineDataPropertyUnchecked) {
   // map.
   if (lookup.IsFound() &&
       (attr != lookup.GetAttributes() || lookup.IsPropertyCallbacks())) {
-    // New attributes - normalize to avoid writing to instance descriptor
-    if (js_object->IsJSGlobalProxy()) {
-      // Since the result is a property, the prototype will exist so
-      // we don't have to check for null.
-      PrototypeIterator iter(isolate, js_object);
-      js_object = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
-    }
-
-    if (attr != lookup.GetAttributes() ||
-        (lookup.IsPropertyCallbacks() &&
-         !lookup.GetCallbackObject()->IsAccessorInfo())) {
-      JSObject::NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0);
-    }
-
     // Use IgnoreAttributes version since a readonly property may be
     // overridden and SetProperty does not allow this.
     Handle<Object> result;
index c70c598..9563f3a 100644 (file)
@@ -40,7 +40,8 @@ SlowPrototype.prototype.bar = 2;
 SlowPrototype.prototype.baz = 3;
 delete SlowPrototype.prototype.baz;
 
-assertFalse(%HasFastProperties(SlowPrototype.prototype));
+// Prototypes stay fast even after deleting properties.
+assertTrue(%HasFastProperties(SlowPrototype.prototype));
 var fast_proto = new SlowPrototype();
 assertTrue(%HasFastProperties(SlowPrototype.prototype));
 assertTrue(%HasFastProperties(fast_proto.__proto__));