Index: test/mjsunit/harmony/object-observe.js |
diff --git a/test/mjsunit/harmony/object-observe.js b/test/mjsunit/harmony/object-observe.js |
index dbdf84848d14e228e4a11a5ff4df4e6fd5a0cbcd..4a610467ccd0925d6113e9f45b0cde1dd301070f 100644 |
--- a/test/mjsunit/harmony/object-observe.js |
+++ b/test/mjsunit/harmony/object-observe.js |
@@ -25,7 +25,7 @@ |
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
-// Flags: --harmony-observation |
+// Flags: --harmony-observation --harmony-proxies --harmony-collections |
var allObservers = []; |
function reset() { |
@@ -56,6 +56,7 @@ function createObserver() { |
assertCallbackRecords: function(recs) { |
this.assertRecordCount(recs.length); |
for (var i = 0; i < recs.length; i++) { |
+ print(i, JSON.stringify(this.records[i]), JSON.stringify(recs[i])); |
assertSame(this.records[i].object, recs[i].object); |
assertEquals('string', typeof recs[i].type); |
assertPropertiesEqual(this.records[i], recs[i]); |
@@ -349,6 +350,159 @@ observer.assertCallbackRecords([ |
{ object: obj, name: "1", type: "new" }, |
]); |
+ |
+// Test all kinds of objects generically. |
+function TestObserveConfigurable(obj, prop) { |
+ reset(); |
+ obj[prop] = 1; |
+ Object.observe(obj, observer.callback); |
+ obj[prop] = 2; |
+ obj[prop] = 3; |
+ delete obj[prop]; |
+ obj[prop] = 4; |
+ obj[prop] = 4; // ignored |
+ obj[prop] = 5; |
+ Object.defineProperty(obj, prop, {value: 6}); |
+ Object.defineProperty(obj, prop, {writable: false}); |
+ obj[prop] = 7; // ignored |
+ Object.defineProperty(obj, prop, {value: 8}); |
+ Object.defineProperty(obj, prop, {value: 7, writable: true}); |
+ Object.defineProperty(obj, prop, {get: function() {}}); |
+ Object.defineProperty(obj, prop, {get: function() {}}); |
+ delete obj[prop]; |
+ delete obj[prop]; |
+ Object.defineProperty(obj, prop, {get: function() {}, configurable: true}); |
+ Object.defineProperty(obj, prop, {value: 9, writable: true}); |
+ obj[prop] = 10; |
+ delete obj[prop]; |
+ Object.defineProperty(obj, prop, {value: 11, configurable: true}); |
+ Object.deliverChangeRecords(observer.callback); |
+ observer.assertCallbackRecords([ |
+ { object: obj, name: prop, type: "updated", oldValue: 1 }, |
+ { object: obj, name: prop, type: "updated", oldValue: 2 }, |
+ { object: obj, name: prop, type: "deleted", oldValue: 3 }, |
+ { object: obj, name: prop, type: "new" }, |
+ { object: obj, name: prop, type: "updated", oldValue: 4 }, |
+ { object: obj, name: prop, type: "updated", oldValue: 5 }, |
+ { object: obj, name: prop, type: "reconfigured", oldValue: 6 }, |
+ { object: obj, name: prop, type: "updated", oldValue: 6 }, |
+ { object: obj, name: prop, type: "reconfigured", oldValue: 8 }, |
+ { object: obj, name: prop, type: "reconfigured", oldValue: 7 }, |
+ { object: obj, name: prop, type: "reconfigured" }, |
+ { object: obj, name: prop, type: "deleted" }, |
+ { object: obj, name: prop, type: "new" }, |
+ { object: obj, name: prop, type: "reconfigured" }, |
+ { object: obj, name: prop, type: "updated", oldValue: 9 }, |
+ { object: obj, name: prop, type: "deleted", oldValue: 10 }, |
+ { object: obj, name: prop, type: "new" }, |
+ ]); |
+ Object.unobserve(obj, observer.callback); |
+ delete obj[prop]; |
+} |
+ |
+function TestObserveNonConfigurable(obj, prop) { |
+ reset(); |
+ obj[prop] = 1; |
+ Object.observe(obj, observer.callback); |
+ obj[prop] = 4; |
+ obj[prop] = 4; // ignored |
+ obj[prop] = 5; |
+ Object.defineProperty(obj, prop, {value: 6}); |
+ Object.defineProperty(obj, prop, {value: 6}); // ignored |
+ Object.defineProperty(obj, prop, {writable: false}); |
+ obj[prop] = 7; // ignored |
+ Object.deliverChangeRecords(observer.callback); |
+ observer.assertCallbackRecords([ |
+ { object: obj, name: prop, type: "updated", oldValue: 1 }, |
+ { object: obj, name: prop, type: "updated", oldValue: 4 }, |
+ { object: obj, name: prop, type: "updated", oldValue: 5 }, |
+ { object: obj, name: prop, type: "reconfigured", oldValue: 6 }, |
+ ]); |
+ Object.unobserve(obj, observer.callback); |
+} |
+ |
+function createProxy(create, x) { |
+ var handler = { |
+ getPropertyDescriptor: function(k) { |
+ return Object.getOwnPropertyDescriptor(this.target, k); |
+ }, |
+ getOwnPropertyDescriptor: function(k) { |
+ return Object.getOwnPropertyDescriptor(this.target, k); |
+ }, |
+ defineProperty: function(k, desc) { |
+ var x = Object.defineProperty(this.target, k, desc); |
+ Object.deliverChangeRecords(this.callback); |
+ return x; |
+ }, |
+ delete: function(k) { |
+ var x = delete this.target[k]; |
+ Object.deliverChangeRecords(this.callback); |
+ return x; |
+ }, |
+ getPropertyNames: function() { |
+ return Object.getOwnPropertyNames(this.target); |
+ }, |
+ target: {isProxy: true}, |
+ callback: function(changeRecords) { |
+ print("callback", JSON.stringify(handler.proxy), JSON.stringify(got)); |
+ for (var i in changeRecords) { |
+ var got = changeRecords[i]; |
+ var change = {object: handler.proxy, name: got.name, type: got.type}; |
+ if ("oldValue" in got) change.oldValue = got.oldValue; |
+ Object.getNotifier(handler.proxy).notify(change); |
+ } |
+ }, |
+ }; |
+ Object.observe(handler.target, handler.callback); |
+ return handler.proxy = create(handler, x); |
+} |
+ |
+var objects = [ |
+ {}, |
+ [], |
+ this, // global object |
+ function(){}, |
+ (function(){ return arguments })(), |
+ (function(){ "use strict"; return arguments })(), |
+ Object(1), Object(true), Object("bla"), |
+ new Date(), |
+ Object, Function, Date, RegExp, |
+ new Set, new Map, new WeakMap, |
+ new ArrayBuffer(10), new Int32Array(5), |
+ createProxy(Proxy.create, null), |
+ createProxy(Proxy.createFunction, function(){}), |
+]; |
+var properties = ["a", "1", 1, "length", "prototype"]; |
+ |
+// Cases that yield non-standard results. |
+// TODO(observe): ...or don't work yet. |
+function blacklisted(obj, prop) { |
+ return (obj instanceof Array && prop == 1) || |
+ (obj instanceof Int32Array && prop == 1) || |
+ (obj instanceof Int32Array && prop === "length") || |
+ // TODO(observe): oldValue when deleting/reconfiguring indexed accessor |
+ prop == 1 || |
+ // TODO(observe): oldValue when reconfiguring array length |
+ (obj instanceof Array && prop === "length") || |
+ // TODO(observe): prototype property on functions |
+ (obj instanceof Function && prop === "prototype") || |
+ // TODO(observe): global object |
+ obj === this; |
+} |
+ |
+for (var i in objects) for (var j in properties) { |
+ var obj = objects[i]; |
+ var prop = properties[j]; |
+ if (blacklisted(obj, prop)) continue; |
+ var desc = Object.getOwnPropertyDescriptor(obj, prop); |
+ print("***", typeof obj, JSON.stringify(obj), prop); |
+ if (!desc || desc.configurable) |
+ TestObserveConfigurable(obj, prop); |
+ else if (desc.writable) |
+ TestObserveNonConfigurable(obj, prop); |
+} |
+ |
+ |
// Observing array length (including truncation) |
reset(); |
var arr = ['a', 'b', 'c', 'd']; |