Revision: 13213
Author:   [email protected]
Date:     Thu Dec 13 01:31:44 2012
Log: Object.observe: Make array length and other magic data properties work correctly.

Also, disable TestFastElementsLength test for now, since it flakes on buildbots for yet unknown reasons.

[email protected]
BUG=v8:2409

Review URL: https://codereview.chromium.org/11554019
http://code.google.com/p/v8/source/detail?r=13213

Modified:
 /branches/bleeding_edge/src/object-observe.js
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/property.h
 /branches/bleeding_edge/src/v8natives.js
 /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js

=======================================
--- /branches/bleeding_edge/src/object-observe.js       Wed Dec  5 04:03:57 2012
+++ /branches/bleeding_edge/src/object-observe.js       Thu Dec 13 01:31:44 2012
@@ -84,8 +84,8 @@
   var objectInfo = objectInfoMap.get(object);
   if (IS_UNDEFINED(objectInfo)) {
     objectInfo = CreateObjectInfo(object);
-    %SetIsObserved(object, true);
   }
+  %SetIsObserved(object, true);

   var changeObservers = objectInfo.changeObservers;
   if (changeObservers.indexOf(callback) < 0)
@@ -106,8 +106,11 @@

   var changeObservers = objectInfo.changeObservers;
   var index = changeObservers.indexOf(callback);
-  if (index >= 0)
+  if (index >= 0) {
     changeObservers.splice(index, 1);
+    if (changeObservers.length === 0)
+      %SetIsObserved(object, false);
+  }

   return object;
 }
=======================================
--- /branches/bleeding_edge/src/objects.cc      Wed Dec 12 03:38:24 2012
+++ /branches/bleeding_edge/src/objects.cc      Thu Dec 13 01:31:44 2012
@@ -2943,8 +2943,9 @@
   }

   Handle<Object> old_value(heap->the_hole_value(), isolate);
-  if (FLAG_harmony_observation && map()->is_observed()) {
-    old_value = handle(lookup->GetLazyValue(), isolate);
+  if (FLAG_harmony_observation &&
+      map()->is_observed() && lookup->IsDataProperty()) {
+    old_value = Object::GetProperty(self, name);
   }

   // This is a real property that is not read-only, or it is a
@@ -3030,8 +3031,8 @@
     } else {
       LookupResult new_lookup(isolate);
       self->LocalLookup(*name, &new_lookup, true);
-      ASSERT(!new_lookup.GetLazyValue()->IsTheHole());
-      if (!new_lookup.GetLazyValue()->SameValue(*old_value)) {
+      if (new_lookup.IsDataProperty() &&
+          !Object::GetProperty(self, name)->SameValue(*old_value)) {
         EnqueueChangeRecord(self, "updated", name, old_value);
       }
     }
@@ -3110,15 +3111,7 @@
   PropertyAttributes old_attributes = ABSENT;
bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
   if (is_observed) {
-    // Function prototypes are stored specially
-    if (self->IsJSFunction() &&
-        JSFunction::cast(*self)->should_have_prototype() &&
-        name->Equals(isolate->heap()->prototype_symbol())) {
-      MaybeObject* maybe = Accessors::FunctionGetPrototype(*self, NULL);
-      if (!maybe->ToHandle(&old_value, isolate)) return maybe;
-    } else {
-      old_value = handle(lookup.GetLazyValue(), isolate);
-    }
+ if (lookup.IsDataProperty()) old_value = Object::GetProperty(self, name);
     old_attributes = lookup.GetAttributes();
   }

@@ -3188,11 +3181,11 @@
     } else {
       LookupResult new_lookup(isolate);
       self->LocalLookup(*name, &new_lookup, true);
-      ASSERT(!new_lookup.GetLazyValue()->IsTheHole());
       if (old_value->IsTheHole() ||
           new_lookup.GetAttributes() != old_attributes) {
         EnqueueChangeRecord(self, "reconfigured", name, old_value);
-      } else if (!new_lookup.GetLazyValue()->SameValue(*old_value)) {
+      } else if (new_lookup.IsDataProperty() &&
+          !Object::GetProperty(self, name)->SameValue(*old_value)) {
         EnqueueChangeRecord(self, "updated", name, old_value);
       }
     }
@@ -4255,8 +4248,8 @@

   Handle<Object> old_value(isolate->heap()->the_hole_value());
bool is_observed = FLAG_harmony_observation && self->map()->is_observed();
-  if (is_observed) {
-    old_value = handle(lookup.GetLazyValue(), isolate);
+  if (is_observed && lookup.IsDataProperty()) {
+    old_value = Object::GetProperty(self, hname);
   }
   MaybeObject* result;

@@ -4947,7 +4940,9 @@
       LookupResult lookup(isolate);
       LocalLookup(*name, &lookup, true);
       preexists = lookup.IsProperty();
-      if (preexists) old_value = handle(lookup.GetLazyValue(), isolate);
+      if (preexists && lookup.IsDataProperty()) {
+        old_value = Object::GetProperty(self, name);
+      }
     }
   }

=======================================
--- /branches/bleeding_edge/src/property.h      Tue Nov 13 03:07:04 2012
+++ /branches/bleeding_edge/src/property.h      Thu Dec 13 01:31:44 2012
@@ -309,6 +309,26 @@
   bool IsProperty() {
     return IsFound() && !IsTransition();
   }
+
+  bool IsDataProperty() {
+    switch (type()) {
+      case FIELD:
+      case NORMAL:
+      case CONSTANT_FUNCTION:
+        return true;
+      case CALLBACKS: {
+        Object* callback = GetCallbackObject();
+        return callback->IsAccessorInfo() || callback->IsForeign();
+      }
+      case HANDLER:
+      case INTERCEPTOR:
+      case TRANSITION:
+      case NONEXISTENT:
+        return false;
+    }
+    UNREACHABLE();
+    return false;
+  }

   bool IsCacheable() { return cacheable_; }
   void DisallowCaching() { cacheable_ = false; }
