1
// Copyright 2012 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
6
// * Redistributions of source code must retain the above copyright
7
// notice, this list of conditions and the following disclaimer.
8
// * Redistributions in binary form must reproduce the above
9
// copyright notice, this list of conditions and the following
10
// disclaimer in the documentation and/or other materials provided
11
// with the distribution.
12
// * Neither the name of Google Inc. nor the names of its
13
// contributors may be used to endorse or promote products derived
14
// from this software without specific prior written permission.
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
// Flags: --harmony-proxies
29
// Flags: --allow-natives-syntax
31
var allObservers = [];
33
allObservers.forEach(function(observer) { observer.reset(); });
36
function stringifyNoThrow(arg) {
38
return JSON.stringify(arg);
40
return '{<circular reference>}';
44
function createObserver() {
45
"use strict"; // So that |this| in callback can be undefined.
51
this.records = undefined;
52
this.callbackCount = 0;
54
assertNotCalled: function() {
55
assertEquals(undefined, this.records);
56
assertEquals(0, this.callbackCount);
58
assertCalled: function() {
59
assertEquals(1, this.callbackCount);
61
assertRecordCount: function(count) {
63
assertEquals(count, this.records.length);
65
assertCallbackRecords: function(recs) {
66
this.assertRecordCount(recs.length);
67
for (var i = 0; i < recs.length; i++) {
68
if ('name' in recs[i]) recs[i].name = String(recs[i].name);
69
print(i, stringifyNoThrow(this.records[i]), stringifyNoThrow(recs[i]));
70
assertSame(this.records[i].object, recs[i].object);
71
assertEquals('string', typeof recs[i].type);
72
assertPropertiesEqual(this.records[i], recs[i]);
77
observer.callback = function(r) {
78
assertEquals(undefined, this);
79
assertEquals('object', typeof r);
80
assertTrue(r instanceof Array)
82
observer.callbackCount++;
86
allObservers.push(observer);
90
var observer = createObserver();
91
var observer2 = createObserver();
93
assertEquals("function", typeof observer.callback);
94
assertEquals("function", typeof observer2.callback);
98
function frozenFunction() {}
99
Object.freeze(frozenFunction);
100
var nonFunction = {};
101
var changeRecordWithAccessor = { type: 'foo' };
102
var recordCreated = false;
103
Object.defineProperty(changeRecordWithAccessor, 'name', {
105
recordCreated = true;
113
assertThrows(function() { Object.observe("non-object", observer.callback); },
115
assertThrows(function() { Object.observe(this, observer.callback); },
117
assertThrows(function() { Object.observe(obj, nonFunction); }, TypeError);
118
assertThrows(function() { Object.observe(obj, frozenFunction); }, TypeError);
119
assertEquals(obj, Object.observe(obj, observer.callback, [1]));
120
assertEquals(obj, Object.observe(obj, observer.callback, [true]));
121
assertEquals(obj, Object.observe(obj, observer.callback, ['foo', null]));
122
assertEquals(obj, Object.observe(obj, observer.callback, [undefined]));
123
assertEquals(obj, Object.observe(obj, observer.callback,
124
['foo', 'bar', 'baz']));
125
assertEquals(obj, Object.observe(obj, observer.callback, []));
126
assertEquals(obj, Object.observe(obj, observer.callback, undefined));
127
assertEquals(obj, Object.observe(obj, observer.callback));
130
assertThrows(function() { Object.unobserve(4, observer.callback); }, TypeError);
131
assertThrows(function() { Object.unobserve(this, observer.callback); },
133
assertThrows(function() { Object.unobserve(obj, nonFunction); }, TypeError);
134
assertEquals(obj, Object.unobserve(obj, observer.callback));
137
// Object.getNotifier
138
var notifier = Object.getNotifier(obj);
139
assertSame(notifier, Object.getNotifier(obj));
140
assertEquals(null, Object.getNotifier(Object.freeze({})));
141
assertThrows(function() { Object.getNotifier(this) }, TypeError);
142
assertFalse(notifier.hasOwnProperty('notify'));
143
assertEquals([], Object.keys(notifier));
144
var notifyDesc = Object.getOwnPropertyDescriptor(notifier.__proto__, 'notify');
145
assertTrue(notifyDesc.configurable);
146
assertTrue(notifyDesc.writable);
147
assertFalse(notifyDesc.enumerable);
148
assertThrows(function() { notifier.notify({}); }, TypeError);
149
assertThrows(function() { notifier.notify({ type: 4 }); }, TypeError);
151
assertThrows(function() { notifier.performChange(1, function(){}); }, TypeError);
152
assertThrows(function() { notifier.performChange(undefined, function(){}); }, TypeError);
153
assertThrows(function() { notifier.performChange('foo', undefined); }, TypeError);
154
assertThrows(function() { notifier.performChange('foo', 'bar'); }, TypeError);
156
notifier.performChange('foo', function() {
157
assertEquals(global, this);
160
var notify = notifier.notify;
161
assertThrows(function() { notify.call(undefined, { type: 'a' }); }, TypeError);
162
assertThrows(function() { notify.call(null, { type: 'a' }); }, TypeError);
163
assertThrows(function() { notify.call(5, { type: 'a' }); }, TypeError);
164
assertThrows(function() { notify.call('hello', { type: 'a' }); }, TypeError);
165
assertThrows(function() { notify.call(false, { type: 'a' }); }, TypeError);
166
assertThrows(function() { notify.call({}, { type: 'a' }); }, TypeError);
167
assertFalse(recordCreated);
168
notifier.notify(changeRecordWithAccessor);
169
assertFalse(recordCreated); // not observed yet
172
// Object.deliverChangeRecords
173
assertThrows(function() { Object.deliverChangeRecords(nonFunction); }, TypeError);
175
Object.observe(obj, observer.callback);
178
// notify uses to [[CreateOwnProperty]] to create changeRecord;
180
var protoExpandoAccessed = false;
181
Object.defineProperty(Object.prototype, 'protoExpando',
184
set: function() { protoExpandoAccessed = true; }
187
notifier.notify({ type: 'foo', protoExpando: 'val'});
188
assertFalse(protoExpandoAccessed);
189
delete Object.prototype.protoExpando;
190
Object.deliverChangeRecords(observer.callback);
193
// Multiple records are delivered.
202
object: notifier, // object property is ignored
207
Object.deliverChangeRecords(observer.callback);
208
observer.assertCallbackRecords([
209
{ object: obj, name: 'foo', type: 'update', expando: 1 },
210
{ object: obj, name: 'bar', type: 'delete', expando2: 'str' }
213
// Non-string accept values are coerced to strings
215
Object.observe(obj, observer.callback, [true, 1, null, undefined]);
216
notifier = Object.getNotifier(obj);
217
notifier.notify({ type: 'true' });
218
notifier.notify({ type: 'false' });
219
notifier.notify({ type: '1' });
220
notifier.notify({ type: '-1' });
221
notifier.notify({ type: 'null' });
222
notifier.notify({ type: 'nill' });
223
notifier.notify({ type: 'undefined' });
224
notifier.notify({ type: 'defined' });
225
Object.deliverChangeRecords(observer.callback);
226
observer.assertCallbackRecords([
227
{ object: obj, type: 'true' },
228
{ object: obj, type: '1' },
229
{ object: obj, type: 'null' },
230
{ object: obj, type: 'undefined' }
233
// No delivery takes place if no records are pending
235
Object.deliverChangeRecords(observer.callback);
236
observer.assertNotCalled();
239
// Multiple observation has no effect.
241
Object.observe(obj, observer.callback);
242
Object.observe(obj, observer.callback);
243
Object.getNotifier(obj).notify({
246
Object.deliverChangeRecords(observer.callback);
247
observer.assertCalled();
250
// Observation can be stopped.
252
Object.unobserve(obj, observer.callback);
253
Object.getNotifier(obj).notify({
256
Object.deliverChangeRecords(observer.callback);
257
observer.assertNotCalled();
260
// Multiple unobservation has no effect
262
Object.unobserve(obj, observer.callback);
263
Object.unobserve(obj, observer.callback);
264
Object.getNotifier(obj).notify({
267
Object.deliverChangeRecords(observer.callback);
268
observer.assertNotCalled();
271
// Re-observation works and only includes changeRecords after of call.
273
Object.getNotifier(obj).notify({
276
Object.observe(obj, observer.callback);
277
Object.getNotifier(obj).notify({
281
Object.deliverChangeRecords(observer.callback);
282
observer.assertRecordCount(1);
284
// Get notifier prior to observing
287
Object.getNotifier(obj);
288
Object.observe(obj, observer.callback);
290
Object.deliverChangeRecords(observer.callback);
291
observer.assertCallbackRecords([
292
{ object: obj, type: 'add', name: 'id' },
295
// The empty-string property is observable
298
Object.observe(obj, observer.callback);
302
Object.deliverChangeRecords(observer.callback);
303
observer.assertCallbackRecords([
304
{ object: obj, type: 'add', name: '' },
305
{ object: obj, type: 'update', name: '', oldValue: '' },
306
{ object: obj, type: 'delete', name: '', oldValue: ' ' },
309
// Object.preventExtensions
311
var obj = { foo: 'bar'};
312
Object.observe(obj, observer.callback);
314
Object.preventExtensions(obj);
316
Object.deliverChangeRecords(observer.callback);
317
observer.assertCallbackRecords([
318
{ object: obj, type: 'add', name: 'baz' },
319
{ object: obj, type: 'preventExtensions' },
323
var obj = { foo: 'bar'};
324
Object.preventExtensions(obj);
325
Object.observe(obj, observer.callback);
326
Object.preventExtensions(obj);
327
Object.deliverChangeRecords(observer.callback);
328
observer.assertNotCalled();
332
var obj = { a: 'a' };
333
Object.defineProperty(obj, 'b', {
338
Object.defineProperty(obj, 'c', {
343
Object.defineProperty(obj, 'd', {
348
Object.observe(obj, observer.callback);
351
Object.deliverChangeRecords(observer.callback);
352
observer.assertCallbackRecords([
353
{ object: obj, type: 'reconfigure', name: 'a' },
354
{ object: obj, type: 'reconfigure', name: 'b' },
355
{ object: obj, type: 'reconfigure', name: 'c' },
356
{ object: obj, type: 'preventExtensions' },
360
var obj = { foo: 'bar'};
362
Object.observe(obj, observer.callback);
364
Object.deliverChangeRecords(observer.callback);
365
observer.assertNotCalled();
369
var obj = { a: 'a' };
370
Object.defineProperty(obj, 'b', {
375
Object.defineProperty(obj, 'c', {
380
Object.defineProperty(obj, 'd', {
385
Object.observe(obj, observer.callback);
388
Object.deliverChangeRecords(observer.callback);
389
observer.assertCallbackRecords([
390
{ object: obj, type: 'reconfigure', name: 'a' },
391
{ object: obj, type: 'reconfigure', name: 'b' },
392
{ object: obj, type: 'preventExtensions' },
396
var obj = { foo: 'bar'};
398
Object.observe(obj, observer.callback);
400
Object.deliverChangeRecords(observer.callback);
401
observer.assertNotCalled();
403
// Observing a continuous stream of changes, while itermittantly unobserving.
406
Object.observe(obj, observer.callback);
407
Object.getNotifier(obj).notify({
412
Object.unobserve(obj, observer.callback);
413
Object.getNotifier(obj).notify({
418
Object.observe(obj, observer.callback);
419
Object.getNotifier(obj).notify({
424
Object.unobserve(obj, observer.callback);
425
Object.getNotifier(obj).notify({
430
Object.observe(obj, observer.callback);
431
Object.getNotifier(obj).notify({
436
Object.unobserve(obj, observer.callback);
437
Object.deliverChangeRecords(observer.callback);
438
observer.assertCallbackRecords([
439
{ object: obj, type: 'update', val: 1 },
440
{ object: obj, type: 'update', val: 3 },
441
{ object: obj, type: 'update', val: 5 }
446
Object.observe(obj, observer.callback, ['somethingElse']);
447
Object.getNotifier(obj).notify({
450
Object.getNotifier(obj).notify({
453
Object.getNotifier(obj).notify({
456
Object.getNotifier(obj).notify({
459
Object.getNotifier(obj).notify({
462
Object.deliverChangeRecords(observer.callback);
463
observer.assertNotCalled();
466
Object.observe(obj, observer.callback, ['add', 'delete', 'setPrototype']);
467
Object.getNotifier(obj).notify({
470
Object.getNotifier(obj).notify({
473
Object.getNotifier(obj).notify({
476
Object.getNotifier(obj).notify({
479
Object.getNotifier(obj).notify({
482
Object.getNotifier(obj).notify({
485
Object.deliverChangeRecords(observer.callback);
486
observer.assertCallbackRecords([
487
{ object: obj, type: 'add' },
488
{ object: obj, type: 'delete' },
489
{ object: obj, type: 'delete' },
490
{ object: obj, type: 'setPrototype' }
494
Object.observe(obj, observer.callback, ['update', 'foo']);
495
Object.getNotifier(obj).notify({
498
Object.getNotifier(obj).notify({
501
Object.getNotifier(obj).notify({
504
Object.getNotifier(obj).notify({
507
Object.getNotifier(obj).notify({
510
Object.getNotifier(obj).notify({
513
Object.deliverChangeRecords(observer.callback);
514
observer.assertCallbackRecords([
515
{ object: obj, type: 'update' },
516
{ object: obj, type: 'foo' },
517
{ object: obj, type: 'foo' }
521
function Thingy(a, b, c) {
526
Thingy.MULTIPLY = 'multiply';
527
Thingy.INCREMENT = 'increment';
528
Thingy.INCREMENT_AND_MULTIPLY = 'incrementAndMultiply';
531
increment: function(amount) {
532
var notifier = Object.getNotifier(this);
535
notifier.performChange(Thingy.INCREMENT, function() {
541
}; // implicit notify
545
multiply: function(amount) {
546
var notifier = Object.getNotifier(this);
549
notifier.performChange(Thingy.MULTIPLY, function() {
555
}; // implicit notify
559
incrementAndMultiply: function(incAmount, multAmount) {
560
var notifier = Object.getNotifier(this);
563
notifier.performChange(Thingy.INCREMENT_AND_MULTIPLY, function() {
564
self.increment(incAmount);
565
self.multiply(multAmount);
568
incremented: incAmount,
569
multiplied: multAmount
570
}; // implicit notify
575
Thingy.observe = function(thingy, callback) {
576
Object.observe(thingy, callback, [Thingy.INCREMENT,
578
Thingy.INCREMENT_AND_MULTIPLY,
582
Thingy.unobserve = function(thingy, callback) {
583
Object.unobserve(thingy);
586
var thingy = new Thingy(2, 4);
588
Object.observe(thingy, observer.callback);
589
Thingy.observe(thingy, observer2.callback);
590
thingy.increment(3); // { a: 5, b: 7 }
591
thingy.b++; // { a: 5, b: 8 }
592
thingy.multiply(2); // { a: 10, b: 16 }
593
thingy.a++; // { a: 11, b: 16 }
594
thingy.incrementAndMultiply(2, 2); // { a: 26, b: 36 }
596
Object.deliverChangeRecords(observer.callback);
597
Object.deliverChangeRecords(observer2.callback);
598
observer.assertCallbackRecords([
599
{ object: thingy, type: 'update', name: 'a', oldValue: 2 },
600
{ object: thingy, type: 'update', name: 'b', oldValue: 4 },
601
{ object: thingy, type: 'update', name: 'b', oldValue: 7 },
602
{ object: thingy, type: 'update', name: 'a', oldValue: 5 },
603
{ object: thingy, type: 'update', name: 'b', oldValue: 8 },
604
{ object: thingy, type: 'update', name: 'a', oldValue: 10 },
605
{ object: thingy, type: 'update', name: 'a', oldValue: 11 },
606
{ object: thingy, type: 'update', name: 'b', oldValue: 16 },
607
{ object: thingy, type: 'update', name: 'a', oldValue: 13 },
608
{ object: thingy, type: 'update', name: 'b', oldValue: 18 },
610
observer2.assertCallbackRecords([
611
{ object: thingy, type: Thingy.INCREMENT, incremented: 3 },
612
{ object: thingy, type: 'update', name: 'b', oldValue: 7 },
613
{ object: thingy, type: Thingy.MULTIPLY, multiplied: 2 },
614
{ object: thingy, type: 'update', name: 'a', oldValue: 10 },
617
type: Thingy.INCREMENT_AND_MULTIPLY,
623
// ArrayPush cached stub
626
function pushMultiple(arr) {
632
for (var i = 0; i < 5; i++) {
637
for (var i = 0; i < 5; i++) {
640
Object.observe(arr, observer.callback);
642
Object.unobserve(arr, observer.callback);
643
Object.deliverChangeRecords(observer.callback);
644
observer.assertCallbackRecords([
645
{ object: arr, type: 'add', name: '0' },
646
{ object: arr, type: 'update', name: 'length', oldValue: 0 },
647
{ object: arr, type: 'add', name: '1' },
648
{ object: arr, type: 'update', name: 'length', oldValue: 1 },
649
{ object: arr, type: 'add', name: '2' },
650
{ object: arr, type: 'update', name: 'length', oldValue: 2 },
655
// ArrayPop cached stub
658
function popMultiple(arr) {
664
for (var i = 0; i < 5; i++) {
665
var arr = ['a', 'b', 'c'];
669
for (var i = 0; i < 5; i++) {
671
var arr = ['a', 'b', 'c'];
672
Object.observe(arr, observer.callback);
674
Object.unobserve(arr, observer.callback);
675
Object.deliverChangeRecords(observer.callback);
676
observer.assertCallbackRecords([
677
{ object: arr, type: 'delete', name: '2', oldValue: 'c' },
678
{ object: arr, type: 'update', name: 'length', oldValue: 3 },
679
{ object: arr, type: 'delete', name: '1', oldValue: 'b' },
680
{ object: arr, type: 'update', name: 'length', oldValue: 2 },
681
{ object: arr, type: 'delete', name: '0', oldValue: 'a' },
682
{ object: arr, type: 'update', name: 'length', oldValue: 1 },
688
function RecursiveThingy() {}
690
RecursiveThingy.MULTIPLY_FIRST_N = 'multiplyFirstN';
692
RecursiveThingy.prototype = {
693
__proto__: Array.prototype,
695
multiplyFirstN: function(amount, n) {
698
var notifier = Object.getNotifier(this);
700
notifier.performChange(RecursiveThingy.MULTIPLY_FIRST_N, function() {
701
self[n-1] = self[n-1]*amount;
702
self.multiplyFirstN(amount, n-1);
706
type: RecursiveThingy.MULTIPLY_FIRST_N,
713
RecursiveThingy.observe = function(thingy, callback) {
714
Object.observe(thingy, callback, [RecursiveThingy.MULTIPLY_FIRST_N]);
717
RecursiveThingy.unobserve = function(thingy, callback) {
718
Object.unobserve(thingy);
721
var thingy = new RecursiveThingy;
722
thingy.push(1, 2, 3, 4);
724
Object.observe(thingy, observer.callback);
725
RecursiveThingy.observe(thingy, observer2.callback);
726
thingy.multiplyFirstN(2, 3); // [2, 4, 6, 4]
728
Object.deliverChangeRecords(observer.callback);
729
Object.deliverChangeRecords(observer2.callback);
730
observer.assertCallbackRecords([
731
{ object: thingy, type: 'update', name: '2', oldValue: 3 },
732
{ object: thingy, type: 'update', name: '1', oldValue: 2 },
733
{ object: thingy, type: 'update', name: '0', oldValue: 1 }
735
observer2.assertCallbackRecords([
736
{ object: thingy, type: RecursiveThingy.MULTIPLY_FIRST_N, multiplied: 2, n: 3 }
740
function DeckSuit() {
741
this.push('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'A', 'Q', 'K');
744
DeckSuit.SHUFFLE = 'shuffle';
746
DeckSuit.prototype = {
747
__proto__: Array.prototype,
749
shuffle: function() {
750
var notifier = Object.getNotifier(this);
752
notifier.performChange(DeckSuit.SHUFFLE, function() {
754
self.sort(function() { return Math.random()* 2 - 1; });
755
var cut = self.splice(0, 6);
756
Array.prototype.push.apply(self, cut);
758
self.sort(function() { return Math.random()* 2 - 1; });
759
var cut = self.splice(0, 6);
760
Array.prototype.push.apply(self, cut);
762
self.sort(function() { return Math.random()* 2 - 1; });
766
type: DeckSuit.SHUFFLE
771
DeckSuit.observe = function(thingy, callback) {
772
Object.observe(thingy, callback, [DeckSuit.SHUFFLE]);
775
DeckSuit.unobserve = function(thingy, callback) {
776
Object.unobserve(thingy);
779
var deck = new DeckSuit;
781
DeckSuit.observe(deck, observer2.callback);
784
Object.deliverChangeRecords(observer2.callback);
785
observer2.assertCallbackRecords([
786
{ object: deck, type: DeckSuit.SHUFFLE }
789
// Observing multiple objects; records appear in order.
793
Object.observe(obj, observer.callback);
794
Object.observe(obj3, observer.callback);
795
Object.observe(obj2, observer.callback);
796
Object.getNotifier(obj).notify({
799
Object.getNotifier(obj2).notify({
802
Object.getNotifier(obj3).notify({
805
Object.observe(obj3, observer.callback);
806
Object.deliverChangeRecords(observer.callback);
807
observer.assertCallbackRecords([
808
{ object: obj, type: 'add' },
809
{ object: obj2, type: 'update' },
810
{ object: obj3, type: 'delete' }
814
// Recursive observation.
816
var callbackCount = 0;
817
function recursiveObserver(r) {
818
assertEquals(1, r.length);
820
if (r[0].oldValue < 100) ++obj[r[0].name];
822
Object.observe(obj, recursiveObserver);
824
Object.deliverChangeRecords(recursiveObserver);
825
assertEquals(100, callbackCount);
830
function recursiveObserver2(r) {
831
recordCount += r.length;
832
if (r[0].oldValue < 100) {
837
Object.observe(obj1, recursiveObserver2);
838
Object.observe(obj2, recursiveObserver2);
840
Object.deliverChangeRecords(recursiveObserver2);
841
assertEquals(199, recordCount);
844
// Observing named properties.
847
Object.observe(obj, observer.callback);
852
obj.a = 4; // ignored
854
Object.defineProperty(obj, "a", {value: 6});
855
Object.defineProperty(obj, "a", {writable: false});
856
obj.a = 7; // ignored
857
Object.defineProperty(obj, "a", {value: 8});
858
Object.defineProperty(obj, "a", {value: 7, writable: true});
859
Object.defineProperty(obj, "a", {get: function() {}});
860
Object.defineProperty(obj, "a", {get: frozenFunction});
861
Object.defineProperty(obj, "a", {get: frozenFunction}); // ignored
862
Object.defineProperty(obj, "a", {get: frozenFunction, set: frozenFunction});
863
Object.defineProperty(obj, "a", {set: frozenFunction}); // ignored
864
Object.defineProperty(obj, "a", {get: undefined, set: frozenFunction});
867
Object.defineProperty(obj, "a", {get: function() {}, configurable: true});
868
Object.defineProperty(obj, "a", {value: 9, writable: true});
874
Object.defineProperty(obj, "a", {value: 11, configurable: true});
875
Object.deliverChangeRecords(observer.callback);
876
observer.assertCallbackRecords([
877
{ object: obj, name: "a", type: "update", oldValue: 1 },
878
{ object: obj, name: "a", type: "update", oldValue: 2 },
879
{ object: obj, name: "a", type: "delete", oldValue: 3 },
880
{ object: obj, name: "a", type: "add" },
881
{ object: obj, name: "a", type: "update", oldValue: 4 },
882
{ object: obj, name: "a", type: "update", oldValue: 5 },
883
{ object: obj, name: "a", type: "reconfigure" },
884
{ object: obj, name: "a", type: "update", oldValue: 6 },
885
{ object: obj, name: "a", type: "reconfigure", oldValue: 8 },
886
{ object: obj, name: "a", type: "reconfigure", oldValue: 7 },
887
{ object: obj, name: "a", type: "reconfigure" },
888
{ object: obj, name: "a", type: "reconfigure" },
889
{ object: obj, name: "a", type: "reconfigure" },
890
{ object: obj, name: "a", type: "delete" },
891
{ object: obj, name: "a", type: "add" },
892
{ object: obj, name: "a", type: "reconfigure" },
893
{ object: obj, name: "a", type: "update", oldValue: 9 },
894
{ object: obj, name: "a", type: "update", oldValue: 10 },
895
{ object: obj, name: "a", type: "update", oldValue: 11 },
896
{ object: obj, name: "a", type: "update", oldValue: 12 },
897
{ object: obj, name: "a", type: "delete", oldValue: 36 },
898
{ object: obj, name: "a", type: "add" },
902
// Observing indexed properties.
905
Object.observe(obj, observer.callback);
910
obj[1] = 4; // ignored
912
Object.defineProperty(obj, "1", {value: 6});
913
Object.defineProperty(obj, "1", {writable: false});
914
obj[1] = 7; // ignored
915
Object.defineProperty(obj, "1", {value: 8});
916
Object.defineProperty(obj, "1", {value: 7, writable: true});
917
Object.defineProperty(obj, "1", {get: function() {}});
918
Object.defineProperty(obj, "1", {get: frozenFunction});
919
Object.defineProperty(obj, "1", {get: frozenFunction}); // ignored
920
Object.defineProperty(obj, "1", {get: frozenFunction, set: frozenFunction});
921
Object.defineProperty(obj, "1", {set: frozenFunction}); // ignored
922
Object.defineProperty(obj, "1", {get: undefined, set: frozenFunction});
925
Object.defineProperty(obj, "1", {get: function() {}, configurable: true});
926
Object.defineProperty(obj, "1", {value: 9, writable: true});
932
Object.defineProperty(obj, "1", {value: 11, configurable: true});
933
Object.deliverChangeRecords(observer.callback);
934
observer.assertCallbackRecords([
935
{ object: obj, name: "1", type: "update", oldValue: 1 },
936
{ object: obj, name: "1", type: "update", oldValue: 2 },
937
{ object: obj, name: "1", type: "delete", oldValue: 3 },
938
{ object: obj, name: "1", type: "add" },
939
{ object: obj, name: "1", type: "update", oldValue: 4 },
940
{ object: obj, name: "1", type: "update", oldValue: 5 },
941
{ object: obj, name: "1", type: "reconfigure" },
942
{ object: obj, name: "1", type: "update", oldValue: 6 },
943
{ object: obj, name: "1", type: "reconfigure", oldValue: 8 },
944
{ object: obj, name: "1", type: "reconfigure", oldValue: 7 },
945
{ object: obj, name: "1", type: "reconfigure" },
946
{ object: obj, name: "1", type: "reconfigure" },
947
{ object: obj, name: "1", type: "reconfigure" },
948
{ object: obj, name: "1", type: "delete" },
949
{ object: obj, name: "1", type: "add" },
950
{ object: obj, name: "1", type: "reconfigure" },
951
{ object: obj, name: "1", type: "update", oldValue: 9 },
952
{ object: obj, name: "1", type: "update", oldValue: 10 },
953
{ object: obj, name: "1", type: "update", oldValue: 11 },
954
{ object: obj, name: "1", type: "update", oldValue: 12 },
955
{ object: obj, name: "1", type: "delete", oldValue: 36 },
956
{ object: obj, name: "1", type: "add" },
960
// Observing symbol properties (not).
964
var symbol = Symbol("secret");
965
Object.observe(obj, observer.callback);
968
Object.defineProperty(obj, symbol, {get: function() {}, configurable: true});
969
Object.defineProperty(obj, symbol, {value: 6});
970
Object.defineProperty(obj, symbol, {writable: false});
972
Object.defineProperty(obj, symbol, {value: 7});
977
obj.__defineSetter__(symbol, function() {});
978
obj.__defineGetter__(symbol, function() {});
979
Object.deliverChangeRecords(observer.callback);
980
observer.assertNotCalled();
983
// Test all kinds of objects generically.
984
function TestObserveConfigurable(obj, prop) {
986
Object.observe(obj, observer.callback);
987
Object.unobserve(obj, observer.callback);
989
Object.observe(obj, observer.callback);
994
obj[prop] = 4; // ignored
996
Object.defineProperty(obj, prop, {value: 6});
997
Object.defineProperty(obj, prop, {writable: false});
998
obj[prop] = 7; // ignored
999
Object.defineProperty(obj, prop, {value: 8});
1000
Object.defineProperty(obj, prop, {value: 7, writable: true});
1001
Object.defineProperty(obj, prop, {get: function() {}});
1002
Object.defineProperty(obj, prop, {get: frozenFunction});
1003
Object.defineProperty(obj, prop, {get: frozenFunction}); // ignored
1004
Object.defineProperty(obj, prop, {get: frozenFunction, set: frozenFunction});
1005
Object.defineProperty(obj, prop, {set: frozenFunction}); // ignored
1006
Object.defineProperty(obj, prop, {get: undefined, set: frozenFunction});
1007
obj.__defineSetter__(prop, frozenFunction); // ignored
1008
obj.__defineSetter__(prop, function() {});
1009
obj.__defineGetter__(prop, function() {});
1011
delete obj[prop]; // ignored
1012
obj.__defineGetter__(prop, function() {});
1014
Object.defineProperty(obj, prop, {get: function() {}, configurable: true});
1015
Object.defineProperty(obj, prop, {value: 9, writable: true});
1021
Object.defineProperty(obj, prop, {value: 11, configurable: true});
1022
Object.deliverChangeRecords(observer.callback);
1023
observer.assertCallbackRecords([
1024
{ object: obj, name: prop, type: "update", oldValue: 1 },
1025
{ object: obj, name: prop, type: "update", oldValue: 2 },
1026
{ object: obj, name: prop, type: "delete", oldValue: 3 },
1027
{ object: obj, name: prop, type: "add" },
1028
{ object: obj, name: prop, type: "update", oldValue: 4 },
1029
{ object: obj, name: prop, type: "update", oldValue: 5 },
1030
{ object: obj, name: prop, type: "reconfigure" },
1031
{ object: obj, name: prop, type: "update", oldValue: 6 },
1032
{ object: obj, name: prop, type: "reconfigure", oldValue: 8 },
1033
{ object: obj, name: prop, type: "reconfigure", oldValue: 7 },
1034
{ object: obj, name: prop, type: "reconfigure" },
1035
{ object: obj, name: prop, type: "reconfigure" },
1036
{ object: obj, name: prop, type: "reconfigure" },
1037
{ object: obj, name: prop, type: "reconfigure" },
1038
{ object: obj, name: prop, type: "reconfigure" },
1039
{ object: obj, name: prop, type: "delete" },
1040
{ object: obj, name: prop, type: "add" },
1041
{ object: obj, name: prop, type: "delete" },
1042
{ object: obj, name: prop, type: "add" },
1043
{ object: obj, name: prop, type: "reconfigure" },
1044
{ object: obj, name: prop, type: "update", oldValue: 9 },
1045
{ object: obj, name: prop, type: "update", oldValue: 10 },
1046
{ object: obj, name: prop, type: "update", oldValue: 11 },
1047
{ object: obj, name: prop, type: "update", oldValue: 12 },
1048
{ object: obj, name: prop, type: "delete", oldValue: 36 },
1049
{ object: obj, name: prop, type: "add" },
1051
Object.unobserve(obj, observer.callback);
1055
function TestObserveNonConfigurable(obj, prop, desc) {
1057
Object.observe(obj, observer.callback);
1058
Object.unobserve(obj, observer.callback);
1060
Object.observe(obj, observer.callback);
1062
obj[prop] = 4; // ignored
1064
Object.defineProperty(obj, prop, {value: 6});
1065
Object.defineProperty(obj, prop, {value: 6}); // ignored
1066
Object.defineProperty(obj, prop, {value: 7});
1067
Object.defineProperty(obj, prop, {enumerable: desc.enumerable}); // ignored
1068
Object.defineProperty(obj, prop, {writable: false});
1069
obj[prop] = 7; // ignored
1070
Object.deliverChangeRecords(observer.callback);
1071
observer.assertCallbackRecords([
1072
{ object: obj, name: prop, type: "update", oldValue: 1 },
1073
{ object: obj, name: prop, type: "update", oldValue: 4 },
1074
{ object: obj, name: prop, type: "update", oldValue: 5 },
1075
{ object: obj, name: prop, type: "update", oldValue: 6 },
1076
{ object: obj, name: prop, type: "reconfigure" },
1078
Object.unobserve(obj, observer.callback);
1081
// TODO(rafaelw) Enable when ES6 Proxies are implemented
1083
function createProxy(create, x) {
1085
getPropertyDescriptor: function(k) {
1086
for (var o = this.target; o; o = Object.getPrototypeOf(o)) {
1087
var desc = Object.getOwnPropertyDescriptor(o, k);
1088
if (desc) return desc;
1092
getOwnPropertyDescriptor: function(k) {
1093
return Object.getOwnPropertyDescriptor(this.target, k);
1095
defineProperty: function(k, desc) {
1096
var x = Object.defineProperty(this.target, k, desc);
1097
Object.deliverChangeRecords(this.callback);
1100
delete: function(k) {
1101
var x = delete this.target[k];
1102
Object.deliverChangeRecords(this.callback);
1105
getPropertyNames: function() {
1106
return Object.getOwnPropertyNames(this.target);
1108
target: {isProxy: true},
1109
callback: function(changeRecords) {
1110
print("callback", stringifyNoThrow(handler.proxy), stringifyNoThrow(got));
1111
for (var i in changeRecords) {
1112
var got = changeRecords[i];
1113
var change = {object: handler.proxy, name: got.name, type: got.type};
1114
if ("oldValue" in got) change.oldValue = got.oldValue;
1115
Object.getNotifier(handler.proxy).notify(change);
1119
Object.observe(handler.target, handler.callback);
1120
return handler.proxy = create(handler, x);
1128
(function(){ return arguments })(),
1129
(function(){ "use strict"; return arguments })(),
1130
Object(1), Object(true), Object("bla"),
1132
Object, Function, Date, RegExp,
1133
new Set, new Map, new WeakMap,
1134
new ArrayBuffer(10), new Int32Array(5)
1135
// TODO(rafaelw) Enable when ES6 Proxies are implemented.
1136
// createProxy(Proxy.create, null),
1137
// createProxy(Proxy.createFunction, function(){}),
1139
var properties = ["a", "1", 1, "length", "setPrototype", "name", "caller"];
1141
// Cases that yield non-standard results.
1142
function blacklisted(obj, prop) {
1143
return (obj instanceof Int32Array && prop == 1) ||
1144
(obj instanceof Int32Array && prop === "length") ||
1145
(obj instanceof ArrayBuffer && prop == 1) ||
1146
(obj instanceof Function && prop === "name") || // Has its own test.
1147
(obj instanceof Function && prop === "length"); // Has its own test.
1150
for (var i in objects) for (var j in properties) {
1151
var obj = objects[i];
1152
var prop = properties[j];
1153
if (blacklisted(obj, prop)) continue;
1154
var desc = Object.getOwnPropertyDescriptor(obj, prop);
1155
print("***", typeof obj, stringifyNoThrow(obj), prop);
1156
if (!desc || desc.configurable)
1157
TestObserveConfigurable(obj, prop);
1158
else if (desc.writable)
1159
TestObserveNonConfigurable(obj, prop, desc);
1163
// Observing array length (including truncation)
1165
var arr = ['a', 'b', 'c', 'd'];
1166
var arr2 = ['alpha', 'beta'];
1167
var arr3 = ['hello'];
1168
arr3[2] = 'goodbye';
1170
Object.defineProperty(arr, '0', {configurable: false});
1171
Object.defineProperty(arr, '2', {get: function(){}});
1172
Object.defineProperty(arr2, '0', {get: function(){}, configurable: false});
1173
Object.observe(arr, observer.callback);
1174
Array.observe(arr, observer2.callback);
1175
Object.observe(arr2, observer.callback);
1176
Array.observe(arr2, observer2.callback);
1177
Object.observe(arr3, observer.callback);
1178
Array.observe(arr3, observer2.callback);
1182
Object.defineProperty(arr, 'length', {writable: false});
1184
arr2.length = 1; // no change expected
1185
Object.defineProperty(arr2, 'length', {value: 1, writable: false});
1190
Object.defineProperty(arr3, 'length', {value: 5});
1192
Object.defineProperty(arr3, 'length', {value: 1, writable: false});
1193
Object.deliverChangeRecords(observer.callback);
1194
observer.assertCallbackRecords([
1195
{ object: arr, name: '3', type: 'delete', oldValue: 'd' },
1196
{ object: arr, name: '2', type: 'delete' },
1197
{ object: arr, name: 'length', type: 'update', oldValue: 4 },
1198
{ object: arr, name: '1', type: 'delete', oldValue: 'b' },
1199
{ object: arr, name: 'length', type: 'update', oldValue: 2 },
1200
{ object: arr, name: 'length', type: 'update', oldValue: 1 },
1201
{ object: arr, name: 'length', type: 'reconfigure' },
1202
{ object: arr2, name: '1', type: 'delete', oldValue: 'beta' },
1203
{ object: arr2, name: 'length', type: 'update', oldValue: 2 },
1204
{ object: arr2, name: 'length', type: 'reconfigure' },
1205
{ object: arr3, name: '2', type: 'delete', oldValue: 'goodbye' },
1206
{ object: arr3, name: '0', type: 'delete', oldValue: 'hello' },
1207
{ object: arr3, name: 'length', type: 'update', oldValue: 6 },
1208
{ object: arr3, name: 'length', type: 'update', oldValue: 0 },
1209
{ object: arr3, name: 'length', type: 'update', oldValue: 1 },
1210
{ object: arr3, name: 'length', type: 'update', oldValue: 2 },
1211
{ object: arr3, name: 'length', type: 'update', oldValue: 1 },
1212
{ object: arr3, name: '4', type: 'add' },
1213
{ object: arr3, name: '4', type: 'delete', oldValue: 5 },
1214
// TODO(rafaelw): It breaks spec compliance to get two records here.
1215
// When the TODO in v8natives.js::DefineArrayProperty is addressed
1216
// which prevents DefineProperty from over-writing the magic length
1217
// property, these will collapse into a single record.
1218
{ object: arr3, name: 'length', type: 'update', oldValue: 5 },
1219
{ object: arr3, name: 'length', type: 'reconfigure' }
1221
Object.deliverChangeRecords(observer2.callback);
1222
observer2.assertCallbackRecords([
1223
{ object: arr, type: 'splice', index: 2, removed: [, 'd'], addedCount: 0 },
1224
{ object: arr, type: 'splice', index: 1, removed: ['b'], addedCount: 0 },
1225
{ object: arr, type: 'splice', index: 1, removed: [], addedCount: 9 },
1226
{ object: arr2, type: 'splice', index: 1, removed: ['beta'], addedCount: 0 },
1227
{ object: arr3, type: 'splice', index: 0, removed: ['hello',, 'goodbye',,,,], addedCount: 0 },
1228
{ object: arr3, type: 'splice', index: 0, removed: [], addedCount: 1 },
1229
{ object: arr3, type: 'splice', index: 1, removed: [], addedCount: 1 },
1230
{ object: arr3, type: 'splice', index: 1, removed: [,], addedCount: 0 },
1231
{ object: arr3, type: 'splice', index: 1, removed: [], addedCount: 4 },
1232
{ object: arr3, name: '4', type: 'add' },
1233
{ object: arr3, type: 'splice', index: 1, removed: [,,,5], addedCount: 0 }
1237
// Updating length on large (slow) array
1239
var slow_arr = %NormalizeElements([]);
1240
slow_arr[500000000] = 'hello';
1241
slow_arr.length = 1000000000;
1242
Object.observe(slow_arr, observer.callback);
1244
function slowSpliceCallback(records) {
1245
spliceRecords = records;
1247
Array.observe(slow_arr, slowSpliceCallback);
1248
slow_arr.length = 100;
1249
Object.deliverChangeRecords(observer.callback);
1250
observer.assertCallbackRecords([
1251
{ object: slow_arr, name: '500000000', type: 'delete', oldValue: 'hello' },
1252
{ object: slow_arr, name: 'length', type: 'update', oldValue: 1000000000 },
1254
Object.deliverChangeRecords(slowSpliceCallback);
1255
assertEquals(spliceRecords.length, 1);
1256
// Have to custom assert this splice record because the removed array is huge.
1257
var splice = spliceRecords[0];
1258
assertSame(splice.object, slow_arr);
1259
assertEquals(splice.type, 'splice');
1260
assertEquals(splice.index, 100);
1261
assertEquals(splice.addedCount, 0);
1262
var array_keys = %GetArrayKeys(splice.removed, splice.removed.length);
1263
assertEquals(array_keys.length, 1);
1264
assertEquals(array_keys[0], 499999900);
1265
assertEquals(splice.removed[499999900], 'hello');
1266
assertEquals(splice.removed.length, 999999900);
1269
// Assignments in loops (checking different IC states).
1272
Object.observe(obj, observer.callback);
1273
for (var i = 0; i < 5; i++) {
1276
Object.deliverChangeRecords(observer.callback);
1277
observer.assertCallbackRecords([
1278
{ object: obj, name: "a0", type: "add" },
1279
{ object: obj, name: "a1", type: "add" },
1280
{ object: obj, name: "a2", type: "add" },
1281
{ object: obj, name: "a3", type: "add" },
1282
{ object: obj, name: "a4", type: "add" },
1287
Object.observe(obj, observer.callback);
1288
for (var i = 0; i < 5; i++) {
1291
Object.deliverChangeRecords(observer.callback);
1292
observer.assertCallbackRecords([
1293
{ object: obj, name: "0", type: "add" },
1294
{ object: obj, name: "1", type: "add" },
1295
{ object: obj, name: "2", type: "add" },
1296
{ object: obj, name: "3", type: "add" },
1297
{ object: obj, name: "4", type: "add" },
1301
// Adding elements past the end of an array should notify on length for
1302
// Object.observe and emit "splices" for Array.observe.
1304
var arr = [1, 2, 3];
1305
Object.observe(arr, observer.callback);
1306
Array.observe(arr, observer2.callback);
1309
Object.defineProperty(arr, '200', {value: 7});
1310
Object.defineProperty(arr, '400', {get: function(){}});
1311
arr[50] = 30; // no length change expected
1312
Object.deliverChangeRecords(observer.callback);
1313
observer.assertCallbackRecords([
1314
{ object: arr, name: '3', type: 'add' },
1315
{ object: arr, name: 'length', type: 'update', oldValue: 3 },
1316
{ object: arr, name: '100', type: 'add' },
1317
{ object: arr, name: 'length', type: 'update', oldValue: 4 },
1318
{ object: arr, name: '200', type: 'add' },
1319
{ object: arr, name: 'length', type: 'update', oldValue: 101 },
1320
{ object: arr, name: '400', type: 'add' },
1321
{ object: arr, name: 'length', type: 'update', oldValue: 201 },
1322
{ object: arr, name: '50', type: 'add' },
1324
Object.deliverChangeRecords(observer2.callback);
1325
observer2.assertCallbackRecords([
1326
{ object: arr, type: 'splice', index: 3, removed: [], addedCount: 1 },
1327
{ object: arr, type: 'splice', index: 4, removed: [], addedCount: 97 },
1328
{ object: arr, type: 'splice', index: 101, removed: [], addedCount: 100 },
1329
{ object: arr, type: 'splice', index: 201, removed: [], addedCount: 200 },
1330
{ object: arr, type: 'add', name: '50' },
1334
// Tests for array methods, first on arrays and then on plain objects
1341
Object.observe(array, observer.callback);
1342
Array.observe(array, observer2.callback);
1345
Object.deliverChangeRecords(observer.callback);
1346
observer.assertCallbackRecords([
1347
{ object: array, name: '2', type: 'add' },
1348
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1349
{ object: array, name: '3', type: 'add' },
1350
{ object: array, name: 'length', type: 'update', oldValue: 3 },
1351
{ object: array, name: '4', type: 'add' },
1352
{ object: array, name: 'length', type: 'update', oldValue: 4 },
1354
Object.deliverChangeRecords(observer2.callback);
1355
observer2.assertCallbackRecords([
1356
{ object: array, type: 'splice', index: 2, removed: [], addedCount: 2 },
1357
{ object: array, type: 'splice', index: 4, removed: [], addedCount: 1 }
1363
Object.observe(array, observer.callback);
1366
Object.deliverChangeRecords(observer.callback);
1367
observer.assertCallbackRecords([
1368
{ object: array, name: '1', type: 'delete', oldValue: 2 },
1369
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1370
{ object: array, name: '0', type: 'delete', oldValue: 1 },
1371
{ object: array, name: 'length', type: 'update', oldValue: 1 },
1377
Object.observe(array, observer.callback);
1380
Object.deliverChangeRecords(observer.callback);
1381
observer.assertCallbackRecords([
1382
{ object: array, name: '0', type: 'update', oldValue: 1 },
1383
{ object: array, name: '1', type: 'delete', oldValue: 2 },
1384
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1385
{ object: array, name: '0', type: 'delete', oldValue: 2 },
1386
{ object: array, name: 'length', type: 'update', oldValue: 1 },
1392
Object.observe(array, observer.callback);
1393
array.unshift(3, 4);
1394
Object.deliverChangeRecords(observer.callback);
1395
observer.assertCallbackRecords([
1396
{ object: array, name: '3', type: 'add' },
1397
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1398
{ object: array, name: '2', type: 'add' },
1399
{ object: array, name: '0', type: 'update', oldValue: 1 },
1400
{ object: array, name: '1', type: 'update', oldValue: 2 },
1405
var array = [1, 2, 3];
1406
Object.observe(array, observer.callback);
1407
array.splice(1, 1, 4, 5);
1408
Object.deliverChangeRecords(observer.callback);
1409
observer.assertCallbackRecords([
1410
{ object: array, name: '3', type: 'add' },
1411
{ object: array, name: 'length', type: 'update', oldValue: 3 },
1412
{ object: array, name: '1', type: 'update', oldValue: 2 },
1413
{ object: array, name: '2', type: 'update', oldValue: 3 },
1418
var array = [3, 2, 1];
1419
Object.observe(array, observer.callback);
1421
assertEquals(1, array[0]);
1422
assertEquals(2, array[1]);
1423
assertEquals(3, array[2]);
1424
Object.deliverChangeRecords(observer.callback);
1425
observer.assertCallbackRecords([
1426
{ object: array, name: '1', type: 'update', oldValue: 2 },
1427
{ object: array, name: '0', type: 'update', oldValue: 3 },
1428
{ object: array, name: '2', type: 'update', oldValue: 1 },
1429
{ object: array, name: '1', type: 'update', oldValue: 3 },
1430
{ object: array, name: '0', type: 'update', oldValue: 2 },
1433
// Splice emitted after Array mutation methods
1434
function MockArray(initial, observer) {
1435
for (var i = 0; i < initial.length; i++)
1436
this[i] = initial[i];
1438
this.length_ = initial.length;
1439
this.observer = observer;
1441
MockArray.prototype = {
1442
set length(length) {
1443
Object.getNotifier(this).notify({ type: 'lengthChange' });
1444
this.length_ = length;
1445
Object.observe(this, this.observer.callback, ['splice']);
1448
return this.length_;
1453
var array = new MockArray([], observer);
1454
Object.observe(array, observer.callback, ['lengthChange']);
1455
Array.prototype.push.call(array, 1);
1456
Object.deliverChangeRecords(observer.callback);
1457
observer.assertCallbackRecords([
1458
{ object: array, type: 'lengthChange' },
1459
{ object: array, type: 'splice', index: 0, removed: [], addedCount: 1 },
1463
var array = new MockArray([1], observer);
1464
Object.observe(array, observer.callback, ['lengthChange']);
1465
Array.prototype.pop.call(array);
1466
Object.deliverChangeRecords(observer.callback);
1467
observer.assertCallbackRecords([
1468
{ object: array, type: 'lengthChange' },
1469
{ object: array, type: 'splice', index: 0, removed: [1], addedCount: 0 },
1473
var array = new MockArray([1], observer);
1474
Object.observe(array, observer.callback, ['lengthChange']);
1475
Array.prototype.shift.call(array);
1476
Object.deliverChangeRecords(observer.callback);
1477
observer.assertCallbackRecords([
1478
{ object: array, type: 'lengthChange' },
1479
{ object: array, type: 'splice', index: 0, removed: [1], addedCount: 0 },
1483
var array = new MockArray([], observer);
1484
Object.observe(array, observer.callback, ['lengthChange']);
1485
Array.prototype.unshift.call(array, 1);
1486
Object.deliverChangeRecords(observer.callback);
1487
observer.assertCallbackRecords([
1488
{ object: array, type: 'lengthChange' },
1489
{ object: array, type: 'splice', index: 0, removed: [], addedCount: 1 },
1493
var array = new MockArray([0, 1, 2], observer);
1494
Object.observe(array, observer.callback, ['lengthChange']);
1495
Array.prototype.splice.call(array, 1, 1);
1496
Object.deliverChangeRecords(observer.callback);
1497
observer.assertCallbackRecords([
1498
{ object: array, type: 'lengthChange' },
1499
{ object: array, type: 'splice', index: 1, removed: [1], addedCount: 0 },
1503
// === PLAIN OBJECTS ===
1507
var array = {0: 1, 1: 2, length: 2}
1508
Object.observe(array, observer.callback);
1509
Array.prototype.push.call(array, 3, 4);
1510
Object.deliverChangeRecords(observer.callback);
1511
observer.assertCallbackRecords([
1512
{ object: array, name: '2', type: 'add' },
1513
{ object: array, name: '3', type: 'add' },
1514
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1520
Object.observe(array, observer.callback);
1521
Array.observe(array, observer2.callback);
1525
Object.deliverChangeRecords(observer.callback);
1526
observer.assertCallbackRecords([
1527
{ object: array, name: '1', type: 'delete', oldValue: 2 },
1528
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1529
{ object: array, name: '0', type: 'delete', oldValue: 1 },
1530
{ object: array, name: 'length', type: 'update', oldValue: 1 },
1532
Object.deliverChangeRecords(observer2.callback);
1533
observer2.assertCallbackRecords([
1534
{ object: array, type: 'splice', index: 1, removed: [2], addedCount: 0 },
1535
{ object: array, type: 'splice', index: 0, removed: [1], addedCount: 0 }
1541
Object.observe(array, observer.callback);
1542
Array.observe(array, observer2.callback);
1546
Object.deliverChangeRecords(observer.callback);
1547
observer.assertCallbackRecords([
1548
{ object: array, name: '0', type: 'update', oldValue: 1 },
1549
{ object: array, name: '1', type: 'delete', oldValue: 2 },
1550
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1551
{ object: array, name: '0', type: 'delete', oldValue: 2 },
1552
{ object: array, name: 'length', type: 'update', oldValue: 1 },
1554
Object.deliverChangeRecords(observer2.callback);
1555
observer2.assertCallbackRecords([
1556
{ object: array, type: 'splice', index: 0, removed: [1], addedCount: 0 },
1557
{ object: array, type: 'splice', index: 0, removed: [2], addedCount: 0 }
1563
Object.observe(array, observer.callback);
1564
Array.observe(array, observer2.callback);
1565
array.unshift(3, 4);
1567
Object.deliverChangeRecords(observer.callback);
1568
observer.assertCallbackRecords([
1569
{ object: array, name: '3', type: 'add' },
1570
{ object: array, name: 'length', type: 'update', oldValue: 2 },
1571
{ object: array, name: '2', type: 'add' },
1572
{ object: array, name: '0', type: 'update', oldValue: 1 },
1573
{ object: array, name: '1', type: 'update', oldValue: 2 },
1574
{ object: array, name: '4', type: 'add' },
1575
{ object: array, name: 'length', type: 'update', oldValue: 4 },
1576
{ object: array, name: '3', type: 'update', oldValue: 2 },
1577
{ object: array, name: '2', type: 'update', oldValue: 1 },
1578
{ object: array, name: '1', type: 'update', oldValue: 4 },
1579
{ object: array, name: '0', type: 'update', oldValue: 3 },
1581
Object.deliverChangeRecords(observer2.callback);
1582
observer2.assertCallbackRecords([
1583
{ object: array, type: 'splice', index: 0, removed: [], addedCount: 2 },
1584
{ object: array, type: 'splice', index: 0, removed: [], addedCount: 1 }
1589
var array = [1, 2, 3];
1590
Object.observe(array, observer.callback);
1591
Array.observe(array, observer2.callback);
1592
array.splice(1, 0, 4, 5); // 1 4 5 2 3
1593
array.splice(0, 2); // 5 2 3
1594
array.splice(1, 2, 6, 7); // 5 6 7
1596
Object.deliverChangeRecords(observer.callback);
1597
observer.assertCallbackRecords([
1598
{ object: array, name: '4', type: 'add' },
1599
{ object: array, name: 'length', type: 'update', oldValue: 3 },
1600
{ object: array, name: '3', type: 'add' },
1601
{ object: array, name: '1', type: 'update', oldValue: 2 },
1602
{ object: array, name: '2', type: 'update', oldValue: 3 },
1604
{ object: array, name: '0', type: 'update', oldValue: 1 },
1605
{ object: array, name: '1', type: 'update', oldValue: 4 },
1606
{ object: array, name: '2', type: 'update', oldValue: 5 },
1607
{ object: array, name: '4', type: 'delete', oldValue: 3 },
1608
{ object: array, name: '3', type: 'delete', oldValue: 2 },
1609
{ object: array, name: 'length', type: 'update', oldValue: 5 },
1611
{ object: array, name: '1', type: 'update', oldValue: 2 },
1612
{ object: array, name: '2', type: 'update', oldValue: 3 },
1614
Object.deliverChangeRecords(observer2.callback);
1615
observer2.assertCallbackRecords([
1616
{ object: array, type: 'splice', index: 1, removed: [], addedCount: 2 },
1617
{ object: array, type: 'splice', index: 0, removed: [1, 4], addedCount: 0 },
1618
{ object: array, type: 'splice', index: 1, removed: [2, 3], addedCount: 2 },
1621
// Exercise StoreIC_ArrayLength
1624
Object.observe(dummy, observer.callback);
1625
Object.unobserve(dummy, observer.callback);
1627
Object.observe(array, observer.callback);
1629
Object.deliverChangeRecords(observer.callback);
1630
observer.assertCallbackRecords([
1631
{ object: array, name: '0', type: 'delete', oldValue: 0 },
1632
{ object: array, name: 'length', type: 'update', oldValue: 1},
1639
Object.observe(obj, observer.callback);
1640
var p = {foo: 'yes'};
1641
var q = {bar: 'no'};
1643
obj.__proto__ = p; // ignored
1644
obj.__proto__ = null;
1645
obj.__proto__ = q; // the __proto__ accessor is gone
1646
// TODO(adamk): Add tests for objects with hidden prototypes
1647
// once we support observing the global object.
1648
Object.deliverChangeRecords(observer.callback);
1649
observer.assertCallbackRecords([
1650
{ object: obj, name: '__proto__', type: 'setPrototype',
1651
oldValue: Object.prototype },
1652
{ object: obj, name: '__proto__', type: 'setPrototype', oldValue: p },
1653
{ object: obj, name: '__proto__', type: 'add' },
1657
// Function.prototype
1659
var fun = function(){};
1660
Object.observe(fun, observer.callback);
1661
var myproto = {foo: 'bar'};
1662
fun.prototype = myproto;
1664
fun.prototype = 7; // ignored
1665
Object.defineProperty(fun, 'prototype', {value: 8});
1666
Object.deliverChangeRecords(observer.callback);
1667
observer.assertRecordCount(3);
1668
// Manually examine the first record in order to test
1669
// lazy creation of oldValue
1670
assertSame(fun, observer.records[0].object);
1671
assertEquals('prototype', observer.records[0].name);
1672
assertEquals('update', observer.records[0].type);
1673
// The only existing reference to the oldValue object is in this
1674
// record, so to test that lazy creation happened correctly
1675
// we compare its constructor to our function (one of the invariants
1676
// ensured when creating an object via AllocateFunctionPrototype).
1677
assertSame(fun, observer.records[0].oldValue.constructor);
1678
observer.records.splice(0, 1);
1679
observer.assertCallbackRecords([
1680
{ object: fun, name: 'prototype', type: 'update', oldValue: myproto },
1681
{ object: fun, name: 'prototype', type: 'update', oldValue: 7 },
1684
// Function.prototype should not be observable except on the object itself
1686
var fun = function(){};
1687
var obj = { __proto__: fun };
1688
Object.observe(obj, observer.callback);
1690
Object.deliverChangeRecords(observer.callback);
1691
observer.assertRecordCount(1);
1692
observer.assertCallbackRecords([
1693
{ object: obj, name: 'prototype', type: 'add' },
1696
// Check that changes in observation status are detected in all IC states and
1697
// in optimized code, especially in cases usually using fast elements.
1700
"a[i] ? ++a[i] : a[i] = v",
1701
"a[i] ? a[i]++ : a[i] = v",
1702
"a[i] ? a[i] += 1 : a[i] = v",
1703
"a[i] ? a[i] -= -1 : a[i] = v",
1706
var props = [1, "1", "a"];
1708
function TestFastElements(prop, mutation, prepopulate, polymorphic, optimize) {
1709
var setElement = eval(
1710
"(function setElement(a, i, v) { " + mutation + "; " +
1711
"/* " + [].join.call(arguments, " ") + " */" +
1714
print("TestFastElements:", setElement);
1716
var arr = prepopulate ? [1, 2, 3, 4, 5] : [0];
1717
if (prepopulate) arr[prop] = 2; // for non-element case
1718
setElement(arr, prop, 3);
1719
setElement(arr, prop, 4);
1720
if (polymorphic) setElement(["M", "i", "l", "n", "e", "r"], 0, "m");
1721
if (optimize) %OptimizeFunctionOnNextCall(setElement);
1722
setElement(arr, prop, 5);
1725
Object.observe(arr, observer.callback);
1726
setElement(arr, prop, 989898);
1727
Object.deliverChangeRecords(observer.callback);
1728
observer.assertCallbackRecords([
1729
{ object: arr, name: "" + prop, type: 'update', oldValue: 5 }
1733
for (var b1 = 0; b1 < 2; ++b1)
1734
for (var b2 = 0; b2 < 2; ++b2)
1735
for (var b3 = 0; b3 < 2; ++b3)
1736
for (var i in props)
1737
for (var j in mutation)
1738
TestFastElements(props[i], mutation[j], b1 != 0, b2 != 0, b3 != 0);
1743
"a.length += newSize - oldSize",
1744
"a.length -= oldSize - newSize",
1747
var mutationByIncr = [
1752
function TestFastElementsLength(
1753
mutation, polymorphic, optimize, oldSize, newSize) {
1754
var setLength = eval(
1755
"(function setLength(a, v) { " + mutation + "; " +
1756
"/* " + [].join.call(arguments, " ") + " */"
1759
print("TestFastElementsLength:", setLength);
1762
var arr = new Array(n);
1763
for (var i = 0; i < n; ++i) arr[i] = i;
1767
setLength(array(oldSize), newSize);
1768
setLength(array(oldSize), newSize);
1769
if (polymorphic) setLength(array(oldSize).map(isNaN), newSize);
1770
if (optimize) %OptimizeFunctionOnNextCall(setLength);
1771
setLength(array(oldSize), newSize);
1774
var arr = array(oldSize);
1775
Object.observe(arr, observer.callback);
1776
setLength(arr, newSize);
1777
Object.deliverChangeRecords(observer.callback);
1778
if (oldSize === newSize) {
1779
observer.assertNotCalled();
1781
var count = oldSize > newSize ? oldSize - newSize : 0;
1782
observer.assertRecordCount(count + 1);
1783
var lengthRecord = observer.records[count];
1784
assertSame(arr, lengthRecord.object);
1785
assertEquals('length', lengthRecord.name);
1786
assertEquals('update', lengthRecord.type);
1787
assertSame(oldSize, lengthRecord.oldValue);
1791
for (var b1 = 0; b1 < 2; ++b1)
1792
for (var b2 = 0; b2 < 2; ++b2)
1793
for (var n1 = 0; n1 < 3; ++n1)
1794
for (var n2 = 0; n2 < 3; ++n2)
1795
for (var i in mutation)
1796
TestFastElementsLength(mutation[i], b1 != 0, b2 != 0, 20*n1, 20*n2);
1798
for (var b1 = 0; b1 < 2; ++b1)
1799
for (var b2 = 0; b2 < 2; ++b2)
1800
for (var n = 0; n < 3; ++n)
1801
for (var i in mutationByIncr)
1802
TestFastElementsLength(mutationByIncr[i], b1 != 0, b2 != 0, 7*n, 7*n+1);
1805
(function TestFunctionName() {
1809
Object.observe(fun, observer.callback);
1810
fun.name = 'x'; // No change. Not writable.
1811
Object.defineProperty(fun, 'name', {value: 'a'});
1812
Object.defineProperty(fun, 'name', {writable: true});
1815
fun.name = 'x'; // No change. Function.prototype.name is non writable
1816
Object.defineProperty(Function.prototype, 'name', {writable: true});
1818
fun.name = 'c'; // Same, no update.
1819
Object.deliverChangeRecords(observer.callback);
1820
observer.assertCallbackRecords([
1821
{ object: fun, type: 'update', name: 'name', oldValue: 'fun' },
1822
{ object: fun, type: 'reconfigure', name: 'name'},
1823
{ object: fun, type: 'update', name: 'name', oldValue: 'a' },
1824
{ object: fun, type: 'delete', name: 'name', oldValue: 'b' },
1825
{ object: fun, type: 'add', name: 'name' },
1830
(function TestFunctionLength() {
1834
Object.observe(fun, observer.callback);
1835
fun.length = 'x'; // No change. Not writable.
1836
Object.defineProperty(fun, 'length', {value: 'a'});
1837
Object.defineProperty(fun, 'length', {writable: true});
1840
fun.length = 'x'; // No change. Function.prototype.length is non writable
1841
Object.defineProperty(Function.prototype, 'length', {writable: true});
1843
fun.length = 'c'; // Same, no update.
1844
Object.deliverChangeRecords(observer.callback);
1845
observer.assertCallbackRecords([
1846
{ object: fun, type: 'update', name: 'length', oldValue: 1 },
1847
{ object: fun, type: 'reconfigure', name: 'length'},
1848
{ object: fun, type: 'update', name: 'length', oldValue: 'a' },
1849
{ object: fun, type: 'delete', name: 'length', oldValue: 'b' },
1850
{ object: fun, type: 'add', name: 'length' },
1855
(function TestObserveInvalidAcceptMessage() {
1858
Object.observe({}, function(){}, "not an object");
1862
assertInstanceof(ex, TypeError);
1863
assertEquals("Third argument to Object.observe must be an array of strings.",