Commit 8ce07187 authored by rossberg@chromium.org's avatar rossberg@chromium.org

Implement Array.observe and emit splice change records for ArrayPush

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

Patch from Rafael Weinstein <rafaelw@chromium.org>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14705 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent c3dde4bd
...@@ -416,6 +416,26 @@ function ArrayPop() { ...@@ -416,6 +416,26 @@ function ArrayPop() {
} }
function ObservedArrayPush() {
var n = TO_UINT32(this.length);
var m = %_ArgumentsLength();
EnqueueSpliceRecord(this, n, [], 0, m);
try {
BeginPerformSplice(this);
for (var i = 0; i < m; i++) {
this[i+n] = %_Arguments(i);
}
this.length = n + m;
} finally {
EndPerformSplice(this);
}
return this.length;
}
// Appends the arguments to the end of the array and returns the new // Appends the arguments to the end of the array and returns the new
// length of the array. See ECMA-262, section 15.4.4.7. // length of the array. See ECMA-262, section 15.4.4.7.
function ArrayPush() { function ArrayPush() {
...@@ -424,6 +444,9 @@ function ArrayPush() { ...@@ -424,6 +444,9 @@ function ArrayPush() {
["Array.prototype.push"]); ["Array.prototype.push"]);
} }
if (%IsObserved(this))
return ObservedArrayPush.apply(this, arguments);
var n = TO_UINT32(this.length); var n = TO_UINT32(this.length);
var m = %_ArgumentsLength(); var m = %_ArgumentsLength();
for (var i = 0; i < m; i++) { for (var i = 0; i < m; i++) {
......
...@@ -166,7 +166,7 @@ function EndPerformChange(objectInfo, type) { ...@@ -166,7 +166,7 @@ function EndPerformChange(objectInfo, type) {
objectInfo); objectInfo);
} }
function ensureObserverRemoved(objectInfo, callback) { function EnsureObserverRemoved(objectInfo, callback) {
function remove(observerList) { function remove(observerList) {
for (var i = 0; i < observerList.length; i++) { for (var i = 0; i < observerList.length; i++) {
if (observerList[i].callback === callback) { if (observerList[i].callback === callback) {
...@@ -219,7 +219,7 @@ function ObjectObserve(object, callback, accept) { ...@@ -219,7 +219,7 @@ function ObjectObserve(object, callback, accept) {
if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object); if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object);
%SetIsObserved(object, true); %SetIsObserved(object, true);
ensureObserverRemoved(objectInfo, callback); EnsureObserverRemoved(objectInfo, callback);
var observer = CreateObserver(callback, accept); var observer = CreateObserver(callback, accept);
if (ObserverIsActive(observer, objectInfo)) if (ObserverIsActive(observer, objectInfo))
...@@ -240,7 +240,7 @@ function ObjectUnobserve(object, callback) { ...@@ -240,7 +240,7 @@ function ObjectUnobserve(object, callback) {
if (IS_UNDEFINED(objectInfo)) if (IS_UNDEFINED(objectInfo))
return object; return object;
ensureObserverRemoved(objectInfo, callback); EnsureObserverRemoved(objectInfo, callback);
if (objectInfo.changeObservers.length === 0 && if (objectInfo.changeObservers.length === 0 &&
objectInfo.inactiveObservers.length === 0) { objectInfo.inactiveObservers.length === 0) {
...@@ -250,6 +250,17 @@ function ObjectUnobserve(object, callback) { ...@@ -250,6 +250,17 @@ function ObjectUnobserve(object, callback) {
return object; return object;
} }
function ArrayObserve(object, callback) {
return ObjectObserve(object, callback, ['new',
'updated',
'deleted',
'splice']);
}
function ArrayUnobserve(object, callback) {
return ObjectUnobserve(object, callback);
}
function EnqueueChangeRecord(changeRecord, observers) { function EnqueueChangeRecord(changeRecord, observers) {
// TODO(rossberg): adjust once there is a story for symbols vs proxies. // TODO(rossberg): adjust once there is a story for symbols vs proxies.
if (IS_SYMBOL(changeRecord.name)) return; if (IS_SYMBOL(changeRecord.name)) return;
...@@ -271,6 +282,39 @@ function EnqueueChangeRecord(changeRecord, observers) { ...@@ -271,6 +282,39 @@ function EnqueueChangeRecord(changeRecord, observers) {
} }
} }
function BeginPerformSplice(array) {
var objectInfo = objectInfoMap.get(array);
if (!IS_UNDEFINED(objectInfo))
BeginPerformChange(objectInfo, 'splice');
}
function EndPerformSplice(array) {
var objectInfo = objectInfoMap.get(array);
if (!IS_UNDEFINED(objectInfo))
EndPerformChange(objectInfo, 'splice');
}
function EnqueueSpliceRecord(array, index, removed, deleteCount, addedCount) {
var objectInfo = objectInfoMap.get(array);
if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
return;
var changeRecord = {
type: 'splice',
object: array,
index: index,
removed: removed,
addedCount: addedCount
};
changeRecord.removed.length = deleteCount;
// TODO(rafaelw): This breaks spec-compliance. Re-enable when freezing isn't
// slow.
// ObjectFreeze(changeRecord);
// ObjectFreeze(changeRecord.removed);
EnqueueChangeRecord(changeRecord, objectInfo.changeObservers);
}
function NotifyChange(type, object, name, oldValue) { function NotifyChange(type, object, name, oldValue) {
var objectInfo = objectInfoMap.get(object); var objectInfo = objectInfoMap.get(object);
if (objectInfo.changeObservers.length === 0) if (objectInfo.changeObservers.length === 0)
...@@ -405,6 +449,10 @@ function SetupObjectObserve() { ...@@ -405,6 +449,10 @@ function SetupObjectObserve() {
"observe", ObjectObserve, "observe", ObjectObserve,
"unobserve", ObjectUnobserve "unobserve", ObjectUnobserve
)); ));
InstallFunctions($Array, DONT_ENUM, $Array(
"observe", ArrayObserve,
"unobserve", ArrayUnobserve
));
InstallFunctions(notifierPrototype, DONT_ENUM, $Array( InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
"notify", ObjectNotifierNotify, "notify", ObjectNotifierNotify,
"performChange", ObjectNotifierPerformChange "performChange", ObjectNotifierPerformChange
......
...@@ -1068,13 +1068,22 @@ observer.assertCallbackRecords([ ...@@ -1068,13 +1068,22 @@ observer.assertCallbackRecords([
reset(); reset();
var array = [1, 2]; var array = [1, 2];
Object.observe(array, observer.callback); Object.observe(array, observer.callback);
Array.observe(array, observer2.callback);
array.push(3, 4); array.push(3, 4);
array.push(5);
Object.deliverChangeRecords(observer.callback); Object.deliverChangeRecords(observer.callback);
observer.assertCallbackRecords([ observer.assertCallbackRecords([
{ object: array, name: '2', type: 'new' }, { object: array, name: '2', type: 'new' },
{ object: array, name: 'length', type: 'updated', oldValue: 2 }, { object: array, name: 'length', type: 'updated', oldValue: 2 },
{ object: array, name: '3', type: 'new' }, { object: array, name: '3', type: 'new' },
{ object: array, name: 'length', type: 'updated', oldValue: 3 }, { object: array, name: 'length', type: 'updated', oldValue: 3 },
{ object: array, name: '4', type: 'new' },
{ object: array, name: 'length', type: 'updated', oldValue: 4 },
]);
Object.deliverChangeRecords(observer2.callback);
observer2.assertCallbackRecords([
{ object: array, type: 'splice', index: 2, removed: [], addedCount: 2 },
{ object: array, type: 'splice', index: 4, removed: [], addedCount: 1 }
]); ]);
// Pop // Pop
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment