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