OLD | NEW |
---|---|
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
11 // with the distribution. | 11 // with the distribution. |
12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
15 // | 15 // |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 // Flags: --harmony-observation | 28 // Flags: --harmony-observation --harmony-proxies |
29 | 29 |
30 var allObservers = []; | 30 var allObservers = []; |
31 function reset() { | 31 function reset() { |
32 allObservers.forEach(function(observer) { observer.reset(); }); | 32 allObservers.forEach(function(observer) { observer.reset(); }); |
33 } | 33 } |
34 | 34 |
35 function createObserver() { | 35 function createObserver() { |
36 "use strict"; // So that |this| in callback can be undefined. | 36 "use strict"; // So that |this| in callback can be undefined. |
37 | 37 |
38 var observer = { | 38 var observer = { |
(...skipping 10 matching lines...) Expand all Loading... | |
49 assertCalled: function() { | 49 assertCalled: function() { |
50 assertEquals(1, this.callbackCount); | 50 assertEquals(1, this.callbackCount); |
51 }, | 51 }, |
52 assertRecordCount: function(count) { | 52 assertRecordCount: function(count) { |
53 this.assertCalled(); | 53 this.assertCalled(); |
54 assertEquals(count, this.records.length); | 54 assertEquals(count, this.records.length); |
55 }, | 55 }, |
56 assertCallbackRecords: function(recs) { | 56 assertCallbackRecords: function(recs) { |
57 this.assertRecordCount(recs.length); | 57 this.assertRecordCount(recs.length); |
58 for (var i = 0; i < recs.length; i++) { | 58 for (var i = 0; i < recs.length; i++) { |
59 print(i, JSON.stringify(this.records[i]), JSON.stringify(recs[i])); | |
rafaelw
2012/11/20 05:11:07
did you intend to leave this in?
rossberg
2012/11/20 10:34:27
Yes. I was taking this in and out so many times in
| |
59 assertSame(this.records[i].object, recs[i].object); | 60 assertSame(this.records[i].object, recs[i].object); |
60 assertEquals('string', typeof recs[i].type); | 61 assertEquals('string', typeof recs[i].type); |
61 assertPropertiesEqual(this.records[i], recs[i]); | 62 assertPropertiesEqual(this.records[i], recs[i]); |
62 } | 63 } |
63 } | 64 } |
64 }; | 65 }; |
65 | 66 |
66 observer.callback = function(r) { | 67 observer.callback = function(r) { |
67 assertEquals(undefined, this); | 68 assertEquals(undefined, this); |
68 assertEquals('object', typeof r); | 69 assertEquals('object', typeof r); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
342 // TODO(observe): oldValue should not be present below. | 343 // TODO(observe): oldValue should not be present below. |
343 { object: obj, name: "1", type: "deleted", oldValue: undefined }, | 344 { object: obj, name: "1", type: "deleted", oldValue: undefined }, |
344 { object: obj, name: "1", type: "new" }, | 345 { object: obj, name: "1", type: "new" }, |
345 // TODO(observe): oldValue should be absent below, and type = "reconfigured". | 346 // TODO(observe): oldValue should be absent below, and type = "reconfigured". |
346 { object: obj, name: "1", type: "updated", oldValue: undefined }, | 347 { object: obj, name: "1", type: "updated", oldValue: undefined }, |
347 { object: obj, name: "1", type: "updated", oldValue: 9 }, | 348 { object: obj, name: "1", type: "updated", oldValue: 9 }, |
348 { object: obj, name: "1", type: "deleted", oldValue: 10 }, | 349 { object: obj, name: "1", type: "deleted", oldValue: 10 }, |
349 { object: obj, name: "1", type: "new" }, | 350 { object: obj, name: "1", type: "new" }, |
350 ]); | 351 ]); |
351 | 352 |
353 | |
354 // Test all kinds of objects generically. | |
355 function TestObserveConfigurable(obj, prop) { | |
356 reset(); | |
357 obj[prop] = 1; | |
358 Object.observe(obj, observer.callback); | |
359 obj[prop] = 2; | |
360 obj[prop] = 3; | |
361 delete obj[prop]; | |
362 obj[prop] = 4; | |
363 obj[prop] = 4; // ignored | |
364 obj[prop] = 5; | |
365 Object.defineProperty(obj, prop, {value: 6}); | |
366 Object.defineProperty(obj, prop, {writable: false}); | |
367 obj[prop] = 7; // ignored | |
368 Object.defineProperty(obj, prop, {value: 8}); | |
369 Object.defineProperty(obj, prop, {value: 7, writable: true}); | |
370 Object.defineProperty(obj, prop, {get: function() {}}); | |
371 Object.defineProperty(obj, prop, {get: function() {}}); | |
372 delete obj[prop]; | |
373 delete obj[prop]; | |
374 Object.defineProperty(obj, prop, {get: function() {}, configurable: true}); | |
375 Object.defineProperty(obj, prop, {value: 9, writable: true}); | |
376 obj[prop] = 10; | |
377 delete obj[prop]; | |
378 Object.defineProperty(obj, prop, {value: 11, configurable: true}); | |
379 Object.deliverChangeRecords(observer.callback); | |
380 observer.assertCallbackRecords([ | |
381 { object: obj, name: prop, type: "updated", oldValue: 1 }, | |
382 { object: obj, name: prop, type: "updated", oldValue: 2 }, | |
383 { object: obj, name: prop, type: "deleted", oldValue: 3 }, | |
384 { object: obj, name: prop, type: "new" }, | |
385 { object: obj, name: prop, type: "updated", oldValue: 4 }, | |
386 { object: obj, name: prop, type: "updated", oldValue: 5 }, | |
387 { object: obj, name: prop, type: "reconfigured", oldValue: 6 }, | |
388 { object: obj, name: prop, type: "updated", oldValue: 6 }, | |
389 { object: obj, name: prop, type: "reconfigured", oldValue: 8 }, | |
390 { object: obj, name: prop, type: "reconfigured", oldValue: 7 }, | |
391 { object: obj, name: prop, type: "reconfigured" }, | |
392 { object: obj, name: prop, type: "deleted" }, | |
393 { object: obj, name: prop, type: "new" }, | |
394 { object: obj, name: prop, type: "reconfigured" }, | |
395 { object: obj, name: prop, type: "updated", oldValue: 9 }, | |
396 { object: obj, name: prop, type: "deleted", oldValue: 10 }, | |
397 { object: obj, name: prop, type: "new" }, | |
398 ]); | |
399 Object.unobserve(obj, observer.callback); | |
400 delete obj[prop]; | |
401 } | |
402 | |
403 function TestObserveNonConfigurable(obj, prop) { | |
404 reset(); | |
405 obj[prop] = 1; | |
406 Object.observe(obj, observer.callback); | |
407 obj[prop] = 4; | |
408 obj[prop] = 4; // ignored | |
409 obj[prop] = 5; | |
410 Object.defineProperty(obj, prop, {value: 6}); | |
411 Object.defineProperty(obj, prop, {value: 6}); // ignored | |
412 Object.defineProperty(obj, prop, {writable: false}); | |
413 obj[prop] = 7; // ignored | |
414 Object.deliverChangeRecords(observer.callback); | |
415 observer.assertCallbackRecords([ | |
416 { object: obj, name: prop, type: "updated", oldValue: 1 }, | |
417 { object: obj, name: prop, type: "updated", oldValue: 4 }, | |
418 { object: obj, name: prop, type: "updated", oldValue: 5 }, | |
419 { object: obj, name: prop, type: "reconfigured", oldValue: 6 }, | |
420 ]); | |
421 Object.unobserve(obj, observer.callback); | |
422 } | |
423 | |
424 function createProxy(create, x) { | |
425 var handler = { | |
426 getPropertyDescriptor: function(k) { | |
427 var x = Object.getOwnPropertyDescriptor(this.target, k); | |
428 Object.deliverChangeRecords(this.callback); | |
rafaelw
2012/11/20 05:11:07
In what case will this generate a changeRecord?
rossberg
2012/11/20 10:34:27
You are right, dumb copy & paste. Shouldn't be nee
| |
429 return x; | |
430 }, | |
431 getOwnPropertyDescriptor: function(k) { | |
432 var x = Object.getOwnPropertyDescriptor(this.target, k); | |
433 Object.deliverChangeRecords(this.callback); | |
rafaelw
2012/11/20 05:11:07
same question?
rossberg
2012/11/20 10:34:27
Ditto.
| |
434 return x; | |
435 }, | |
436 defineProperty: function(k, desc) { | |
437 var x = Object.defineProperty(this.target, k, desc); | |
rafaelw
2012/11/20 05:11:07
how much of direct proxies are implemented? i woul
rossberg
2012/11/20 10:34:27
'set' is a derived trap in the old semantics, whic
| |
438 Object.deliverChangeRecords(this.callback); | |
439 return x; | |
440 }, | |
441 delete: function(k) { | |
442 var x = delete this.target[k]; | |
443 Object.deliverChangeRecords(this.callback); | |
444 return x; | |
445 }, | |
446 getPropertyNames: function() { | |
447 return Object.getOwnPropertyNames(this.target); | |
448 }, | |
449 target: {isProxy: true}, | |
450 callback: function(changeRecords) { | |
451 print("callback", JSON.stringify(handler.proxy), JSON.stringify(got)); | |
452 for (var i in changeRecords) { | |
453 var got = changeRecords[i]; | |
454 var change = {object: handler.proxy, name: got.name, type: got.type}; | |
455 if ("oldValue" in got) change.oldValue = got.oldValue; | |
456 Object.getNotifier(handler.proxy).notify(change); | |
457 } | |
458 }, | |
459 }; | |
460 Object.observe(handler.target, handler.callback); | |
461 return handler.proxy = create(handler, x); | |
462 } | |
463 | |
464 var objects = [ | |
465 {}, | |
466 [], | |
467 this, // global object | |
468 function(){}, | |
469 (function(){ return arguments })(), | |
470 (function(){ "use strict"; return arguments })(), | |
471 Object(1), Object(true), Object("bla"), | |
472 new Date(), | |
473 Object, Function, Date, RegExp, | |
474 new ArrayBuffer(10), new Int32Array(5), | |
rafaelw
2012/11/20 05:11:07
new Map, new Set, new WeakMap?
rossberg
2012/11/20 10:34:27
Done.
| |
475 createProxy(Proxy.create, null), | |
476 createProxy(Proxy.createFunction, function(){}), | |
477 ]; | |
478 var properties = ["a", "1", 1, "length", "prototype"]; | |
479 | |
480 // Cases that yield non-standard results. | |
481 // TODO(observe): ...or don't work yet. | |
482 function blacklisted(obj, prop) { | |
483 return (obj instanceof Array && prop == 1) || | |
484 (obj instanceof Int32Array && prop == 1) || | |
485 (obj instanceof Int32Array && prop === "length") || | |
486 // TODO(observe): oldValue when deleting/reconfiguring indexed accessor | |
487 prop == 1 || | |
488 // TODO(observe): oldValue when reconfiguring array length | |
489 (obj instanceof Array && prop === "length") || | |
490 // TODO(observe): prototype property on functions | |
491 (obj instanceof Function && prop === "prototype") || | |
492 // TODO(observe): global object | |
493 obj === this; | |
494 } | |
495 | |
496 for (var i in objects) for (var j in properties) { | |
497 var obj = objects[i]; | |
498 var prop = properties[j]; | |
499 if (blacklisted(obj, prop)) continue; | |
500 var desc = Object.getOwnPropertyDescriptor(obj, prop); | |
501 print("***", typeof obj, JSON.stringify(obj), prop); | |
502 if (!desc || desc.configurable) | |
503 TestObserveConfigurable(obj, prop); | |
504 else if (desc.writable) | |
505 TestObserveNonConfigurable(obj, prop); | |
506 } | |
507 | |
508 | |
352 // Observing array length (including truncation) | 509 // Observing array length (including truncation) |
353 reset(); | 510 reset(); |
354 var arr = ['a', 'b', 'c', 'd']; | 511 var arr = ['a', 'b', 'c', 'd']; |
355 var arr2 = ['alpha', 'beta']; | 512 var arr2 = ['alpha', 'beta']; |
356 var arr3 = ['hello']; | 513 var arr3 = ['hello']; |
357 arr3[2] = 'goodbye'; | 514 arr3[2] = 'goodbye'; |
358 arr3.length = 6; | 515 arr3.length = 6; |
359 // TODO(adamk): Enable this test case when it can run in a reasonable | 516 // TODO(adamk): Enable this test case when it can run in a reasonable |
360 // amount of time. | 517 // amount of time. |
361 //var slow_arr = new Array(1000000000); | 518 //var slow_arr = new Array(1000000000); |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
583 var array = {0: 1, 1: 2, 2: 3, length: 3}; | 740 var array = {0: 1, 1: 2, 2: 3, length: 3}; |
584 Object.observe(array, observer.callback); | 741 Object.observe(array, observer.callback); |
585 Array.prototype.splice.call(array, 1, 1, 4, 5); | 742 Array.prototype.splice.call(array, 1, 1, 4, 5); |
586 Object.deliverChangeRecords(observer.callback); | 743 Object.deliverChangeRecords(observer.callback); |
587 observer.assertCallbackRecords([ | 744 observer.assertCallbackRecords([ |
588 { object: array, name: '3', type: 'new' }, | 745 { object: array, name: '3', type: 'new' }, |
589 { object: array, name: '1', type: 'updated', oldValue: 2 }, | 746 { object: array, name: '1', type: 'updated', oldValue: 2 }, |
590 { object: array, name: '2', type: 'updated', oldValue: 3 }, | 747 { object: array, name: '2', type: 'updated', oldValue: 3 }, |
591 { object: array, name: 'length', type: 'updated', oldValue: 3 }, | 748 { object: array, name: 'length', type: 'updated', oldValue: 3 }, |
592 ]); | 749 ]); |
OLD | NEW |