@@ -327,9 +347,15 @@
       }
       case CONSTANT_FUNCTION:
         return GetConstantFunction();
-      default:
+      case CALLBACKS:
+      case HANDLER:
+      case INTERCEPTOR:
+      case TRANSITION:
+      case NONEXISTENT:
         return Isolate::Current()->heap()->the_hole_value();
     }
+    UNREACHABLE();
+    return NULL;
   }

   Map* GetTransitionTarget() {
=======================================
--- /branches/bleeding_edge/src/v8natives.js    Fri Dec  7 04:18:50 2012
+++ /branches/bleeding_edge/src/v8natives.js    Thu Dec 13 01:31:44 2012
@@ -893,16 +893,35 @@
     }
     // Make sure the below call to DefineObjectProperty() doesn't overwrite
     // any magic "length" property by removing the value.
+ // TODO(mstarzinger): This hack should be removed once we have addressed the
+    // respective TODO in Runtime_DefineOrRedefineDataProperty.
+    // For the time being, we need a hack to prevent Object.observe from
+    // generating two change records.
+    var isObserved = %IsObserved(obj);
+    if (isObserved) %SetIsObserved(obj, false);
     obj.length = new_length;
     desc.value_ = void 0;
     desc.hasValue_ = false;
- if (!DefineObjectProperty(obj, "length", desc, should_throw) || threw) { + threw = !DefineObjectProperty(obj, "length", desc, should_throw) || threw;
+    if (isObserved) %SetIsObserved(obj, true);
+    if (threw) {
       if (should_throw) {
         throw MakeTypeError("redefine_disallowed", [p]);
       } else {
         return false;
       }
     }
+    if (isObserved) {
+      var new_desc = GetOwnProperty(obj, "length");
+      var updated = length_desc.value_ !== new_desc.value_;
+      var reconfigured = length_desc.writable_ !== new_desc.writable_ ||
+          length_desc.configurable_ !== new_desc.configurable_ ||
+          length_desc.enumerable_ !== new_desc.configurable_;
+      if (updated || reconfigured) {
+        NotifyChange(reconfigured ? "reconfigured" : "updated",
+            obj, "length", length_desc.value_);
+      }
+    }
     return true;
   }

=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Wed Dec 12 03:38:24 2012 +++ /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Thu Dec 13 01:31:44 2012
@@ -536,16 +536,13 @@
   createProxy(Proxy.create, null),
   createProxy(Proxy.createFunction, function(){}),
 ];
-var properties = ["a", "1", 1, "length", "prototype"];
+var properties = ["a", "1", 1, "length", "prototype", "name", "caller"];

 // Cases that yield non-standard results.
-// TODO(observe): ...or don't work yet.
 function blacklisted(obj, prop) {
   return (obj instanceof Int32Array && prop == 1) ||
     (obj instanceof Int32Array && prop === "length") ||
-    (obj instanceof ArrayBuffer && prop == 1) ||
-    // TODO(observe): oldValue when reconfiguring array length
-    (obj instanceof Array && prop === "length")
+    (obj instanceof ArrayBuffer && prop == 1)
 }

 for (var i in objects) for (var j in properties) {
@@ -581,8 +578,10 @@
 arr.length = 2;
 arr.length = 0;
 arr.length = 10;
+Object.defineProperty(arr, 'length', {writable: false});
 arr2.length = 0;
 arr2.length = 1; // no change expected
+Object.defineProperty(arr2, 'length', {value: 1, writable: false});
 arr3.length = 0;
 Object.defineProperty(arr3, 'length', {value: 5});
 Object.defineProperty(arr3, 'length', {value: 10, writable: false});
@@ -594,15 +593,15 @@
   { object: arr, name: '1', type: 'deleted', oldValue: 'b' },
   { object: arr, name: 'length', type: 'updated', oldValue: 2 },
   { object: arr, name: 'length', type: 'updated', oldValue: 1 },
+  { object: arr, name: 'length', type: 'reconfigured', oldValue: 10 },
   { object: arr2, name: '1', type: 'deleted', oldValue: 'beta' },
   { object: arr2, name: 'length', type: 'updated', oldValue: 2 },
+  { object: arr2, name: 'length', type: 'reconfigured', oldValue: 1 },
   { object: arr3, name: '2', type: 'deleted', oldValue: 'goodbye' },
   { object: arr3, name: '0', type: 'deleted', oldValue: 'hello' },
   { object: arr3, name: 'length', type: 'updated', oldValue: 6 },
   { object: arr3, name: 'length', type: 'updated', oldValue: 0 },
-  { object: arr3, name: 'length', type: 'updated', oldValue: 5 },
-  // TODO(adamk): This record should be merged with the above
-  { object: arr3, name: 'length', type: 'reconfigured' },
+  { object: arr3, name: 'length', type: 'reconfigured', oldValue: 5 },
 ]);

 // Assignments in loops (checking different IC states).
@@ -944,8 +943,11 @@
   }
 }

+// TODO(rossberg): Still flaky on buildbots, disable for now...
+/*
 for (var b1 = 0; b1 < 2; ++b1)
   for (var b2 = 0; b2 < 2; ++b2)
     for (var n1 = 0; n1 < 3; ++n1)
       for (var n2 = 0; n2 < 3; ++n2)
         TestFastElementsLength(b1 != 0, b2 != 0, 20*n1, 20*n2);
+*/

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to