Reviewers: rafaelw, rossberg,

Message:
Just sending out so folks can take a look, this will likely be rolled up in
Rafael's patch as he writes tests for arrays.

Description:
Handle Object.observe notifications for setting Array.length

Also handles notification of deleted properties when an array
is truncated by setting length.

Still needs tests.

Please review this at https://codereview.chromium.org/11338048/

SVN Base: [email protected]:rafaelw/v8@master

Affected files:
  M src/accessors.cc
  M src/heap.h
  M src/object-observe.cc


Index: src/accessors.cc
diff --git a/src/accessors.cc b/src/accessors.cc
index 1bc9221a20c5c973c020bfea7f90cd0a46ce4316..5e39dea8c21fe1c59c57d5546826fd0e13b23e2c 100644
--- a/src/accessors.cc
+++ b/src/accessors.cc
@@ -35,6 +35,7 @@
 #include "frames-inl.h"
 #include "isolate.h"
 #include "list-inl.h"
+#include "object-observe.h"
 #include "property-details.h"

 namespace v8 {
@@ -95,6 +96,67 @@ Object* Accessors::FlattenNumber(Object* value) {
 }


+static MaybeObject* ArraySetLengthObserved(Isolate* isolate,
+                                           Handle<JSArray> array,
+ Handle<Object> new_length_handle) {
+  List<Handle<Object> > indices;
+  List<Handle<Object> > old_values;
+  Handle<Object> old_length_handle(array->length(), isolate);
+  uint32_t old_length;
+  CHECK(old_length_handle->ToArrayIndex(&old_length));
+  uint32_t new_length;
+  CHECK(new_length_handle->ToArrayIndex(&new_length));
+  uint32_t end_index = old_length;
+  while (end_index && end_index-- > new_length) {
+    ElementsAccessor* accessor = array->GetElementsAccessor();
+    if (!accessor->HasElement(*array, *array, end_index))
+      continue;
+    indices.Add(isolate->factory()->NewNumberFromUint(end_index));
+    if (array->HasDictionaryElements()) {
+      // Slow case: need to make sure the element isn't a callback.
+      SeededNumberDictionary* dictionary = array->element_dictionary();
+      int entry = dictionary->FindEntry(end_index);
+      if (entry == SeededNumberDictionary::kNotFound)
+        continue;
+ // TODO: Since we have the details available here, we could short-circuit
+      // the whole operation if we notice the element is non-configurable.
+      PropertyDetails details = dictionary->DetailsAt(entry);
+      if (details.type() == CALLBACKS) {
+        old_values.Add(isolate->factory()->the_hole_value());
+        continue;
+      }
+    }
+    Object* element;
+    // TODO: Can this actually fail?
+    { MaybeObject* maybe_object = accessor->Get(*array, *array, end_index);
+      if (!maybe_object->ToObject(&element)) return maybe_object;
+    }
+    old_values.Add(Handle<Object>(element, isolate));
+  }
+
+  MaybeObject* result = array->SetElementsLength(*new_length_handle);
+  if (result->IsFailure()) return result;
+
+  Handle<Object> result_handle(result->ToObjectUnchecked(), isolate);
+  CHECK(array->length()->ToArrayIndex(&new_length));
+  if (old_length != new_length) {
+    if (old_length > new_length) {
+      ASSERT(old_length - new_length
+             <= static_cast<uint32_t>(indices.length()));
+      for (uint32_t i = 0; i < (old_length - new_length); ++i) {
+        ObjectObservation::EnqueueChangeRecord(
+            isolate, array, isolate->factory()->deleted_symbol(),
+            indices[i], old_values[i]);
+      }
+    }
+    ObjectObservation::EnqueueChangeRecord(
+        isolate, array, isolate->factory()->updated_symbol(),
+        isolate->factory()->length_symbol(), old_length_handle);
+  }
+  return *result_handle;
+}
+
+
MaybeObject* Accessors::ArraySetLength(JSObject* object, Object* value, void*) {
   Isolate* isolate = object->GetIsolate();

@@ -112,7 +174,7 @@ MaybeObject* Accessors::ArraySetLength(JSObject* object, Object* value, void*) {
   HandleScope scope(isolate);

   // Protect raw pointers.
-  Handle<JSObject> object_handle(object, isolate);
+  Handle<JSArray> array_handle(JSArray::cast(object), isolate);
   Handle<Object> value_handle(value, isolate);

   bool has_exception;
@@ -122,7 +184,9 @@ MaybeObject* Accessors::ArraySetLength(JSObject* object, Object* value, void*) {
   if (has_exception) return Failure::Exception();

   if (uint32_v->Number() == number_v->Number()) {
- return Handle<JSArray>::cast(object_handle)->SetElementsLength(*uint32_v);
+    return FLAG_harmony_object_observe
+        ? ArraySetLengthObserved(isolate, array_handle, uint32_v)
+        : array_handle->SetElementsLength(*uint32_v);
   }
   return isolate->Throw(
       *isolate->factory()->NewRangeError("invalid_array_length",
Index: src/heap.h
diff --git a/src/heap.h b/src/heap.h
index 179cff8b10f9f54b7a5efbcfbf603f2a5d32eec3..f6e5b04e05c3d1d0f6c6242f6fe3955639dcc390 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -251,7 +251,9 @@ namespace internal {
   V(infinity_symbol, "Infinity")                                         \
   V(minus_infinity_symbol, "-Infinity")                                  \
   V(hidden_stack_trace_symbol, "v8::hidden_stack_trace")                 \
-  V(query_colon_symbol, "(?:)")
+  V(query_colon_symbol, "(?:)")                                          \
+  V(updated_symbol, "updated")                                           \
+  V(deleted_symbol, "deleted")

 // Forward declarations.
 class GCTracer;
Index: src/object-observe.cc
diff --git a/src/object-observe.cc b/src/object-observe.cc
index 83c1f92bb76716398151608cb34525284046de4e..49e27a85c8d80b3eba231d439dfa8ece8219f215 100644
--- a/src/object-observe.cc
+++ b/src/object-observe.cc
@@ -53,6 +53,11 @@ void ObjectObservation::EnqueueChangeRecord(Isolate* isolate,
                                             Handle<Object> oldValue) {
   bool threw = false;

+  // TODO: Actually pass something to JS to indicate that there
+  // is no old value
+  if (oldValue->IsTheHole())
+    oldValue = isolate->factory()->undefined_value();
+
   Handle<Object> args[] = {
     object, type, name, oldValue
   };


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

Reply via email to