Object.oberve: assertions to narrow down flaky crashes with array length mutation.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 14 Dec 2012 14:19:18 +0000 (14:19 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 14 Dec 2012 14:19:18 +0000 (14:19 +0000)
R=mstarzinger@chromium.org
BUG=v8:2409

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

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

src/objects-debug.cc
src/objects-inl.h
src/objects.h
src/runtime.cc
test/mjsunit/harmony/object-observe.js

index e3226c1..5045e1e 100644 (file)
@@ -311,6 +311,9 @@ void Map::MapVerify() {
     SLOW_ASSERT(transitions()->IsSortedNoDuplicates());
     SLOW_ASSERT(transitions()->IsConsistentWithBackPointers(this));
   }
+  ASSERT(!is_observed() || instance_type() < FIRST_JS_OBJECT_TYPE ||
+         instance_type() > LAST_JS_OBJECT_TYPE ||
+         has_slow_elements_kind() || has_external_array_elements());
 }
 
 
index d61d3da..9e6044a 100644 (file)
@@ -1416,23 +1416,29 @@ void JSObject::initialize_elements() {
 
 
 MaybeObject* JSObject::ResetElements() {
-  Object* obj;
+  if (map()->is_observed()) {
+    // Maintain invariant that observed elements are always in dictionary mode.
+    SeededNumberDictionary* dictionary;
+    MaybeObject* maybe = SeededNumberDictionary::Allocate(0);
+    if (!maybe->To(&dictionary)) return maybe;
+    if (map() == GetHeap()->non_strict_arguments_elements_map()) {
+      FixedArray::cast(elements())->set(1, dictionary);
+    } else {
+      set_elements(dictionary);
+    }
+    return this;
+  }
+
   ElementsKind elements_kind = GetInitialFastElementsKind();
   if (!FLAG_smi_only_arrays) {
     elements_kind = FastSmiToObjectElementsKind(elements_kind);
   }
-  MaybeObject* maybe_obj = GetElementsTransitionMap(GetIsolate(),
-                                                    elements_kind);
-  if (!maybe_obj->ToObject(&obj)) return maybe_obj;
-  set_map(Map::cast(obj));
+  MaybeObject* maybe = GetElementsTransitionMap(GetIsolate(), elements_kind);
+  Map* map;
+  if (!maybe->To(&map)) return maybe;
+  set_map(map);
   initialize_elements();
-  if (FLAG_harmony_observation && map()->is_observed()) {
-    // Maintain invariant that observed elements are always in dictionary mode.
-    // For this to work on arrays, we have to make sure to reset length first.
-    if (IsJSArray()) JSArray::cast(this)->set_length(Smi::FromInt(0));
-    maybe_obj = NormalizeElements();
-    if (maybe_obj->IsFailure()) return maybe_obj;
-  }
+
   return this;
 }
 
@@ -3362,6 +3368,9 @@ bool Map::owns_descriptors() {
 
 
 void Map::set_is_observed(bool is_observed) {
+  ASSERT(instance_type() < FIRST_JS_OBJECT_TYPE ||
+         instance_type() > LAST_JS_OBJECT_TYPE ||
+         has_slow_elements_kind() || has_external_array_elements());
   set_bit_field3(IsObserved::update(bit_field3(), is_observed));
 }
 
index 13414dc..174f43d 100644 (file)
@@ -4764,6 +4764,10 @@ class Map: public HeapObject {
   inline void set_elements_kind(ElementsKind elements_kind) {
     ASSERT(elements_kind < kElementsKindCount);
     ASSERT(kElementsKindCount <= (1 << kElementsKindBitCount));
+    ASSERT(!is_observed() ||
+           elements_kind == DICTIONARY_ELEMENTS ||
+           elements_kind == NON_STRICT_ARGUMENTS_ELEMENTS ||
+           IsExternalArrayElementsKind(elements_kind));
     set_bit_field2((bit_field2() & ~kElementsKindMask) |
         (elements_kind << kElementsKindShift));
     ASSERT(this->elements_kind() == elements_kind);
index badcaab..c44a183 100644 (file)
@@ -4376,6 +4376,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) {
   Handle<Object> object = args.at<Object>(0);
   if (object->IsJSObject()) {
     Handle<JSObject> js_object(Handle<JSObject>::cast(object));
+    ASSERT(!js_object->map()->is_observed());
     ElementsKind new_kind = js_object->HasFastHoleyElements()
         ? FAST_HOLEY_DOUBLE_ELEMENTS
         : FAST_DOUBLE_ELEMENTS;
@@ -4392,6 +4393,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsDoubleToObject) {
   Handle<Object> object = args.at<Object>(0);
   if (object->IsJSObject()) {
     Handle<JSObject> js_object(Handle<JSObject>::cast(object));
+    ASSERT(!js_object->map()->is_observed());
     ElementsKind new_kind = js_object->HasFastHoleyElements()
         ? FAST_HOLEY_ELEMENTS
         : FAST_ELEMENTS;
@@ -13487,19 +13489,21 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetIsObserved) {
     ASSERT(proto->IsJSGlobalObject());
     obj = JSReceiver::cast(proto);
   }
+  ASSERT(!(obj->map()->is_observed() && obj->IsJSObject() &&
+           JSObject::cast(obj)->HasFastElements()));
   if (obj->map()->is_observed() != is_observed) {
-    MaybeObject* maybe = obj->map()->Copy();
-    Map* map;
-    if (!maybe->To(&map)) return maybe;
-    map->set_is_observed(is_observed);
-    obj->set_map(map);
     if (is_observed && obj->IsJSObject() &&
         !JSObject::cast(obj)->HasExternalArrayElements()) {
       // Go to dictionary mode, so that we don't skip map checks.
-      maybe = JSObject::cast(obj)->NormalizeElements();
+      MaybeObject* maybe = JSObject::cast(obj)->NormalizeElements();
       if (maybe->IsFailure()) return maybe;
       ASSERT(!JSObject::cast(obj)->HasFastElements());
     }
+    MaybeObject* maybe = obj->map()->Copy();
+    Map* map;
+    if (!maybe->To(&map)) return maybe;
+    map->set_is_observed(is_observed);
+    obj->set_map(map);
   }
   return isolate->heap()->undefined_value();
 }
index 4e2594f..4820582 100644 (file)
@@ -943,11 +943,8 @@ function TestFastElementsLength(polymorphic, optimize, oldSize, newSize) {
   }
 }
 
-// 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);
-*/