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
This commit is contained in:
parent
c3dde4bd9d
commit
8ce0718763
23
src/array.js
23
src/array.js
@ -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
|
||||
// length of the array. See ECMA-262, section 15.4.4.7.
|
||||
function ArrayPush() {
|
||||
@ -424,6 +444,9 @@ function ArrayPush() {
|
||||
["Array.prototype.push"]);
|
||||
}
|
||||
|
||||
if (%IsObserved(this))
|
||||
return ObservedArrayPush.apply(this, arguments);
|
||||
|
||||
var n = TO_UINT32(this.length);
|
||||
var m = %_ArgumentsLength();
|
||||
for (var i = 0; i < m; i++) {
|
||||
|
@ -166,7 +166,7 @@ function EndPerformChange(objectInfo, type) {
|
||||
objectInfo);
|
||||
}
|
||||
|
||||
function ensureObserverRemoved(objectInfo, callback) {
|
||||
function EnsureObserverRemoved(objectInfo, callback) {
|
||||
function remove(observerList) {
|
||||
for (var i = 0; i < observerList.length; i++) {
|
||||
if (observerList[i].callback === callback) {
|
||||
@ -219,7 +219,7 @@ function ObjectObserve(object, callback, accept) {
|
||||
if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object);
|
||||
%SetIsObserved(object, true);
|
||||
|
||||
ensureObserverRemoved(objectInfo, callback);
|
||||
EnsureObserverRemoved(objectInfo, callback);
|
||||
|
||||
var observer = CreateObserver(callback, accept);
|
||||
if (ObserverIsActive(observer, objectInfo))
|
||||
@ -240,7 +240,7 @@ function ObjectUnobserve(object, callback) {
|
||||
if (IS_UNDEFINED(objectInfo))
|
||||
return object;
|
||||
|
||||
ensureObserverRemoved(objectInfo, callback);
|
||||
EnsureObserverRemoved(objectInfo, callback);
|
||||
|
||||
if (objectInfo.changeObservers.length === 0 &&
|
||||
objectInfo.inactiveObservers.length === 0) {
|
||||
@ -250,6 +250,17 @@ function ObjectUnobserve(object, callback) {
|
||||
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) {
|
||||
// TODO(rossberg): adjust once there is a story for symbols vs proxies.
|
||||
if (IS_SYMBOL(changeRecord.name)) return;
|
||||
@ -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) {
|
||||
var objectInfo = objectInfoMap.get(object);
|
||||
if (objectInfo.changeObservers.length === 0)
|
||||
@ -405,6 +449,10 @@ function SetupObjectObserve() {
|
||||
"observe", ObjectObserve,
|
||||
"unobserve", ObjectUnobserve
|
||||
));
|
||||
InstallFunctions($Array, DONT_ENUM, $Array(
|
||||
"observe", ArrayObserve,
|
||||
"unobserve", ArrayUnobserve
|
||||
));
|
||||
InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
|
||||
"notify", ObjectNotifierNotify,
|
||||
"performChange", ObjectNotifierPerformChange
|
||||
|
@ -1068,13 +1068,22 @@ observer.assertCallbackRecords([
|
||||
reset();
|
||||
var array = [1, 2];
|
||||
Object.observe(array, observer.callback);
|
||||
Array.observe(array, observer2.callback);
|
||||
array.push(3, 4);
|
||||
array.push(5);
|
||||
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 },
|
||||
{ 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
|
||||
|
Loading…
Reference in New Issue
Block a user