Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Side by Side Diff: test/mjsunit/es7/object-observe.js

Issue 1909433003: Remove support for Object.observe (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Rebased Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
4 // met:
5 //
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.
15 //
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.
27
28 // Flags: --harmony-object-observe
29 // Flags: --allow-natives-syntax
30
31 var allObservers = [];
32 function reset() {
33 allObservers.forEach(function(observer) { observer.reset(); });
34 }
35
36 function stringifyNoThrow(arg) {
37 try {
38 return JSON.stringify(arg);
39 } catch (e) {
40 return '{<circular reference>}';
41 }
42 }
43
44 function createObserver() {
45 "use strict"; // So that |this| in callback can be undefined.
46
47 var observer = {
48 records: undefined,
49 callbackCount: 0,
50 reset: function() {
51 this.records = undefined;
52 this.callbackCount = 0;
53 },
54 assertNotCalled: function() {
55 assertEquals(undefined, this.records);
56 assertEquals(0, this.callbackCount);
57 },
58 assertCalled: function() {
59 assertEquals(1, this.callbackCount);
60 },
61 assertRecordCount: function(count) {
62 this.assertCalled();
63 assertEquals(count, this.records.length);
64 },
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]);
73 }
74 }
75 };
76
77 observer.callback = function(r) {
78 assertEquals(undefined, this);
79 assertEquals('object', typeof r);
80 assertTrue(r instanceof Array)
81 observer.records = r;
82 observer.callbackCount++;
83 };
84
85 observer.reset();
86 allObservers.push(observer);
87 return observer;
88 }
89
90 var observer = createObserver();
91 var observer2 = createObserver();
92
93 assertEquals("function", typeof observer.callback);
94 assertEquals("function", typeof observer2.callback);
95
96 var obj = {};
97
98 function frozenFunction() {}
99 Object.freeze(frozenFunction);
100 var nonFunction = {};
101 var changeRecordWithAccessor = { type: 'foo' };
102 var recordCreated = false;
103 Object.defineProperty(changeRecordWithAccessor, 'name', {
104 get: function() {
105 recordCreated = true;
106 return "bar";
107 },
108 enumerable: true
109 })
110
111
112 // Object.observe
113 assertThrows(function() { Object.observe("non-object", observer.callback); },
114 TypeError);
115 assertThrows(function() { Object.observe(this, observer.callback); },
116 TypeError);
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));
128
129 // Object.unobserve
130 assertThrows(function() { Object.unobserve(4, observer.callback); }, TypeError);
131 assertThrows(function() { Object.unobserve(this, observer.callback); },
132 TypeError);
133 assertThrows(function() { Object.unobserve(obj, nonFunction); }, TypeError);
134 assertEquals(obj, Object.unobserve(obj, observer.callback));
135
136
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);
150
151 assertThrows(function() { notifier.performChange(1, function(){}); }, TypeError) ;
152 assertThrows(function() { notifier.performChange(undefined, function(){}); }, Ty peError);
153 assertThrows(function() { notifier.performChange('foo', undefined); }, TypeError );
154 assertThrows(function() { notifier.performChange('foo', 'bar'); }, TypeError);
155 var global = this;
156 notifier.performChange('foo', function() {
157 assertEquals(global, this);
158 });
159
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
170
171
172 // Object.deliverChangeRecords
173 assertThrows(function() { Object.deliverChangeRecords(nonFunction); }, TypeError );
174
175 Object.observe(obj, observer.callback);
176
177
178 // notify uses to [[CreateOwnProperty]] to create changeRecord;
179 reset();
180 var protoExpandoAccessed = false;
181 Object.defineProperty(Object.prototype, 'protoExpando',
182 {
183 configurable: true,
184 set: function() { protoExpandoAccessed = true; }
185 }
186 );
187 notifier.notify({ type: 'foo', protoExpando: 'val'});
188 assertFalse(protoExpandoAccessed);
189 delete Object.prototype.protoExpando;
190 Object.deliverChangeRecords(observer.callback);
191
192
193 // Multiple records are delivered.
194 reset();
195 notifier.notify({
196 type: 'update',
197 name: 'foo',
198 expando: 1
199 });
200
201 notifier.notify({
202 object: notifier, // object property is ignored
203 type: 'delete',
204 name: 'bar',
205 expando2: 'str'
206 });
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' }
211 ]);
212
213 // Non-string accept values are coerced to strings
214 reset();
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' }
231 ]);
232
233 // No delivery takes place if no records are pending
234 reset();
235 Object.deliverChangeRecords(observer.callback);
236 observer.assertNotCalled();
237
238
239 // Multiple observation has no effect.
240 reset();
241 Object.observe(obj, observer.callback);
242 Object.observe(obj, observer.callback);
243 Object.getNotifier(obj).notify({
244 type: 'update',
245 });
246 Object.deliverChangeRecords(observer.callback);
247 observer.assertCalled();
248
249
250 // Observation can be stopped.
251 reset();
252 Object.unobserve(obj, observer.callback);
253 Object.getNotifier(obj).notify({
254 type: 'update',
255 });
256 Object.deliverChangeRecords(observer.callback);
257 observer.assertNotCalled();
258
259
260 // Multiple unobservation has no effect
261 reset();
262 Object.unobserve(obj, observer.callback);
263 Object.unobserve(obj, observer.callback);
264 Object.getNotifier(obj).notify({
265 type: 'update',
266 });
267 Object.deliverChangeRecords(observer.callback);
268 observer.assertNotCalled();
269
270
271 // Re-observation works and only includes changeRecords after of call.
272 reset();
273 Object.getNotifier(obj).notify({
274 type: 'update',
275 });
276 Object.observe(obj, observer.callback);
277 Object.getNotifier(obj).notify({
278 type: 'update',
279 });
280 records = undefined;
281 Object.deliverChangeRecords(observer.callback);
282 observer.assertRecordCount(1);
283
284 // Get notifier prior to observing
285 reset();
286 var obj = {};
287 Object.getNotifier(obj);
288 Object.observe(obj, observer.callback);
289 obj.id = 1;
290 Object.deliverChangeRecords(observer.callback);
291 observer.assertCallbackRecords([
292 { object: obj, type: 'add', name: 'id' },
293 ]);
294
295 // The empty-string property is observable
296 reset();
297 var obj = {};
298 Object.observe(obj, observer.callback);
299 obj[''] = '';
300 obj[''] = ' ';
301 delete obj[''];
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: ' ' },
307 ]);
308
309 // Object.preventExtensions
310 reset();
311 var obj = { foo: 'bar'};
312 Object.observe(obj, observer.callback);
313 obj.baz = 'bat';
314 Object.preventExtensions(obj);
315
316 Object.deliverChangeRecords(observer.callback);
317 observer.assertCallbackRecords([
318 { object: obj, type: 'add', name: 'baz' },
319 { object: obj, type: 'preventExtensions' },
320 ]);
321
322 reset();
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();
329
330 // Object.freeze
331 reset();
332 var obj = { a: 'a' };
333 Object.defineProperty(obj, 'b', {
334 writable: false,
335 configurable: true,
336 value: 'b'
337 });
338 Object.defineProperty(obj, 'c', {
339 writable: true,
340 configurable: false,
341 value: 'c'
342 });
343 Object.defineProperty(obj, 'd', {
344 writable: false,
345 configurable: false,
346 value: 'd'
347 });
348 Object.observe(obj, observer.callback);
349 Object.freeze(obj);
350
351 Object.deliverChangeRecords(observer.callback);
352 observer.assertCallbackRecords([
353 { object: obj, type: 'preventExtensions' },
354 { object: obj, type: 'reconfigure', name: 'a' },
355 { object: obj, type: 'reconfigure', name: 'b' },
356 { object: obj, type: 'reconfigure', name: 'c' },
357 ]);
358
359 reset();
360 var obj = { foo: 'bar'};
361 Object.freeze(obj);
362 Object.observe(obj, observer.callback);
363 Object.freeze(obj);
364 Object.deliverChangeRecords(observer.callback);
365 observer.assertNotCalled();
366
367 // Object.seal
368 reset();
369 var obj = { a: 'a' };
370 Object.defineProperty(obj, 'b', {
371 writable: false,
372 configurable: true,
373 value: 'b'
374 });
375 Object.defineProperty(obj, 'c', {
376 writable: true,
377 configurable: false,
378 value: 'c'
379 });
380 Object.defineProperty(obj, 'd', {
381 writable: false,
382 configurable: false,
383 value: 'd'
384 });
385 Object.observe(obj, observer.callback);
386 Object.seal(obj);
387
388 Object.deliverChangeRecords(observer.callback);
389 observer.assertCallbackRecords([
390 { object: obj, type: 'preventExtensions' },
391 { object: obj, type: 'reconfigure', name: 'a' },
392 { object: obj, type: 'reconfigure', name: 'b' },
393 ]);
394
395 reset();
396 var obj = { foo: 'bar'};
397 Object.seal(obj);
398 Object.observe(obj, observer.callback);
399 Object.seal(obj);
400 Object.deliverChangeRecords(observer.callback);
401 observer.assertNotCalled();
402
403 // Observing a continuous stream of changes, while itermittantly unobserving.
404 reset();
405 var obj = {};
406 Object.observe(obj, observer.callback);
407 Object.getNotifier(obj).notify({
408 type: 'update',
409 val: 1
410 });
411
412 Object.unobserve(obj, observer.callback);
413 Object.getNotifier(obj).notify({
414 type: 'update',
415 val: 2
416 });
417
418 Object.observe(obj, observer.callback);
419 Object.getNotifier(obj).notify({
420 type: 'update',
421 val: 3
422 });
423
424 Object.unobserve(obj, observer.callback);
425 Object.getNotifier(obj).notify({
426 type: 'update',
427 val: 4
428 });
429
430 Object.observe(obj, observer.callback);
431 Object.getNotifier(obj).notify({
432 type: 'update',
433 val: 5
434 });
435
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 }
442 ]);
443
444 // Accept
445 reset();
446 Object.observe(obj, observer.callback, ['somethingElse']);
447 Object.getNotifier(obj).notify({
448 type: 'add'
449 });
450 Object.getNotifier(obj).notify({
451 type: 'update'
452 });
453 Object.getNotifier(obj).notify({
454 type: 'delete'
455 });
456 Object.getNotifier(obj).notify({
457 type: 'reconfigure'
458 });
459 Object.getNotifier(obj).notify({
460 type: 'setPrototype'
461 });
462 Object.deliverChangeRecords(observer.callback);
463 observer.assertNotCalled();
464
465 reset();
466 Object.observe(obj, observer.callback, ['add', 'delete', 'setPrototype']);
467 Object.getNotifier(obj).notify({
468 type: 'add'
469 });
470 Object.getNotifier(obj).notify({
471 type: 'update'
472 });
473 Object.getNotifier(obj).notify({
474 type: 'delete'
475 });
476 Object.getNotifier(obj).notify({
477 type: 'delete'
478 });
479 Object.getNotifier(obj).notify({
480 type: 'reconfigure'
481 });
482 Object.getNotifier(obj).notify({
483 type: 'setPrototype'
484 });
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' }
491 ]);
492
493 reset();
494 Object.observe(obj, observer.callback, ['update', 'foo']);
495 Object.getNotifier(obj).notify({
496 type: 'add'
497 });
498 Object.getNotifier(obj).notify({
499 type: 'update'
500 });
501 Object.getNotifier(obj).notify({
502 type: 'delete'
503 });
504 Object.getNotifier(obj).notify({
505 type: 'foo'
506 });
507 Object.getNotifier(obj).notify({
508 type: 'bar'
509 });
510 Object.getNotifier(obj).notify({
511 type: 'foo'
512 });
513 Object.deliverChangeRecords(observer.callback);
514 observer.assertCallbackRecords([
515 { object: obj, type: 'update' },
516 { object: obj, type: 'foo' },
517 { object: obj, type: 'foo' }
518 ]);
519
520 reset();
521 function Thingy(a, b, c) {
522 this.a = a;
523 this.b = b;
524 }
525
526 Thingy.MULTIPLY = 'multiply';
527 Thingy.INCREMENT = 'increment';
528 Thingy.INCREMENT_AND_MULTIPLY = 'incrementAndMultiply';
529
530 Thingy.prototype = {
531 increment: function(amount) {
532 var notifier = Object.getNotifier(this);
533
534 var self = this;
535 notifier.performChange(Thingy.INCREMENT, function() {
536 self.a += amount;
537 self.b += amount;
538
539 return {
540 incremented: amount
541 }; // implicit notify
542 });
543 },
544
545 multiply: function(amount) {
546 var notifier = Object.getNotifier(this);
547
548 var self = this;
549 notifier.performChange(Thingy.MULTIPLY, function() {
550 self.a *= amount;
551 self.b *= amount;
552
553 return {
554 multiplied: amount
555 }; // implicit notify
556 });
557 },
558
559 incrementAndMultiply: function(incAmount, multAmount) {
560 var notifier = Object.getNotifier(this);
561
562 var self = this;
563 notifier.performChange(Thingy.INCREMENT_AND_MULTIPLY, function() {
564 self.increment(incAmount);
565 self.multiply(multAmount);
566
567 return {
568 incremented: incAmount,
569 multiplied: multAmount
570 }; // implicit notify
571 });
572 }
573 }
574
575 Thingy.observe = function(thingy, callback) {
576 Object.observe(thingy, callback, [Thingy.INCREMENT,
577 Thingy.MULTIPLY,
578 Thingy.INCREMENT_AND_MULTIPLY,
579 'update']);
580 }
581
582 Thingy.unobserve = function(thingy, callback) {
583 Object.unobserve(thingy);
584 }
585
586 var thingy = new Thingy(2, 4);
587
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 }
595
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 },
609 ]);
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 },
615 {
616 object: thingy,
617 type: Thingy.INCREMENT_AND_MULTIPLY,
618 incremented: 2,
619 multiplied: 2
620 }
621 ]);
622
623 // ArrayPush cached stub
624 reset();
625
626 function pushMultiple(arr) {
627 arr.push('a');
628 arr.push('b');
629 arr.push('c');
630 }
631
632 for (var i = 0; i < 5; i++) {
633 var arr = [];
634 pushMultiple(arr);
635 }
636
637 for (var i = 0; i < 5; i++) {
638 reset();
639 var arr = [];
640 Object.observe(arr, observer.callback);
641 pushMultiple(arr);
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 },
651 ]);
652 }
653
654
655 // ArrayPop cached stub
656 reset();
657
658 function popMultiple(arr) {
659 arr.pop();
660 arr.pop();
661 arr.pop();
662 }
663
664 for (var i = 0; i < 5; i++) {
665 var arr = ['a', 'b', 'c'];
666 popMultiple(arr);
667 }
668
669 for (var i = 0; i < 5; i++) {
670 reset();
671 var arr = ['a', 'b', 'c'];
672 Object.observe(arr, observer.callback);
673 popMultiple(arr);
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 },
683 ]);
684 }
685
686
687 reset();
688 function RecursiveThingy() {}
689
690 RecursiveThingy.MULTIPLY_FIRST_N = 'multiplyFirstN';
691
692 RecursiveThingy.prototype = {
693 __proto__: Array.prototype,
694
695 multiplyFirstN: function(amount, n) {
696 if (!n)
697 return;
698 var notifier = Object.getNotifier(this);
699 var self = this;
700 notifier.performChange(RecursiveThingy.MULTIPLY_FIRST_N, function() {
701 self[n-1] = self[n-1]*amount;
702 self.multiplyFirstN(amount, n-1);
703 });
704
705 notifier.notify({
706 type: RecursiveThingy.MULTIPLY_FIRST_N,
707 multiplied: amount,
708 n: n
709 });
710 },
711 }
712
713 RecursiveThingy.observe = function(thingy, callback) {
714 Object.observe(thingy, callback, [RecursiveThingy.MULTIPLY_FIRST_N]);
715 }
716
717 RecursiveThingy.unobserve = function(thingy, callback) {
718 Object.unobserve(thingy);
719 }
720
721 var thingy = new RecursiveThingy;
722 thingy.push(1, 2, 3, 4);
723
724 Object.observe(thingy, observer.callback);
725 RecursiveThingy.observe(thingy, observer2.callback);
726 thingy.multiplyFirstN(2, 3); // [2, 4, 6, 4]
727
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 }
734 ]);
735 observer2.assertCallbackRecords([
736 { object: thingy, type: RecursiveThingy.MULTIPLY_FIRST_N, multiplied: 2, n: 3 }
737 ]);
738
739 reset();
740 function DeckSuit() {
741 this.push('1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'A', 'Q', 'K');
742 }
743
744 DeckSuit.SHUFFLE = 'shuffle';
745
746 DeckSuit.prototype = {
747 __proto__: Array.prototype,
748
749 shuffle: function() {
750 var notifier = Object.getNotifier(this);
751 var self = this;
752 notifier.performChange(DeckSuit.SHUFFLE, function() {
753 self.reverse();
754 self.sort(function() { return Math.random()* 2 - 1; });
755 var cut = self.splice(0, 6);
756 Array.prototype.push.apply(self, cut);
757 self.reverse();
758 self.sort(function() { return Math.random()* 2 - 1; });
759 var cut = self.splice(0, 6);
760 Array.prototype.push.apply(self, cut);
761 self.reverse();
762 self.sort(function() { return Math.random()* 2 - 1; });
763 });
764
765 notifier.notify({
766 type: DeckSuit.SHUFFLE
767 });
768 },
769 }
770
771 DeckSuit.observe = function(thingy, callback) {
772 Object.observe(thingy, callback, [DeckSuit.SHUFFLE]);
773 }
774
775 DeckSuit.unobserve = function(thingy, callback) {
776 Object.unobserve(thingy);
777 }
778
779 var deck = new DeckSuit;
780
781 DeckSuit.observe(deck, observer2.callback);
782 deck.shuffle();
783
784 Object.deliverChangeRecords(observer2.callback);
785 observer2.assertCallbackRecords([
786 { object: deck, type: DeckSuit.SHUFFLE }
787 ]);
788
789 // Observing multiple objects; records appear in order.
790 reset();
791 var obj2 = {};
792 var obj3 = {}
793 Object.observe(obj, observer.callback);
794 Object.observe(obj3, observer.callback);
795 Object.observe(obj2, observer.callback);
796 Object.getNotifier(obj).notify({
797 type: 'add',
798 });
799 Object.getNotifier(obj2).notify({
800 type: 'update',
801 });
802 Object.getNotifier(obj3).notify({
803 type: 'delete',
804 });
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' }
811 ]);
812
813
814 // Recursive observation.
815 var obj = {a: 1};
816 var callbackCount = 0;
817 function recursiveObserver(r) {
818 assertEquals(1, r.length);
819 ++callbackCount;
820 if (r[0].oldValue < 100) ++obj[r[0].name];
821 }
822 Object.observe(obj, recursiveObserver);
823 ++obj.a;
824 Object.deliverChangeRecords(recursiveObserver);
825 assertEquals(100, callbackCount);
826
827 var obj1 = {a: 1};
828 var obj2 = {a: 1};
829 var recordCount = 0;
830 function recursiveObserver2(r) {
831 recordCount += r.length;
832 if (r[0].oldValue < 100) {
833 ++obj1.a;
834 ++obj2.a;
835 }
836 }
837 Object.observe(obj1, recursiveObserver2);
838 Object.observe(obj2, recursiveObserver2);
839 ++obj1.a;
840 Object.deliverChangeRecords(recursiveObserver2);
841 assertEquals(199, recordCount);
842
843
844 // Observing named properties.
845 reset();
846 var obj = {a: 1}
847 Object.observe(obj, observer.callback);
848 obj.a = 2;
849 obj["a"] = 3;
850 delete obj.a;
851 obj.a = 4;
852 obj.a = 4; // ignored
853 obj.a = 5;
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});
865 delete obj.a;
866 delete obj.a;
867 Object.defineProperty(obj, "a", {get: function() {}, configurable: true});
868 Object.defineProperty(obj, "a", {value: 9, writable: true});
869 obj.a = 10;
870 ++obj.a;
871 obj.a++;
872 obj.a *= 3;
873 delete obj.a;
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" },
899 ]);
900
901
902 // Observing indexed properties.
903 reset();
904 var obj = {'1': 1}
905 Object.observe(obj, observer.callback);
906 obj[1] = 2;
907 obj[1] = 3;
908 delete obj[1];
909 obj[1] = 4;
910 obj[1] = 4; // ignored
911 obj[1] = 5;
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});
923 delete obj[1];
924 delete obj[1];
925 Object.defineProperty(obj, "1", {get: function() {}, configurable: true});
926 Object.defineProperty(obj, "1", {value: 9, writable: true});
927 obj[1] = 10;
928 ++obj[1];
929 obj[1]++;
930 obj[1] *= 3;
931 delete obj[1];
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" },
957 ]);
958
959
960 // Observing symbol properties (not).
961 print("*****")
962 reset();
963 var obj = {}
964 var symbol = Symbol("secret");
965 Object.observe(obj, observer.callback);
966 obj[symbol] = 3;
967 delete obj[symbol];
968 Object.defineProperty(obj, symbol, {get: function() {}, configurable: true});
969 Object.defineProperty(obj, symbol, {value: 6});
970 Object.defineProperty(obj, symbol, {writable: false});
971 delete obj[symbol];
972 Object.defineProperty(obj, symbol, {value: 7});
973 ++obj[symbol];
974 obj[symbol]++;
975 obj[symbol] *= 3;
976 delete obj[symbol];
977 obj.__defineSetter__(symbol, function() {});
978 obj.__defineGetter__(symbol, function() {});
979 Object.deliverChangeRecords(observer.callback);
980 observer.assertNotCalled();
981
982
983 // Test all kinds of objects generically.
984 function TestObserveConfigurable(obj, prop) {
985 reset();
986 Object.observe(obj, observer.callback);
987 Object.unobserve(obj, observer.callback);
988 obj[prop] = 1;
989 Object.observe(obj, observer.callback);
990 obj[prop] = 2;
991 obj[prop] = 3;
992 delete obj[prop];
993 obj[prop] = 4;
994 obj[prop] = 4; // ignored
995 obj[prop] = 5;
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() {});
1010 delete obj[prop];
1011 delete obj[prop]; // ignored
1012 obj.__defineGetter__(prop, function() {});
1013 delete obj[prop];
1014 Object.defineProperty(obj, prop, {get: function() {}, configurable: true});
1015 Object.defineProperty(obj, prop, {value: 9, writable: true});
1016 obj[prop] = 10;
1017 ++obj[prop];
1018 obj[prop]++;
1019 obj[prop] *= 3;
1020 delete obj[prop];
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" },
1050 ]);
1051 Object.unobserve(obj, observer.callback);
1052 delete obj[prop];
1053 }
1054
1055 function TestObserveNonConfigurable(obj, prop, desc) {
1056 reset();
1057 Object.observe(obj, observer.callback);
1058 Object.unobserve(obj, observer.callback);
1059 obj[prop] = 1;
1060 Object.observe(obj, observer.callback);
1061 obj[prop] = 4;
1062 obj[prop] = 4; // ignored
1063 obj[prop] = 5;
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" },
1077 ]);
1078 Object.unobserve(obj, observer.callback);
1079 }
1080
1081 // TODO(rafaelw) Enable when ES6 Proxies are implemented
1082 /*
1083 function createProxy(create, x) {
1084 var handler = {
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;
1089 }
1090 return undefined;
1091 },
1092 getOwnPropertyDescriptor: function(k) {
1093 return Object.getOwnPropertyDescriptor(this.target, k);
1094 },
1095 defineProperty: function(k, desc) {
1096 var x = Object.defineProperty(this.target, k, desc);
1097 Object.deliverChangeRecords(this.callback);
1098 return x;
1099 },
1100 delete: function(k) {
1101 var x = delete this.target[k];
1102 Object.deliverChangeRecords(this.callback);
1103 return x;
1104 },
1105 getPropertyNames: function() {
1106 return Object.getOwnPropertyNames(this.target);
1107 },
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);
1116 }
1117 },
1118 };
1119 Object.observe(handler.target, handler.callback);
1120 return handler.proxy = create(handler, x);
1121 }
1122 */
1123
1124 var objects = [
1125 {},
1126 [],
1127 function(){},
1128 (function(){ return arguments })(),
1129 (function(){ "use strict"; return arguments })(),
1130 Object(1), Object(true), Object("bla"),
1131 new Date(),
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(){}),
1138 ];
1139 var properties = ["a", "1", 1, "length", "setPrototype", "name", "caller"];
1140
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.
1148 }
1149
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);
1160 }
1161
1162
1163 // Observing array length (including truncation)
1164 reset();
1165 var arr = ['a', 'b', 'c', 'd'];
1166 var arr2 = ['alpha', 'beta'];
1167 var arr3 = ['hello'];
1168 arr3[2] = 'goodbye';
1169 arr3.length = 6;
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);
1179 arr.length = 2;
1180 arr.length = 0;
1181 arr.length = 10;
1182 Object.defineProperty(arr, 'length', {writable: false});
1183 arr2.length = 0;
1184 arr2.length = 1; // no change expected
1185 Object.defineProperty(arr2, 'length', {value: 1, writable: false});
1186 arr3.length = 0;
1187 ++arr3.length;
1188 arr3.length++;
1189 arr3.length /= 2;
1190 Object.defineProperty(arr3, 'length', {value: 5});
1191 arr3[4] = 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' }
1220 ]);
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 }
1234 ]);
1235
1236
1237 // Updating length on large (slow) array
1238 reset();
1239 var slow_arr = %NormalizeElements([]);
1240 slow_arr[500000000] = 'hello';
1241 slow_arr.length = 1000000000;
1242 Object.observe(slow_arr, observer.callback);
1243 var spliceRecords;
1244 function slowSpliceCallback(records) {
1245 spliceRecords = records;
1246 }
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 },
1253 ]);
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);
1267
1268
1269 // Assignments in loops (checking different IC states).
1270 reset();
1271 var obj = {};
1272 Object.observe(obj, observer.callback);
1273 for (var i = 0; i < 5; i++) {
1274 obj["a" + i] = i;
1275 }
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" },
1283 ]);
1284
1285 reset();
1286 var obj = {};
1287 Object.observe(obj, observer.callback);
1288 for (var i = 0; i < 5; i++) {
1289 obj[i] = i;
1290 }
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" },
1298 ]);
1299
1300
1301 // Adding elements past the end of an array should notify on length for
1302 // Object.observe and emit "splices" for Array.observe.
1303 reset();
1304 var arr = [1, 2, 3];
1305 Object.observe(arr, observer.callback);
1306 Array.observe(arr, observer2.callback);
1307 arr[3] = 10;
1308 arr[100] = 20;
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' },
1323 ]);
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' },
1331 ]);
1332
1333
1334 // Tests for array methods, first on arrays and then on plain objects
1335 //
1336 // === ARRAYS ===
1337 //
1338 // Push
1339 reset();
1340 var array = [1, 2];
1341 Object.observe(array, observer.callback);
1342 Array.observe(array, observer2.callback);
1343 array.push(3, 4);
1344 array.push(5);
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 },
1353 ]);
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 }
1358 ]);
1359
1360 // Pop
1361 reset();
1362 var array = [1, 2];
1363 Object.observe(array, observer.callback);
1364 array.pop();
1365 array.pop();
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 },
1372 ]);
1373
1374 // Shift
1375 reset();
1376 var array = [1, 2];
1377 Object.observe(array, observer.callback);
1378 array.shift();
1379 array.shift();
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 },
1387 ]);
1388
1389 // Unshift
1390 reset();
1391 var array = [1, 2];
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 },
1401 ]);
1402
1403 // Splice
1404 reset();
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 },
1414 ]);
1415
1416 // Sort
1417 reset();
1418 var array = [3, 2, 1];
1419 Object.observe(array, observer.callback);
1420 array.sort();
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 },
1431 ]);
1432
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];
1437
1438 this.length_ = initial.length;
1439 this.observer = observer;
1440 }
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']);
1446 },
1447 get length() {
1448 return this.length_;
1449 }
1450 }
1451
1452 reset();
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 },
1460 ]);
1461
1462 reset();
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 },
1470 ]);
1471
1472 reset();
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 },
1480 ]);
1481
1482 reset();
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 },
1490 ]);
1491
1492 reset();
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 },
1500 ]);
1501
1502 //
1503 // === PLAIN OBJECTS ===
1504 //
1505 // Push
1506 reset()
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 },
1515 ]);
1516
1517 // Pop
1518 reset();
1519 var array = [1, 2];
1520 Object.observe(array, observer.callback);
1521 Array.observe(array, observer2.callback);
1522 array.pop();
1523 array.pop();
1524 array.pop();
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 },
1531 ]);
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 }
1536 ]);
1537
1538 // Shift
1539 reset();
1540 var array = [1, 2];
1541 Object.observe(array, observer.callback);
1542 Array.observe(array, observer2.callback);
1543 array.shift();
1544 array.shift();
1545 array.shift();
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 },
1553 ]);
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 }
1558 ]);
1559
1560 // Unshift
1561 reset();
1562 var array = [1, 2];
1563 Object.observe(array, observer.callback);
1564 Array.observe(array, observer2.callback);
1565 array.unshift(3, 4);
1566 array.unshift(5);
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 },
1580 ]);
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 }
1585 ]);
1586
1587 // Splice
1588 reset();
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
1595 array.splice(2, 0);
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 },
1603
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 },
1610
1611 { object: array, name: '1', type: 'update', oldValue: 2 },
1612 { object: array, name: '2', type: 'update', oldValue: 3 },
1613 ]);
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 },
1619 ]);
1620
1621 // Exercise StoreIC_ArrayLength
1622 reset();
1623 var dummy = {};
1624 Object.observe(dummy, observer.callback);
1625 Object.unobserve(dummy, observer.callback);
1626 var array = [0];
1627 Object.observe(array, observer.callback);
1628 array.splice(0, 1);
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},
1633 ]);
1634
1635
1636 // __proto__
1637 reset();
1638 var obj = {};
1639 Object.observe(obj, observer.callback);
1640 var p = {foo: 'yes'};
1641 var q = {bar: 'no'};
1642 obj.__proto__ = p;
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' },
1654 ]);
1655
1656
1657 // Function.prototype
1658 reset();
1659 var fun = function(){};
1660 Object.observe(fun, observer.callback);
1661 var myproto = {foo: 'bar'};
1662 fun.prototype = myproto;
1663 fun.prototype = 7;
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 },
1682 ]);
1683
1684 // Function.prototype should not be observable except on the object itself
1685 reset();
1686 var fun = function(){};
1687 var obj = { __proto__: fun };
1688 Object.observe(obj, observer.callback);
1689 obj.prototype = 7;
1690 Object.deliverChangeRecords(observer.callback);
1691 observer.assertRecordCount(1);
1692 observer.assertCallbackRecords([
1693 { object: obj, name: 'prototype', type: 'add' },
1694 ]);
1695
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.
1698 var mutation = [
1699 "a[i] = v",
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",
1704 ];
1705
1706 var props = [1, "1", "a"];
1707
1708 function TestFastElements(prop, mutation, prepopulate, polymorphic, optimize) {
1709 var setElement = eval(
1710 "(function setElement(a, i, v) { " + mutation + "; " +
1711 "/* " + [].join.call(arguments, " ") + " */" +
1712 "})"
1713 );
1714 print("TestFastElements:", setElement);
1715
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);
1723
1724 reset();
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 }
1730 ]);
1731 }
1732
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);
1739
1740
1741 var mutation = [
1742 "a.length = v",
1743 "a.length += newSize - oldSize",
1744 "a.length -= oldSize - newSize",
1745 ];
1746
1747 var mutationByIncr = [
1748 "++a.length",
1749 "a.length++",
1750 ];
1751
1752 function TestFastElementsLength(
1753 mutation, polymorphic, optimize, oldSize, newSize) {
1754 var setLength = eval(
1755 "(function setLength(a, v) { " + mutation + "; " +
1756 "/* " + [].join.call(arguments, " ") + " */"
1757 + "})"
1758 );
1759 print("TestFastElementsLength:", setLength);
1760
1761 function array(n) {
1762 var arr = new Array(n);
1763 for (var i = 0; i < n; ++i) arr[i] = i;
1764 return arr;
1765 }
1766
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);
1772
1773 reset();
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();
1780 } else {
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);
1788 }
1789 }
1790
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);
1797
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);
1803
1804
1805 (function TestFunctionName() {
1806 reset();
1807
1808 function fun() {}
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});
1813 fun.name = 'b';
1814 delete fun.name;
1815 fun.name = 'x'; // No change. Function.prototype.name is non writable
1816 Object.defineProperty(Function.prototype, 'name', {writable: true});
1817 fun.name = 'c';
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' },
1826 ]);
1827 })();
1828
1829
1830 (function TestFunctionLength() {
1831 reset();
1832
1833 function fun(x) {}
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});
1838 fun.length = 'b';
1839 delete fun.length;
1840 fun.length = 'x'; // No change. Function.prototype.length is non writable
1841 Object.defineProperty(Function.prototype, 'length', {writable: true});
1842 fun.length = 'c';
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' },
1851 ]);
1852 })();
1853
1854
1855 (function TestObserveInvalidAcceptMessage() {
1856 var ex;
1857 try {
1858 Object.observe({}, function(){}, "not an object");
1859 } catch (e) {
1860 ex = e;
1861 }
1862 assertInstanceof(ex, TypeError);
1863 assertEquals("Third argument to Object.observe must be an array of strings.",
1864 ex.message);
1865 })()
OLDNEW
« no previous file with comments | « test/mjsunit/es6/regress/regress-3750.js ('k') | test/mjsunit/es7/object-observe-debug-event.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698