Revision: 12917
Author:   [email protected]
Date:     Fri Nov  9 04:28:22 2012
Log:      Minimal implementation and tests of observable array methods

Bail out of any special-casing in array methods.
Further optimization is possible, but can be left for later.

Review URL: https://codereview.chromium.org/11369151
Patch from Adam Klein <[email protected]>.
http://code.google.com/p/v8/source/detail?r=12917

Modified:
 /branches/bleeding_edge/src/array.js
 /branches/bleeding_edge/src/builtins.cc
 /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js

=======================================
--- /branches/bleeding_edge/src/array.js        Thu Oct 25 07:53:26 2012
+++ /branches/bleeding_edge/src/array.js        Fri Nov  9 04:28:22 2012
@@ -441,8 +441,8 @@
   }
   n--;
   var value = this[n];
+  delete this[n];
   this.length = n;
-  delete this[n];
   return value;
 }

@@ -581,7 +581,7 @@

   var first = this[0];

-  if (IS_ARRAY(this)) {
+  if (IS_ARRAY(this) && !%IsObserved(this)) {
     SmartMove(this, 0, 1, len, 0);
   } else {
     SimpleMove(this, 0, 1, len, 0);
@@ -602,7 +602,7 @@
   var len = TO_UINT32(this.length);
   var num_arguments = %_ArgumentsLength();

-  if (IS_ARRAY(this)) {
+  if (IS_ARRAY(this) && !%IsObserved(this)) {
     SmartMove(this, 0, 0, len, num_arguments);
   } else {
     SimpleMove(this, 0, 0, len, num_arguments);
@@ -649,6 +649,7 @@
   if (end_i < start_i) return result;

   if (IS_ARRAY(this) &&
+      !%IsObserved(this) &&
       (end_i > 1000) &&
       (%EstimateNumberOfElements(this) < end_i)) {
     SmartSlice(this, start_i, end_i - start_i, len, result);
@@ -705,7 +706,9 @@

   var use_simple_splice = true;

-  if (IS_ARRAY(this) && num_additional_args !== del_count) {
+  if (IS_ARRAY(this) &&
+      !%IsObserved(this) &&
+      num_additional_args !== del_count) {
     // If we are only deleting/moving a few things near the end of the
     // array then the simple version is going to be faster, because it
     // doesn't touch most of the array.
=======================================
--- /branches/bleeding_edge/src/builtins.cc     Tue Sep 25 06:35:42 2012
+++ /branches/bleeding_edge/src/builtins.cc     Fri Nov  9 04:28:22 2012
@@ -510,6 +510,10 @@
   FixedArray* elms = FixedArray::cast(elms_obj);
   JSArray* array = JSArray::cast(receiver);

+  if (FLAG_harmony_observation && array->map()->is_observed()) {
+    return CallJsBuiltin(isolate, "ArrayPush", args);
+  }
+
   int len = Smi::cast(array->length())->value();
   int to_add = args.length() - 1;
   if (to_add == 0) {
@@ -566,11 +570,15 @@
   FixedArray* elms = FixedArray::cast(elms_obj);
   JSArray* array = JSArray::cast(receiver);

+  if (FLAG_harmony_observation && array->map()->is_observed()) {
+    return CallJsBuiltin(isolate, "ArrayPop", args);
+  }
+
   int len = Smi::cast(array->length())->value();
   if (len == 0) return heap->undefined_value();

   // Get top element
-  MaybeObject* top = elms->get(len - 1);
+  Object* top = elms->get(len - 1);

   // Set the length.
   array->set_length(Smi::FromInt(len - 1));
@@ -581,9 +589,7 @@
     return top;
   }

-  top = array->GetPrototype()->GetElement(len - 1);
-
-  return top;
+  return array->GetPrototype()->GetElement(len - 1);
 }


@@ -604,6 +610,10 @@
   JSArray* array = JSArray::cast(receiver);
   ASSERT(array->HasFastSmiOrObjectElements());

+  if (FLAG_harmony_observation && array->map()->is_observed()) {
+    return CallJsBuiltin(isolate, "ArrayShift", args);
+  }
+
   int len = Smi::cast(array->length())->value();
   if (len == 0) return heap->undefined_value();

@@ -646,6 +656,10 @@
   JSArray* array = JSArray::cast(receiver);
   ASSERT(array->HasFastSmiOrObjectElements());

+  if (FLAG_harmony_observation && array->map()->is_observed()) {
+    return CallJsBuiltin(isolate, "ArrayUnshift", args);
+  }
+
   int len = Smi::cast(array->length())->value();
   int to_add = args.length() - 1;
   int new_length = len + to_add;
@@ -802,6 +816,10 @@
   JSArray* array = JSArray::cast(receiver);
   ASSERT(array->HasFastSmiOrObjectElements());

+  if (FLAG_harmony_observation && array->map()->is_observed()) {
+    return CallJsBuiltin(isolate, "ArraySplice", args);
+  }
+
   int len = Smi::cast(array->length())->value();

   int n_arguments = args.length() - 1;
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Fri Nov 9 03:00:13 2012 +++ /branches/bleeding_edge/test/mjsunit/harmony/object-observe.js Fri Nov 9 04:28:22 2012
@@ -359,7 +359,6 @@
 Object.defineProperty(arr3, 'length', {value: 5});
 Object.defineProperty(arr3, 'length', {value: 10, writable: false});
 Object.deliverChangeRecords(observer.callback);
-observer.records.forEach(function(r){print(JSON.stringify(r))});
 observer.assertCallbackRecords([
   { object: arr, name: '3', type: 'deleted', oldValue: 'd' },
   // TODO(adamk): oldValue should not be present below
@@ -431,3 +430,147 @@
   { object: arr, name: 'length', type: 'updated', oldValue: 201 },
   { object: arr, name: '50', type: 'new' },
 ]);
+
+// Tests for array methods, first on arrays and then on plain objects
+//
+// === ARRAYS ===
+//
+// Push
+reset();
+var array = [1, 2];
+Object.observe(array, observer.callback);
+array.push(3, 4);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '2', type: 'new' },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+  { object: array, name: '3', type: 'new' },
+  { object: array, name: 'length', type: 'updated', oldValue: 3 },
+]);
+
+// Pop
+reset();
+var array = [1, 2];
+Object.observe(array, observer.callback);
+array.pop();
+array.pop();
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '1', type: 'deleted', oldValue: 2 },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+  { object: array, name: '0', type: 'deleted', oldValue: 1 },
+  { object: array, name: 'length', type: 'updated', oldValue: 1 },
+]);
+
+// Shift
+reset();
+var array = [1, 2];
+Object.observe(array, observer.callback);
+array.shift();
+array.shift();
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '0', type: 'updated', oldValue: 1 },
+  { object: array, name: '1', type: 'deleted', oldValue: 2 },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+  { object: array, name: '0', type: 'deleted', oldValue: 2 },
+  { object: array, name: 'length', type: 'updated', oldValue: 1 },
+]);
+
+// Unshift
+reset();
+var array = [1, 2];
+Object.observe(array, observer.callback);
+array.unshift(3, 4);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '3', type: 'new' },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+  { object: array, name: '2', type: 'new' },
+  { object: array, name: '0', type: 'updated', oldValue: 1 },
+  { object: array, name: '1', type: 'updated', oldValue: 2 },
+]);
+
+// Splice
+reset();
+var array = [1, 2, 3];
+Object.observe(array, observer.callback);
+array.splice(1, 1, 4, 5);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '3', type: 'new' },
+  { object: array, name: 'length', type: 'updated', oldValue: 3 },
+  { object: array, name: '1', type: 'updated', oldValue: 2 },
+  { object: array, name: '2', type: 'updated', oldValue: 3 },
+]);
+
+//
+// === PLAIN OBJECTS ===
+//
+// Push
+reset()
+var array = {0: 1, 1: 2, length: 2}
+Object.observe(array, observer.callback);
+Array.prototype.push.call(array, 3, 4);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '2', type: 'new' },
+  { object: array, name: '3', type: 'new' },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+]);
+
+// Pop
+reset()
+var array = {0: 1, 1: 2, length: 2};
+Object.observe(array, observer.callback);
+Array.prototype.pop.call(array);
+Array.prototype.pop.call(array);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '1', type: 'deleted', oldValue: 2 },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+  { object: array, name: '0', type: 'deleted', oldValue: 1 },
+  { object: array, name: 'length', type: 'updated', oldValue: 1 },
+]);
+
+// Shift
+reset()
+var array = {0: 1, 1: 2, length: 2};
+Object.observe(array, observer.callback);
+Array.prototype.shift.call(array);
+Array.prototype.shift.call(array);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '0', type: 'updated', oldValue: 1 },
+  { object: array, name: '1', type: 'deleted', oldValue: 2 },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+  { object: array, name: '0', type: 'deleted', oldValue: 2 },
+  { object: array, name: 'length', type: 'updated', oldValue: 1 },
+]);
+
+// Unshift
+reset()
+var array = {0: 1, 1: 2, length: 2};
+Object.observe(array, observer.callback);
+Array.prototype.unshift.call(array, 3, 4);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '3', type: 'new' },
+  { object: array, name: '2', type: 'new' },
+  { object: array, name: '0', type: 'updated', oldValue: 1 },
+  { object: array, name: '1', type: 'updated', oldValue: 2 },
+  { object: array, name: 'length', type: 'updated', oldValue: 2 },
+]);
+
+// Splice
+reset()
+var array = {0: 1, 1: 2, 2: 3, length: 3};
+Object.observe(array, observer.callback);
+Array.prototype.splice.call(array, 1, 1, 4, 5);
+Object.deliverChangeRecords(observer.callback);
+observer.assertCallbackRecords([
+  { object: array, name: '3', type: 'new' },
+  { object: array, name: '1', type: 'updated', oldValue: 2 },
+  { object: array, name: '2', type: 'updated', oldValue: 3 },
+  { object: array, name: 'length', type: 'updated', oldValue: 3 },
+]);

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

Reply via email to