diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/jit-test/tests/proxy | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/jit-test/tests/proxy')
127 files changed, 2387 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/proxy/bug-862848-1.js b/js/src/jit-test/tests/proxy/bug-862848-1.js new file mode 100644 index 000000000..870b5632c --- /dev/null +++ b/js/src/jit-test/tests/proxy/bug-862848-1.js @@ -0,0 +1,24 @@ +// obj.hasOwnProperty(id), Object.getOwnPropertyDescriptor(obj, id), and +// Object.defineProperty(obj, id, desc) do not look at obj's prototype. + +var angryHandler = new Proxy({}, { + has: () => true, + get: (t, id) => { + throw new Error("angryHandler should not be queried (" + id + ")"); + } +}); +var angryProto = new Proxy({}, angryHandler); + +var obj = Object.create(angryProto, { + // Define hasOwnProperty directly on obj since we are poisoning its + // prototype chain. + hasOwnProperty: { + value: Object.prototype.hasOwnProperty + } +}); + +assertEq(Object.getOwnPropertyDescriptor(obj, "foo"), undefined); +assertEq(obj.hasOwnProperty("foo"), false); +Object.defineProperty(obj, "foo", {value: 5}); +assertEq(obj.hasOwnProperty("foo"), true); +assertEq(obj.foo, 5); diff --git a/js/src/jit-test/tests/proxy/bug1072817.js b/js/src/jit-test/tests/proxy/bug1072817.js new file mode 100644 index 000000000..d8656e0c7 --- /dev/null +++ b/js/src/jit-test/tests/proxy/bug1072817.js @@ -0,0 +1,5 @@ +// |jit-test| error: TypeError +var r = Proxy.revocable({}, {}); +var p = r.proxy; +r.revoke(); +p instanceof Object; diff --git a/js/src/jit-test/tests/proxy/bug1095973.js b/js/src/jit-test/tests/proxy/bug1095973.js new file mode 100644 index 000000000..6e917a16c --- /dev/null +++ b/js/src/jit-test/tests/proxy/bug1095973.js @@ -0,0 +1,5 @@ +var C = {}; +var B = new Proxy(C, {}); +var A = Object.create(B); +B.x = 1; +assertEq(C.x, 1); diff --git a/js/src/jit-test/tests/proxy/bug897403.js b/js/src/jit-test/tests/proxy/bug897403.js new file mode 100644 index 000000000..45b743eac --- /dev/null +++ b/js/src/jit-test/tests/proxy/bug897403.js @@ -0,0 +1,3 @@ +var f = (function () {}).bind({}); +var p = new Proxy(f, {}); +Object.defineProperty(p, "caller", {get: function(){}}); diff --git a/js/src/jit-test/tests/proxy/bug901979-1.js b/js/src/jit-test/tests/proxy/bug901979-1.js new file mode 100644 index 000000000..f6432a838 --- /dev/null +++ b/js/src/jit-test/tests/proxy/bug901979-1.js @@ -0,0 +1,16 @@ +// A proxy on the prototype chain of the global can't intercept lazy definition of globals. +// Thanks to André Bargull for this one. +load(libdir + "immutable-prototype.js"); + +var global = this; +var status = "pass"; +var handler = { + get: function get(t, pk, r) { status = "FAIL get"; }, + has: function has(t, pk) { status = "FAIL has"; } +}; + +if (globalPrototypeChainIsMutable()) + Object.prototype.__proto__ = new Proxy(Object.create(null), handler); + +Map; +assertEq(status, "pass"); diff --git a/js/src/jit-test/tests/proxy/bug901979-2.js b/js/src/jit-test/tests/proxy/bug901979-2.js new file mode 100644 index 000000000..93f45dc89 --- /dev/null +++ b/js/src/jit-test/tests/proxy/bug901979-2.js @@ -0,0 +1,37 @@ +// A proxy on the prototype chain of the global should not observe anything at +// all about lazy resolution of globals. +load(libdir + "immutable-prototype.js"); + +var global = this; +var status = "pass"; + +// This is a little tricky. There are two proxies. +// 1. handler is a proxy that fails the test if you try to call a method on it. +var metaHandler = { + get: _ => { status = "SMASH"; }, + has: _ => { status = "SMASH"; }, + invoke: _ => { status = "SMASH"; } +}; +var handler = new Proxy({}, metaHandler); + +// 2. Then we create a proxy using 'handler' as its handler. This means the test +// will fail if *any* method of the handler is called, not just get/has/invoke. +var angryProxy = new Proxy(Object.create(null), handler); +if (globalPrototypeChainIsMutable()) { + this.__proto__ = angryProxy; + Object.prototype.__proto__ = angryProxy; +} + +// Trip the alarm once, to make sure the proxies are working. +this.nonExistingProperty; +if (globalPrototypeChainIsMutable()) + assertEq(status, "SMASH"); +else + assertEq(status, "pass"); + +// OK. Reset the status and run the actual test. +status = "pass"; +Map; +ArrayBuffer; +Date; +assertEq(status, "pass"); diff --git a/js/src/jit-test/tests/proxy/bug911553.js b/js/src/jit-test/tests/proxy/bug911553.js new file mode 100644 index 000000000..5ccfbe21d --- /dev/null +++ b/js/src/jit-test/tests/proxy/bug911553.js @@ -0,0 +1,10 @@ +assertEq( + ""+new Proxy( + {toString:() => "inner toString"}, + {get:(t, pk) => (pk === "toString" ? () => "proxy toString" : t[pk])}), + "proxy toString") +assertEq( + ""+new Proxy( + {valueOf:() => "inner valueOf"}, + {get:(t, pk) => (pk === "valueOf" ? () => "proxy valueOf" : t[pk])}), + "proxy valueOf") diff --git a/js/src/jit-test/tests/proxy/defineProperty-fallback.js b/js/src/jit-test/tests/proxy/defineProperty-fallback.js new file mode 100644 index 000000000..e8f52dc38 --- /dev/null +++ b/js/src/jit-test/tests/proxy/defineProperty-fallback.js @@ -0,0 +1,8 @@ +"use strict"; +var obj = {}; +Object.defineProperty(obj, "test", {configurable: false, writable: false, value: "hey"}); +Object.defineProperty(obj, "test", {configurable: false, writable: false}); + +var wrapper = new Proxy(obj, {}); +Object.defineProperty(wrapper, "test", {configurable: false, writable: false}); +assertEq(wrapper.test, "hey"); diff --git a/js/src/jit-test/tests/proxy/delete-not-invoked-on-proto.js b/js/src/jit-test/tests/proxy/delete-not-invoked-on-proto.js new file mode 100644 index 000000000..1de2eaef0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/delete-not-invoked-on-proto.js @@ -0,0 +1,10 @@ +// Create Proxy that throws for everything. +var {proxy, revoke} = Proxy.revocable({}, {}); + +var obj = {__proto__: proxy, a: 1}; +// This revokes the proxy, so every operation on it THROWS. +revoke(); + +assertEq(delete obj.a, true); +assertEq(delete obj.b, true); +// Should not have invoked anything on [[Prototype]] diff --git a/js/src/jit-test/tests/proxy/freeze-proxy.js b/js/src/jit-test/tests/proxy/freeze-proxy.js new file mode 100644 index 000000000..ff82c50fd --- /dev/null +++ b/js/src/jit-test/tests/proxy/freeze-proxy.js @@ -0,0 +1,22 @@ +var called = []; +var proxy = new Proxy({a: 1, get b() {}}, { + getOwnPropertyDescriptor(target, P) { + called.push("getOwnPropertyDescriptor"); + return Object.getOwnPropertyDescriptor(target, P); + }, + defineProperty(target, P, desc) { + called.push("defineProperty"); + if (P == "a") { + assertEq(Object.getOwnPropertyNames(desc).length, 2); + assertEq(desc.configurable, false); + assertEq(desc.writable, false); + } else { + assertEq(Object.getOwnPropertyNames(desc).length, 1); + assertEq(desc.configurable, false); + } + return Object.defineProperty(target, P, desc); + } +}); + +Object.freeze(proxy); +assertEq(called.toString(), "getOwnPropertyDescriptor,defineProperty,getOwnPropertyDescriptor,defineProperty"); diff --git a/js/src/jit-test/tests/proxy/function-toString.js b/js/src/jit-test/tests/proxy/function-toString.js new file mode 100644 index 000000000..cedcf552a --- /dev/null +++ b/js/src/jit-test/tests/proxy/function-toString.js @@ -0,0 +1,10 @@ +load(libdir + 'asserts.js'); + +// Function.prototype.toString doesn't accept ES6 proxies. + +var proxy = new Proxy(function() {}, {}); +assertThrowsInstanceOf(() => Function.prototype.toString.call(proxy), TypeError); +var o = Proxy.revocable(function() {}, {}); +assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError); +o.revoke(); +assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError); diff --git a/js/src/jit-test/tests/proxy/getElementIfPresent-not-present.js b/js/src/jit-test/tests/proxy/getElementIfPresent-not-present.js new file mode 100644 index 000000000..ccfb92d8c --- /dev/null +++ b/js/src/jit-test/tests/proxy/getElementIfPresent-not-present.js @@ -0,0 +1,5 @@ +x = (/x/).exec(); +y = wrapWithProto((new WeakMap), x); +y.length = 7; +Array.prototype.push.call(y, 1); +Array.prototype.reverse.call(y); diff --git a/js/src/jit-test/tests/proxy/operations-on-revoked.js b/js/src/jit-test/tests/proxy/operations-on-revoked.js new file mode 100644 index 000000000..181f4c6fc --- /dev/null +++ b/js/src/jit-test/tests/proxy/operations-on-revoked.js @@ -0,0 +1,18 @@ +load(libdir + 'asserts.js'); + +var r = Proxy.revocable({}, {}); +var r2 = Proxy.revocable(function(){}, {}); +r.revoke(); +r2.revoke(); + +var p = r.proxy; +var p2 = r2.proxy; + +assertThrowsInstanceOf(() => ({} instanceof p), TypeError); +assertThrowsInstanceOf(() => ({} instanceof p2), TypeError); + +assertThrowsInstanceOf(() => Object.prototype.toString.call(p), TypeError); +assertThrowsInstanceOf(() => Object.prototype.toString.call(p2), TypeError); + +assertThrowsInstanceOf(() => RegExp.prototype.exec.call(p, ""), TypeError); +assertThrowsInstanceOf(() => RegExp.prototype.exec.call(p2, ""), TypeError); diff --git a/js/src/jit-test/tests/proxy/preserve-iscallable-isconstructor.js b/js/src/jit-test/tests/proxy/preserve-iscallable-isconstructor.js new file mode 100644 index 000000000..2ffded36c --- /dev/null +++ b/js/src/jit-test/tests/proxy/preserve-iscallable-isconstructor.js @@ -0,0 +1,17 @@ +load(libdir + "asserts.js"); + +var global = newGlobal() +var fun = global.eval("(function() {})") +var p = new Proxy(fun, {}) + +// Nuking should preserve IsCallable and IsConstructor. +assertEq(isConstructor(p), true); +assertEq(typeof p, "function"); +nukeCCW(fun); +assertEq(isConstructor(p), true); +assertEq(typeof p, "function"); + +// But actually calling and constructing should throw, because it's been +// nuked. +assertThrowsInstanceOf(() => { p(); }, TypeError); +assertThrowsInstanceOf(() => { new p(); }, TypeError); diff --git a/js/src/jit-test/tests/proxy/proxy-array-length.js b/js/src/jit-test/tests/proxy/proxy-array-length.js new file mode 100644 index 000000000..11045aac0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/proxy-array-length.js @@ -0,0 +1,4 @@ +var a = [1, 2, 3]; +var p = new Proxy(a, {}); +assertEq(p.length, 3); +assertEq(JSON.stringify(p), "[1,2,3]"); diff --git a/js/src/jit-test/tests/proxy/seal-proxy.js b/js/src/jit-test/tests/proxy/seal-proxy.js new file mode 100644 index 000000000..74923cde0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/seal-proxy.js @@ -0,0 +1,16 @@ +var called = []; +var proxy = new Proxy({a: 1}, { + getOwnPropertyDescriptor(target, P) { + called.push("getOwnPropertyDescriptor"); + return Object.getOwnPropertyDescriptor(target, P); + }, + defineProperty(target, P, desc) { + called.push("defineProperty"); + assertEq(Object.getOwnPropertyNames(desc).length, 1); + assertEq(desc.configurable, false); + return Object.defineProperty(target, P, desc); + } +}); + +Object.seal(proxy); +assertEq(called.toString(), "defineProperty"); diff --git a/js/src/jit-test/tests/proxy/surfaces.js b/js/src/jit-test/tests/proxy/surfaces.js new file mode 100644 index 000000000..087b53f94 --- /dev/null +++ b/js/src/jit-test/tests/proxy/surfaces.js @@ -0,0 +1,14 @@ +// Check superficial properties of the Proxy constructor. + +var desc = Object.getOwnPropertyDescriptor(this, "Proxy"); +assertEq(desc.configurable, true); +assertEq(desc.enumerable, false); +assertEq(desc.writable, true); +assertEq(desc.value, Proxy); + +assertEq(typeof Proxy, "function"); +assertEq(Object.getPrototypeOf(Proxy), Function.prototype); +assertEq(Proxy.length, 2); + +// Proxy is a constructor but has no .prototype property. +assertEq(Proxy.hasOwnProperty("prototype"), false); diff --git a/js/src/jit-test/tests/proxy/target-becomes-nonextensible-during-preventExtensions.js b/js/src/jit-test/tests/proxy/target-becomes-nonextensible-during-preventExtensions.js new file mode 100644 index 000000000..3ef475c72 --- /dev/null +++ b/js/src/jit-test/tests/proxy/target-becomes-nonextensible-during-preventExtensions.js @@ -0,0 +1,8 @@ +// Don't assert +var obj = {}; +var proxy = new Proxy(obj, { + get preventExtensions() { + Object.preventExtensions(obj); + } +}); +Object.preventExtensions(proxy); diff --git a/js/src/jit-test/tests/proxy/testBug793160.js b/js/src/jit-test/tests/proxy/testBug793160.js new file mode 100644 index 000000000..8e15baa26 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testBug793160.js @@ -0,0 +1,3 @@ +var obj = new Proxy(Object.create(null), {}); +assertEq(typeof obj, 'object'); +assertEq(obj != null, true); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyApply1.js b/js/src/jit-test/tests/proxy/testDirectProxyApply1.js new file mode 100644 index 000000000..a198dd144 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyApply1.js @@ -0,0 +1,6 @@ +// Forward to the target if the trap is undefined +var target = function (x, y) { + return x + y; +} +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) + assertEq(p(2, 3), 5); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyApply2.js b/js/src/jit-test/tests/proxy/testDirectProxyApply2.js new file mode 100644 index 000000000..cc2c5332e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyApply2.js @@ -0,0 +1,19 @@ +/* + * Call the trap with the handler as the this value, the target as the first + * argument, the original this value as the second argument, and the original + * arguments as the third argument. + */ +var target = function () {}; +var receiver = {}; +var handler = { + apply: function (target1, receiver1, args) { + assertEq(this, handler); + assertEq(target1, target); + assertEq(receiver1, receiver); + assertEq(args.length, 2); + assertEq(args[0], 2); + assertEq(args[1], 3); + } +} +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + p.call(receiver, 2, 3); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyApply3.js b/js/src/jit-test/tests/proxy/testDirectProxyApply3.js new file mode 100644 index 000000000..2a5f927f6 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyApply3.js @@ -0,0 +1,10 @@ +// Return the trap result +// Man, wouldn't haskell's "uninfix" be cleaner? (+) +function justAdd(x, y) { + return x + y; +} + +var handler = { apply : function (target, receiver, args) { return args[0] * args[1]; } }; + +for (let p of [new Proxy(justAdd, handler), Proxy.revocable(justAdd, handler).proxy]) + assertEq(p(2,3), 6); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyApply4.js b/js/src/jit-test/tests/proxy/testDirectProxyApply4.js new file mode 100644 index 000000000..448f8c3b2 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyApply4.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = function () { }; +var handler = { apply: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => holder.proxy(), TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyConstruct1.js b/js/src/jit-test/tests/proxy/testDirectProxyConstruct1.js new file mode 100644 index 000000000..6117d8b6c --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct1.js @@ -0,0 +1,12 @@ +// Forward to the target if the trap is undefined +var p; +var target = function (x, y) { + assertEq(new.target, p); + this.foo = x + y; +} + +for (p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { + var obj = new p(2, 3); + assertEq(obj.foo, 5); + assertEq(Object.getPrototypeOf(obj), target.prototype); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyConstruct2.js b/js/src/jit-test/tests/proxy/testDirectProxyConstruct2.js new file mode 100644 index 000000000..7f2421bd6 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct2.js @@ -0,0 +1,21 @@ +load(libdir + "asserts.js"); +/* + * Call the trap with the handler as the this value, the target as the first + * argument, and the original arguments as the third argument. + * + * Hooks that don't return an object must throw. + */ +var p; +var target = function () {}; +var handler = { + construct: function (target1, args, newTarget) { + assertEq(this, handler); + assertEq(target1, target); + assertEq(args.length, 2); + assertEq(args[0], 2); + assertEq(args[1], 3); + assertEq(newTarget, p); + } +} +for (p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(function () {new p(2, 3)}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyConstruct3.js b/js/src/jit-test/tests/proxy/testDirectProxyConstruct3.js new file mode 100644 index 000000000..881523cdf --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct3.js @@ -0,0 +1,11 @@ +// Return the trap result +function setFoo(x,y) { this.foo = x + y; } +var handler = { construct: function (target, args) { return { foo : args[0] * args[1]}; } } + +for (let proxy of [new Proxy(setFoo, handler), Proxy.revocable(setFoo, handler).proxy]) { + var obj1 = new proxy(2, 3); + assertEq(obj1.foo, 6); + obj1.bar = proxy; + var obj2 = new obj1.bar(2, 3); + assertEq(obj2.foo, 6); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyConstruct4.js b/js/src/jit-test/tests/proxy/testDirectProxyConstruct4.js new file mode 100644 index 000000000..35a44deac --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct4.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = function () { }; +var handler = { construct: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => new holder.proxy(), TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js b/js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js new file mode 100644 index 000000000..e86bc456f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyConstruct5.js @@ -0,0 +1,14 @@ +load(libdir + "asserts.js"); + +// Make sure that a proxy only has a [[Construct]] if the target does + +var handler = {}; +var p = new Proxy(Math.sin, handler); +var r = Proxy.revocable(Math.sin, handler).proxy; + +assertThrowsInstanceOf(() => new p, TypeError, "Can't use 'new' on proxy with non-constructor target"); +assertThrowsInstanceOf(() => new r, TypeError, "Can't use 'new' on proxy with non-constructor target"); +// Better throw regardless of whether we have a handler trap. +handler.construct = (() => ({})); +assertThrowsInstanceOf(() => new p, TypeError, "Can't use 'new' on proxy with non-constructor target"); +assertThrowsInstanceOf(() => new r, TypeError, "Can't use 'new' on proxy with non-constructor target"); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyConstructor.js b/js/src/jit-test/tests/proxy/testDirectProxyConstructor.js new file mode 100644 index 000000000..9ff2710bf --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyConstructor.js @@ -0,0 +1,19 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if Proxy is not called as a constructor +assertThrowsInstanceOf(function () { Proxy({}, {}); }, TypeError); + +// Throw a TypeError if Proxy is called with less than two arguments +assertThrowsInstanceOf(function () { new Proxy(); }, TypeError); +assertThrowsInstanceOf(function () { new Proxy({}); }, TypeError); + +// Throw a TypeError if the first argument is not a non-null object +assertThrowsInstanceOf(function () { new Proxy(0, {}); }, TypeError); +assertThrowsInstanceOf(function () { new Proxy(null, {}); }, TypeError); + +// Throw a TypeError if the second argument is not a non-null object +assertThrowsInstanceOf(function () { new Proxy({}, 0); }, TypeError); +assertThrowsInstanceOf(function () { new Proxy({}, null); }, TypeError); + +// Result of the call should be an object +assertEq(typeof new Proxy({}, {}), 'object'); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty1.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty1.js new file mode 100644 index 000000000..243ad55a1 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty1.js @@ -0,0 +1,23 @@ +// Forward to the target if the trap is not defined + +var target; +function testProxy(p, key) { + Object.defineProperty(p, key, { + value: 'bar', + writable: true, + enumerable: false, + configurable: true + }); + var desc = Object.getOwnPropertyDescriptor(target, key); + assertEq(desc.value, 'bar'); + assertEq(desc.writable, true); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); +} + +for (var key of ['foo', Symbol("quux")]) { + target = {}; + testProxy(new Proxy(target, {}), key); + target = {}; + testProxy(Proxy.revocable(target, {}).proxy, key); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js new file mode 100644 index 000000000..e07323efe --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty2.js @@ -0,0 +1,34 @@ +/* + * Call the trap with the handler as the this value, the target as the first + * argument, the name of the property as the second argument, and the descriptor + * as the third argument. + */ +var target = {}; +var handler = { + defineProperty: function (target1, key, desc1) { + assertEq(this, handler); + assertEq(target1, target); + log.push(key); + assertEq(desc1 == desc, false); + assertEq(desc1.value, 'bar'); + assertEq(desc1.writable, true); + assertEq(desc1.enumerable, false); + assertEq(desc1.configurable, true); + return true; + } +}; +var desc = { + value: 'bar', + writable: true, + enumerable: false, + configurable: true +}; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + var log = []; + Object.defineProperty(p, 'foo', desc); + Object.defineProperty(p, Symbol.for('quux'), desc); + assertEq(log.length, 2); + assertEq(log[0], 'foo'); + assertEq(log[1], Symbol.for('quux')); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty3.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty3.js new file mode 100644 index 000000000..8a466aded --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty3.js @@ -0,0 +1,16 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap defines a new property on a non-extensible + * object + */ +var target = {}; +Object.preventExtensions(target); + +var handler = { defineProperty: function (target, name, desc) { return true; } }; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + assertThrowsInstanceOf(function () { + Object.defineProperty(p, 'foo', { configurable: true }); + }, TypeError); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty4.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty4.js new file mode 100644 index 000000000..30e8d6316 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty4.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap defines a non-configurable property that does + * not exist on the target + */ +var handler = { defineProperty: function (target, name, desc) { return true; } }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) { + assertThrowsInstanceOf(function () { + Object.defineProperty(p, 'foo', { configurable: false }); + }, TypeError); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty5.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty5.js new file mode 100644 index 000000000..78d8847a9 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty5.js @@ -0,0 +1,13 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { defineProperty: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +var p = holder.proxy; +assertThrowsInstanceOf(() => Object.defineProperty(p, 'foo', {}), TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty6.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty6.js new file mode 100644 index 000000000..9818257e0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty6.js @@ -0,0 +1,16 @@ +// Bug 1133094 - Proxy.[[DefineOwnProperty]]() should not throw when asked to +// define a configurable accessor property over an existing configurable data +// property on the target, even if the trap leaves the target unchanged. + +var hits = 0; +var p = new Proxy({x: 1}, { + defineProperty(t, k, desc) { + // don't bother redefining the existing property t.x + hits++; + return true; + } +}); + +assertEq(Object.defineProperty(p, "x", {get: function () {}}), p); +assertEq(hits, 1); +assertEq(p.x, 1); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty7.js b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty7.js new file mode 100644 index 000000000..8c05147c5 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefineProperty7.js @@ -0,0 +1,17 @@ +// Bug 1133085 - Test that descriptors are properly reconstituted +// when only .get or only .set is present. + +load(libdir + "asserts.js"); + +var input, output; +var p = new Proxy({x: 0}, { + defineProperty(t, k, desc) { output = desc; print("ok"); return true; } +}); + +input = {get: function () {}}; +Object.defineProperty(p, "x", input); +assertDeepEq(output, input); + +input = {set: function () {}}; +Object.defineProperty(p, "x", input); +assertDeepEq(output, input); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyDefinePropertyFailure.js b/js/src/jit-test/tests/proxy/testDirectProxyDefinePropertyFailure.js new file mode 100644 index 000000000..0de3000c3 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyDefinePropertyFailure.js @@ -0,0 +1,26 @@ +// Test handling of false return from a handler.defineProperty() hook. + +load(libdir + "asserts.js"); + +var obj = {x: 1, y: 2}; +var nope = false; +var p = new Proxy(obj, { + defineProperty(target, key, desc) { return nope; } +}); + +// Object.defineProperty throws on failure. +print(1); +assertThrowsInstanceOf(() => Object.defineProperty(p, "z", {value: 3}), TypeError); +assertEq("z" in obj, false); +assertThrowsInstanceOf(() => Object.defineProperty(p, "x", {value: 0}), TypeError); + +// Setting a property ultimately causes [[DefineOwnProperty]] to be called. +// In strict mode code only, this is a TypeError. +print(2); +assertEq(p.z = 3, 3); +assertThrowsInstanceOf(() => { "use strict"; p.z = 3; }, TypeError); + +// Other falsy values also trigger failure. +print(3); +for (nope of [0, -0, NaN, ""]) + assertThrowsInstanceOf(() => { "use strict"; p.z = 3; }, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyEnumerate1.js b/js/src/jit-test/tests/proxy/testDirectProxyEnumerate1.js new file mode 100644 index 000000000..485e03182 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyEnumerate1.js @@ -0,0 +1,14 @@ +// for-in with revoked Proxy +load(libdir + "asserts.js"); + +let {proxy, revoke} = Proxy.revocable({a: 1}, {}); + +for (let x in proxy) + assertEq(x, "a") + +revoke(); + +assertThrowsInstanceOf(function() { + for (let x in proxy) + assertEq(true, false); +}, TypeError) diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGet1.js b/js/src/jit-test/tests/proxy/testDirectProxyGet1.js new file mode 100644 index 000000000..43cb5385d --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGet1.js @@ -0,0 +1,12 @@ +// Forward to the target if the trap is not defined +var target = { foo: 'bar' }; +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { + assertEq(p.foo, 'bar'); + assertEq(p['foo'], 'bar'); +} + +var s = Symbol.for("moon"); +var obj = {}; +obj[s] = "dust"; +for (let p of [new Proxy(obj, {}), Proxy.revocable(obj, {}).proxy]) + assertEq(p[s], "dust"); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGet2.js b/js/src/jit-test/tests/proxy/testDirectProxyGet2.js new file mode 100644 index 000000000..ab3efae32 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGet2.js @@ -0,0 +1,22 @@ +/* + * Call the trap with the handler as the this value, the target as the first + * argument, the name of the property as the second argument, and the receiver + * as the third argument + */ +var target = {}; +for (var key of ['foo', Symbol.iterator]) { + handler = {}; + for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + handler.get = + function (target1, name, receiver) { + assertEq(this, handler); + assertEq(target1, target); + assertEq(name, key); + assertEq(receiver, p); + called = true; + }; + var called = false; + assertEq(p[key], undefined); + assertEq(called, true); + } +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGet3.js b/js/src/jit-test/tests/proxy/testDirectProxyGet3.js new file mode 100644 index 000000000..e0acc3b3b --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGet3.js @@ -0,0 +1,50 @@ +load(libdir + "asserts.js"); + +function testProxy(handlerReturn, prop, shouldThrow) { + var handler = { get: function () { return handlerReturn; } }; + for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + if (shouldThrow) + assertThrowsInstanceOf(function () { return p[prop]; }, TypeError); + else + assertEq(p[prop], handlerReturn); + } +} + +/* + * Throw a TypeError if the trap reports a different value for a non-writable, + * non-configurable property + */ +var target = {}; +Object.defineProperty(target, 'foo', { + value: 'bar', + writable: false, + configurable: false +}); +testProxy('baz', 'foo', true); +/* + * Don't throw a TypeError if the trap reports the same value for a non-writable, + * non-configurable property + */ +testProxy('bar', 'foo', false); + +/* + * Don't throw a TypeError if the trap reports a different value for a writable, + * non-configurable property + */ +Object.defineProperty(target, 'prop', { + value: 'bar', + writable: true, + configurable: false +}); +testProxy('baz', 'prop', false); + +/* + * Don't throw a TypeError if the trap reports a different value for a non-writable, + * configurable property + */ +Object.defineProperty(target, 'prop2', { + value: 'bar', + writable: false, + configurable: true +}); +testProxy('baz', 'prop2', false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGet4.js b/js/src/jit-test/tests/proxy/testDirectProxyGet4.js new file mode 100644 index 000000000..4fc7b2f9a --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGet4.js @@ -0,0 +1,14 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap does not report undefined for a non-configurable + * accessor property that does not have a getter + */ +var target = {}; +Object.defineProperty(target, 'foo', { + set: function (value) {}, + configurable: false +}); +var handler = { get: function (target, name, receiver) { return 'baz'; } }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(function () { p['foo'] }, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGet5.js b/js/src/jit-test/tests/proxy/testDirectProxyGet5.js new file mode 100644 index 000000000..a71a36e80 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGet5.js @@ -0,0 +1,16 @@ +// Return the trap result +var target = { foo: 'bar' }; +var s1 = Symbol("moon"), s2 = Symbol("sun"); +target[s1] = "wrong"; + +var handler = { }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + handler.get = (() => 'baz'); + assertEq(p.foo, 'baz'); + + handler.get = (() => undefined); + assertEq(p.foo, undefined); + + handler.get = (() => s2); + assertEq(p[s1], s2); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGet6.js b/js/src/jit-test/tests/proxy/testDirectProxyGet6.js new file mode 100644 index 000000000..c86818122 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGet6.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { get: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => holder.proxy.foo, TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js new file mode 100644 index 000000000..fc2dc0388 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited1.js @@ -0,0 +1,16 @@ +// Getting a property that exists on an ordinary object +// does not touch a proxy on its proto chain. + +load(libdir + "asserts.js"); + +var angryHandler = new Proxy({}, { + get(t, id) { throw new Error("angryHandler should not be queried (" + id + ")"); } +}); +var angryProto = new Proxy({}, angryHandler); +var obj = Object.create(angryProto, { + x: {value: 3}, + y: {get: () => 4} +}); +assertThrowsInstanceOf(() => obj.z, Error); // check that angryProto works +assertEq(obj.x, 3); +assertEq(obj.y, 4); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js new file mode 100644 index 000000000..3c58e314e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited2.js @@ -0,0 +1,31 @@ +// Getting a property that's inherted from a proxy calls the proxy's get handler. + +var handler = { + get(t, id, r) { + assertEq(this, handler); + assertEq(t, target); + assertEq(id, "foo"); + assertEq(r, obj); + return "bar"; + }, + getOwnPropertyDescriptor(t, id) { + throw "FAIL"; + } +}; + +var target = {}; +var proto = new Proxy(target, handler); +var obj = Object.create(proto); +assertEq(obj.foo, "bar"); + +// Longer proto chain: same result. +var origObj = obj; +for (var i = 0; i < 4; i++) + obj = Object.create(obj); +assertEq(obj.foo, "bar"); + +// Chain of transparent proxy wrappers: same result. +obj = origObj; +for (var i = 0; i < 4; i++) + obj = new Proxy(obj, {}); +assertEq(obj.foo, "bar"); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js new file mode 100644 index 000000000..d9f34e60e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited3.js @@ -0,0 +1,21 @@ +// Recursion through the get hook works; runaway recursion is checked. + +load(libdir + "asserts.js"); + +var hits = 0, limit = 10; +var proto = new Proxy({}, { + get(t, id, r) { + assertEq(r, obj); + if (hits++ >= limit) + return "ding"; + return obj[id]; + } +}); + +var obj = Object.create(proto); +assertEq(obj.prop, "ding"); + +hits = 0; +limit = Infinity; +assertThrowsInstanceOf(() => obj.prop, InternalError); +assertEq(hits > 10, true); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetInherited4.js b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited4.js new file mode 100644 index 000000000..b1cf4a905 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetInherited4.js @@ -0,0 +1,6 @@ +// A proxy P whose target is an object X whose prototype is an array V inherits V.length. + +var V = [1, 2, 3]; +var X = Object.create(V); +var P = new Proxy(X, {}); +assertEq(P.length, 3); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor1.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor1.js new file mode 100644 index 000000000..20163dfe0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor1.js @@ -0,0 +1,27 @@ +// Forward to the target if the trap is not defined +var target = {}; +Object.defineProperty(target, 'foo', { + value: 'bar', + writable: true, + enumerable: false, + configurable: true +}); + +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { + var desc = Object.getOwnPropertyDescriptor(p, 'foo'); + assertEq(desc.value, 'bar'); + assertEq(desc.writable, true); + assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); +} + +var proto = {}; +Object.defineProperty(proto, 'foo', { + value: 'bar', + writable: true, + enumerable: false, + configurable: true +}); +var target = Object.create(proto); +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) + assertEq(Object.getOwnPropertyDescriptor(p, 'foo'), undefined); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor10.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor10.js new file mode 100644 index 000000000..79a46d59f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor10.js @@ -0,0 +1,35 @@ +// Return a new descriptor object that agrees with that returned by the trap +var target = {}; +Object.defineProperty(target, 'foo', { + value: 'bar', + writable: true, + enumerable: false, + configurable: true +}); + +var desc = { + value: 'baz', + writable: false, + enumerable: true, + configurable: true +}; +var handler = { getOwnPropertyDescriptor: function () { return desc; } }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + var desc1 = Object.getOwnPropertyDescriptor(p, 'foo'); + assertEq(desc1 == desc, false); + assertEq(desc1.value, 'baz'); + assertEq(desc1.writable, false); + assertEq(desc1.enumerable, true); + assertEq(desc1.configurable, true); +} + +// The returned descriptor must agree in configurability. +desc = { configurable : true }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + var desc1 = Object.getOwnPropertyDescriptor(p, 'foo'); + assertEq(desc1 == desc, false); + assertEq(desc1.value, undefined); + assertEq(desc1.writable, false); + assertEq(desc1.enumerable, false); + assertEq(desc1.configurable, true); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor11.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor11.js new file mode 100644 index 000000000..7af839477 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor11.js @@ -0,0 +1,14 @@ +// Bug 1133294 - Object.getOwnPropertyDescriptor should never return an incomplete descriptor. + +load(libdir + "asserts.js"); + +var p = new Proxy({}, { + getOwnPropertyDescriptor() { return {configurable: true}; } +}); +var desc = Object.getOwnPropertyDescriptor(p, "x"); +assertDeepEq(desc, { + value: undefined, + writable: false, + enumerable: false, + configurable: true +}); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor2.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor2.js new file mode 100644 index 000000000..abf1f5d6d --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor2.js @@ -0,0 +1,20 @@ +/* + * Call the trap with the handler as the this value, the target as the first + * argument, and the name of the property as the second argument + */ +var target = {}; +var called; +var handler = { + getOwnPropertyDescriptor: function (target1, name) { + assertEq(this, handler); + assertEq(target1, target); + assertEq(name, 'foo'); + called = true; + } +}; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + called = false; + Object.getOwnPropertyDescriptor(p, 'foo'); + assertEq(called, true); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor3.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor3.js new file mode 100644 index 000000000..0bfdc7fb8 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor3.js @@ -0,0 +1,13 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { getOwnPropertyDescriptor: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +var test = function () { Object.getOwnPropertyDescriptor(holder.proxy, 'foo'); } +assertThrowsInstanceOf(test, TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor4.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor4.js new file mode 100644 index 000000000..9920121f2 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor4.js @@ -0,0 +1,14 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap reports a non-configurable property as + * non-existent + */ +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: false +}); + +var handler = { getOwnPropertyDescriptor: () => undefined }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(p, 'foo'), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor5.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor5.js new file mode 100644 index 000000000..0d3334680 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor5.js @@ -0,0 +1,15 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap reports an existing own property as + * non-existent on a non-extensible object + */ +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: true +}); +Object.preventExtensions(target); + +var handler = { getOwnPropertyDescriptor: () => undefined }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(p, 'foo'), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor6.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor6.js new file mode 100644 index 000000000..558aab850 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor6.js @@ -0,0 +1,9 @@ +// Return undefined if the trap returns undefined +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: true +}); + +var handler = { getOwnPropertyDescriptor: () => undefined }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertEq(Object.getOwnPropertyDescriptor(p, 'foo'), undefined); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor7.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor7.js new file mode 100644 index 000000000..4f11bb733 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor7.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap reports a new own property on a non-extensible + * object + */ +var target = {}; +Object.preventExtensions(target); + +var handler = { getOwnPropertyDescriptor: () => ({}) }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(p, 'foo'), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor8.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor8.js new file mode 100644 index 000000000..6178d8a03 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor8.js @@ -0,0 +1,13 @@ +load(libdir + "asserts.js"); + +var target = {}; +var handler = { + getOwnPropertyDescriptor: function () { return { value: 2, configurable: true}; } +}; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + Object.getOwnPropertyDescriptor(p, 'foo'); + +Object.preventExtensions(target); +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(p, 'foo'), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor9.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor9.js new file mode 100644 index 000000000..7a07f74f6 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyDescriptor9.js @@ -0,0 +1,9 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap returns a non-configurable descriptor for a + * non-existent property + */ +var handler = { getOwnPropertyDescriptor: () => ({ configurable: false }) }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(p, 'foo'), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames1.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames1.js new file mode 100644 index 000000000..11618c876 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames1.js @@ -0,0 +1,30 @@ +// Forward to the target if the trap is not defined +var objAB = Object.create(null, { + a: { + enumerable: true, + configurable: true + }, + b: { + enumerable: false, + configurable: true + } +}); + +var objCD = Object.create(objAB, { + c: { + enumerable: true, + configurable: true + }, + d: { + enumerable: false, + configurable: true + } +}); + +objCD[Symbol("moon")] = "something"; +for (let p of [new Proxy(objCD, {}), Proxy.revocable(objCD, {}).proxy]) { + var names = Object.getOwnPropertyNames(p); + assertEq(names.length, 2); + assertEq(names[0], 'c'); + assertEq(names[1], 'd'); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames2.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames2.js new file mode 100644 index 000000000..8d3696369 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames2.js @@ -0,0 +1,19 @@ +/* + * Call the trap with the handler as the this value, and the target as the first + * argument + */ +var target = {}; +var called = false; +var handler = { + ownKeys: function (target1) { + assertEq(this, handler); + assertEq(target1, target); + called = true; + return []; + } +}; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + assertEq(Object.getOwnPropertyNames(p).length, 0); + assertEq(called, true); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames3.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames3.js new file mode 100644 index 000000000..cd674a8ba --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames3.js @@ -0,0 +1,6 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the trap does not return an object +var handler = { ownKeys: () => undefined }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyNames(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames4.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames4.js new file mode 100644 index 000000000..090a3d741 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames4.js @@ -0,0 +1,5 @@ +load(libdir + "asserts.js"); + +var handler = { ownKeys : () => [ 'foo', 'foo' ] }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) + assertDeepEq(Object.getOwnPropertyNames(p), ['foo', 'foo']); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames5.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames5.js new file mode 100644 index 000000000..9ff27518f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames5.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap reports a new own property on a non-extensible + * object + */ +var target = {}; +Object.preventExtensions(target); + +var handler = { ownKeys: () => [ 'foo' ] }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyNames(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames6.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames6.js new file mode 100644 index 000000000..355f88a92 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames6.js @@ -0,0 +1,11 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the trap skips a non-configurable property +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: false +}); + +var handler = { ownKeys: () => [] }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyNames(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames7.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames7.js new file mode 100644 index 000000000..a06293414 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames7.js @@ -0,0 +1,15 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap skips an existing own property on a + * non-extensible object + */ +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: true +}); +Object.preventExtensions(target); + +var handler = { ownKeys: () => [] }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.getOwnPropertyNames(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames8.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames8.js new file mode 100644 index 000000000..bd4631ff8 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames8.js @@ -0,0 +1,42 @@ +// Return the names returned by the trap +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: true +}); + +var handler = { ownKeys: () => [ 'bar' ] }; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + var names = Object.getOwnPropertyNames(p); + assertEq(names.length, 1); + assertEq(names[0], 'bar'); +} + +var protoWithAB = Object.create(null, { + a: { + enumerable: true, + configurable: true + }, + b: { + enumerable: false, + configurable: true + } +}); +var objWithCD = Object.create(protoWithAB, { + c: { + enumerable: true, + configurable: true + }, + d: { + enumerable: true, + configurable: true + } +}); + +handler = { ownKeys: () => [ 'c', 'e' ] }; +for (let p of [new Proxy(objWithCD, handler), Proxy.revocable(objWithCD, handler).proxy]) { + var names = Object.getOwnPropertyNames(p); + assertEq(names.length, 2); + assertEq(names[0], 'c'); + assertEq(names[1], 'e'); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames9.js b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames9.js new file mode 100644 index 000000000..18a2cc89d --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyGetOwnPropertyNames9.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { ownKeys: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => Object.getOwnPropertyNames(holder.proxy), TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHas1.js b/js/src/jit-test/tests/proxy/testDirectProxyHas1.js new file mode 100644 index 000000000..0e46d03e8 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHas1.js @@ -0,0 +1,18 @@ +// Forward to the target if the trap is not defined +var proto = Object.create(null, { + 'foo': { + configurable: true + } +}); +var target = Object.create(proto, { + 'bar': { + configurable: true + } +}); + +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { + assertEq('foo' in p, true); + assertEq('bar' in p, true); + assertEq('baz' in p, false); + assertEq(Symbol() in p, false); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHas2.js b/js/src/jit-test/tests/proxy/testDirectProxyHas2.js new file mode 100644 index 000000000..a1aba55df --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHas2.js @@ -0,0 +1,21 @@ +/* + * Call the trap with the handler as the this value, the target as the first + * argument, and the name of the property as the second argument + */ +var target = {}; +for (var key of ['foo', Symbol('bar')]) { + var called; + var handler = { + has: function (target1, name) { + assertEq(this, handler); + assertEq(target1, target); + assertEq(name, key); + called = true; + } + }; + for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + called = false; + key in p; + assertEq(called, true); + } +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHas3.js b/js/src/jit-test/tests/proxy/testDirectProxyHas3.js new file mode 100644 index 000000000..1840c85fd --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHas3.js @@ -0,0 +1,13 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap reports a non-configurable own property as + * non-existent + */ +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: false +}); +var handler = { has: () => false }; +for (p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(function () { 'foo' in p; }, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHas4.js b/js/src/jit-test/tests/proxy/testDirectProxyHas4.js new file mode 100644 index 000000000..e2f88a7dd --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHas4.js @@ -0,0 +1,15 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap reports an existing own property as + * non-existent on a non-extensible object + */ +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: true +}); +Object.preventExtensions(target); + +var handler = { has: () => false }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(function () { 'foo' in p }, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHas5.js b/js/src/jit-test/tests/proxy/testDirectProxyHas5.js new file mode 100644 index 000000000..c581ede8e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHas5.js @@ -0,0 +1,18 @@ +// Return the trap result +var proto = Object.create(null, { + 'foo': { + configurable: true + } +}); +var target = Object.create(proto, { + 'bar': { + configurable: true + } +}); + +var handler = { has: () => false }; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + assertEq('foo' in p, false); + assertEq('bar' in p, false); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHas6.js b/js/src/jit-test/tests/proxy/testDirectProxyHas6.js new file mode 100644 index 000000000..179e40f86 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHas6.js @@ -0,0 +1,13 @@ +/* + * Don't throw a type error if the trap reports an undefined property as + * non-present, regardless of extensibility. + */ +var target = {}; +Object.preventExtensions(target); + +var handler = { has: () => false }; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + assertEq('foo' in p, false); + assertEq(Symbol.iterator in p, false); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHas7.js b/js/src/jit-test/tests/proxy/testDirectProxyHas7.js new file mode 100644 index 000000000..096688204 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHas7.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { has: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => 'foo' in holder.proxy, TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyHasOwnProperty.js b/js/src/jit-test/tests/proxy/testDirectProxyHasOwnProperty.js new file mode 100644 index 000000000..5b7a97bba --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyHasOwnProperty.js @@ -0,0 +1,35 @@ +// Forward to the target if the trap is not defined +var proto = Object.create(null, { + 'foo': { + configurable: true + } +}); +var descs = { + 'bar': { + configurable: true + } +}; +descs[Symbol.for("quux")] = {configurable: true}; +var target = Object.create(proto, descs); + +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { + assertEq(({}).hasOwnProperty.call(p, 'foo'), false); + assertEq(({}).hasOwnProperty.call(p, 'bar'), true); + assertEq(({}).hasOwnProperty.call(p, 'quux'), false); + assertEq(({}).hasOwnProperty.call(p, Symbol('quux')), false); + assertEq(({}).hasOwnProperty.call(p, 'Symbol(quux)'), false); + assertEq(({}).hasOwnProperty.call(p, Symbol.for('quux')), true); +} + +// Make sure only the getOwnPropertyDescriptor trap is called, and not the has +// trap. +var called; +var handler = { getOwnPropertyDescriptor: function () { called = true; }, + has: function () { assertEq(false, true, "has trap must not be called"); } + } + +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) { + called = false; + assertEq(({}).hasOwnProperty.call(p, 'foo'), false); + assertEq(called, true); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyIsExtensible1.js b/js/src/jit-test/tests/proxy/testDirectProxyIsExtensible1.js new file mode 100644 index 000000000..6d0bdc629 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyIsExtensible1.js @@ -0,0 +1,97 @@ +load(libdir + "asserts.js"); +// Test ES6 Proxy trap compliance for Object.isExtensible() on exotic proxy +// objects. +var unsealed = {}; +var sealed = Object.seal({}); +var handler = {}; + +assertEq(Object.isExtensible(unsealed), true); +assertEq(Object.isExtensible(sealed), false); + +var targetSealed = new Proxy(sealed, handler); +var targetUnsealed = new Proxy(unsealed, handler); + +var handlerCalled = false; + +function testExtensible(target, expectedResult, shouldIgnoreHandler = false) +{ + for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + handlerCalled = false; + if (typeof expectedResult === "boolean") + assertEq(Object.isExtensible(p), expectedResult, "Must return the correct value."); + else + assertThrowsInstanceOf(() => Object.isExtensible(p), expectedResult); + assertEq(handlerCalled, !shouldIgnoreHandler, "Must call handler appropriately"); + } +} + +// without traps, forward to the target +// First, make sure we get the obvious answer on a non-exotic target. +testExtensible(sealed, false, /* shouldIgnoreHandler = */true); +testExtensible(unsealed, true, /* shouldIgnoreHandler = */true); + +// Now, keep everyone honest. What if the target itself is a proxy? +// Note that we cheat a little. |handlerCalled| is true in a sense, just not +// for the toplevel handler. +// While we're here, test that the argument is passed correctly. +var targetsTarget = {}; +function ensureCalled(target) { assertEq(target, targetsTarget); handlerCalled = true; return true; } +var proxyTarget = new Proxy(targetsTarget, { isExtensible : ensureCalled }); +testExtensible(proxyTarget, true); + +// What if the trap says it's necessarily sealed? +function fakeSealed() { handlerCalled = true; return false; } +handler.isExtensible = fakeSealed; +testExtensible(targetSealed, false); +testExtensible(targetUnsealed, TypeError); + +// What if the trap says it's never sealed? +function fakeUnsealed() { handlerCalled = true; return true; } +handler.isExtensible = fakeUnsealed; +testExtensible(targetSealed, TypeError); +testExtensible(targetUnsealed, true); + +// make sure we are able to prevent further extensions mid-flight and throw if the +// hook tries to lie. +function makeSealedTruth(target) { handlerCalled = true; Object.preventExtensions(target); return false; } +function makeSealedLie(target) { handlerCalled = true; Object.preventExtensions(target); return true; } +handler.isExtensible = makeSealedTruth; +testExtensible({}, false); +handler.isExtensible = makeSealedLie; +testExtensible({}, TypeError); + +// What if the trap doesn't directly return a boolean? +function falseyNonBool() { handlerCalled = true; return undefined; } +handler.isExtensible = falseyNonBool; +testExtensible(sealed, false); +testExtensible(unsealed, TypeError); + +function truthyNonBool() { handlerCalled = true; return {}; } +handler.isExtensible = truthyNonBool; +testExtensible(sealed, TypeError); +testExtensible(unsealed, true); + +// What if the trap throws? +function ExtensibleError() { } +ExtensibleError.prototype = new Error(); +ExtensibleError.prototype.constructor = ExtensibleError; +function throwFromTrap() { throw new ExtensibleError(); } +handler.isExtensible = throwFromTrap; + +// exercise some other code paths and make sure that they invoke the trap and +// can handle the ensuing error. +for (let p of [new Proxy(sealed, handler), Proxy.revocable(sealed, handler).proxy]) { + assertThrowsInstanceOf(function () { Object.isExtensible(p); }, + ExtensibleError, "Must throw if the trap does."); + assertThrowsInstanceOf(function () { Object.isFrozen(p); }, + ExtensibleError, "Must throw if the trap does."); + assertThrowsInstanceOf(function () { Object.isSealed(p); }, + ExtensibleError, "Must throw if the trap does."); +} + +// What if the trap likes to re-ask old questions? +for (let p of [new Proxy(sealed, handler), Proxy.revocable(sealed, handler).proxy]) { + handler.isExtensible = (() => Object.isExtensible(p)); + assertThrowsInstanceOf(() => Object.isExtensible(p), + InternalError, "Should allow and detect infinite recurison."); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyIsExtensible2.js b/js/src/jit-test/tests/proxy/testDirectProxyIsExtensible2.js new file mode 100644 index 000000000..09662541d --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyIsExtensible2.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { isExtensible: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => Object.isExtensible(holder.proxy), TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys1.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys1.js new file mode 100644 index 000000000..217685050 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys1.js @@ -0,0 +1,27 @@ +// Forward to the target if the trap is not defined +var proto = Object.create(null, { + a: { + enumerable: true, + configurable: true + }, + b: { + enumerable: false, + configurable: true + } +}); +var target = Object.create(proto, { + c: { + enumerable: true, + configurable: true + }, + d: { + enumerable: false, + configurable: true + } +}); + +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { + var names = Object.keys(p); + assertEq(names.length, 1); + assertEq(names[0], 'c'); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys10.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys10.js new file mode 100644 index 000000000..47a4360aa --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys10.js @@ -0,0 +1,23 @@ +load(libdir + "asserts.js"); + +// Allow [[GetOwnPropertyDescriptor]] to spoof enumerability of target object's +// properties. Note that this also tests that the getOwnPropertyDescriptor is +// called by Object.keys(), as expected. + +var target = {}; +var handler = { + getOwnPropertyDescriptor : function (target, P) { + var targetDesc = Object.getOwnPropertyDescriptor(target, P); + // Lie about enumerability + targetDesc.enumerable = !targetDesc.enumerable; + return targetDesc; + } +}; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + Object.defineProperty(target, "foo", { configurable: true, enumerable: false }); + assertDeepEq(Object.keys(p), ["foo"]); + + Object.defineProperty(target, "foo", {configurable: true, enumerable: true}); + assertDeepEq(Object.keys(p), []); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys11.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys11.js new file mode 100644 index 000000000..2cd54d3b0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys11.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { ownKeys: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => Object.keys(holder.proxy), TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys2.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys2.js new file mode 100644 index 000000000..db03be48f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys2.js @@ -0,0 +1,20 @@ +/* + * Call the trap with the handler as the this value, and the target as the first + * argument + */ +var target = {}; +var called; +var handler = { + ownKeys: function (target1) { + assertEq(this, handler); + assertEq(target1, target); + called = true; + return []; + } +}; + +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + called = false; + Object.keys(new Proxy(target, handler)); + assertEq(called, true); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys3.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys3.js new file mode 100644 index 000000000..1ffc2edd6 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys3.js @@ -0,0 +1,7 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the trap does not return an object + +var handler = { ownKeys: () => undefined }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) + assertThrowsInstanceOf(() => Object.keys(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys4.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys4.js new file mode 100644 index 000000000..f20d5368d --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys4.js @@ -0,0 +1,5 @@ +load(libdir + "asserts.js"); + +var handler = { ownKeys: () => [ 'foo', 'foo' ] }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) + assertDeepEq(Object.keys(p), []); // Properties are not enumerable. diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys5.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys5.js new file mode 100644 index 000000000..2ccfc5cd3 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys5.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap reports a new own property on a non-extensible + * object + */ +var target = {}; +Object.preventExtensions(target); + +var handler = { ownKeys: () => [ 'foo' ] }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.keys(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys6.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys6.js new file mode 100644 index 000000000..00d97cbbc --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys6.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the trap skips a non-configurable enumerable property +var target = {}; +Object.defineProperty(target, 'foo', { + enumerable: true, + configurable: false +}); + +var handler = { ownKeys: () => [] }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.keys(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys7.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys7.js new file mode 100644 index 000000000..448691930 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys7.js @@ -0,0 +1,16 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap skips an existing own enumerable property on a + * non-extensible object + */ +var target = {}; +Object.defineProperty(target, 'foo', { + enumerable: true, + configurable: true +}); +Object.preventExtensions(target); + +var handler = { ownKeys: () => [] }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.keys(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys8.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys8.js new file mode 100644 index 000000000..448691930 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys8.js @@ -0,0 +1,16 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap skips an existing own enumerable property on a + * non-extensible object + */ +var target = {}; +Object.defineProperty(target, 'foo', { + enumerable: true, + configurable: true +}); +Object.preventExtensions(target); + +var handler = { ownKeys: () => [] }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => Object.keys(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyKeys9.js b/js/src/jit-test/tests/proxy/testDirectProxyKeys9.js new file mode 100644 index 000000000..92695cdcd --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyKeys9.js @@ -0,0 +1,35 @@ +// Cull non-existent names returned by the trap. +var nullProtoAB = Object.create(null, { + a: { + enumerable: true, + configurable: true + }, + b: { + enumerable: false, + configurable: true + } +}); +var protoABWithCD = Object.create(nullProtoAB, { + c: { + enumerable: true, + configurable: true + }, + d: { + enumerable: false, + configurable: true + } +}); + +var returnedNames; +var handler = { ownKeys: () => returnedNames }; + +for (let p of [new Proxy(protoABWithCD, handler), Proxy.revocable(protoABWithCD, handler).proxy]) { + returnedNames = [ 'e' ]; + var names = Object.keys(p); + assertEq(names.length, 0); + + returnedNames = [ 'c' ]; + names = Object.keys(p); + assertEq(names.length, 1); + assertEq(names[0], 'c'); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxyOnProtoWithForIn.js b/js/src/jit-test/tests/proxy/testDirectProxyOnProtoWithForIn.js new file mode 100644 index 000000000..984e913c0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyOnProtoWithForIn.js @@ -0,0 +1,23 @@ +let proxy = new Proxy({ + a: 1, + b: 2, + c: 3 +}, { + enumerate() { + // Should not be invoked. + assertEq(false, true); + }, + + ownKeys() { + return ['a', 'b']; + } +}); + +let object = Object.create(proxy); +object.d = 4; + +let a = []; +for (let x in object) { + a.push(x); +} +assertEq(a.toString(), "d,a,b"); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyOwnKeysSymbol.js b/js/src/jit-test/tests/proxy/testDirectProxyOwnKeysSymbol.js new file mode 100644 index 000000000..ad6e1845e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyOwnKeysSymbol.js @@ -0,0 +1,33 @@ +// Make sure that we can find own, enumerable symbols. +var symbol = Symbol("bad"); +var symbol2 = Symbol("good"); +var proxy = new Proxy({}, { + ownKeys() { + return [symbol, symbol2]; + }, + getOwnPropertyDescriptor(target, name) { + if (name == symbol) + return {configurable: true, enumerable: false, value: {}}; + // Only this enumerable symbol should be defined. + if (name == symbol2) + return {configurable: true, enumerable: true, value: {}}; + assertEq(true, false); + }, + get(target, name) { + // Slightly confusing, but these are the descriptors that defineProperties + // is going to define on the object. + if (name == symbol) + return {configurable: true, value: "bad"}; + if (name == symbol2) + return {configurable: true, value: "good"}; + assertEq(true, false); + } +}); +assertEq(Object.getOwnPropertySymbols(proxy).length, 2); + +var obj = {}; +Object.defineProperties(obj, proxy); +assertEq(Object.getOwnPropertySymbols(obj).length, 1); +assertEq(symbol in obj, false); +assertEq(symbol2 in obj, true); +assertEq(obj[symbol2], "good"); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js new file mode 100644 index 000000000..384be1cf5 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions1.js @@ -0,0 +1,10 @@ +// Forward to the target if the trap is not defined +var target = {}; +var proxy = new Proxy(target, {}); +Object.preventExtensions(proxy); +assertEq(Object.isExtensible(target), false); + +target = {}; +proxy = Proxy.revocable(target, {}).proxy; +Object.preventExtensions(proxy); +assertEq(Object.isExtensible(target), false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js new file mode 100644 index 000000000..42591e22c --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions2.js @@ -0,0 +1,25 @@ +/* + * Call the trap with the handler as the this value and the target as the first + * argument. + */ +var target = {}; +var handler = { + preventExtensions: function (target1) { + assertEq(this, handler); + assertEq(target1, target); + Object.preventExtensions(target1); + called = true; + return true; + } +}; + +var proxy = new Proxy(target, handler); +var called = false; +Object.preventExtensions(proxy); +assertEq(called, true); + +target = {}; +proxy = Proxy.revocable(target, handler).proxy; +called = false; +Object.preventExtensions(proxy); +assertEq(called, true); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js new file mode 100644 index 000000000..7cb404618 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions3.js @@ -0,0 +1,7 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the trap reports an extensible object as non-extensible + +var handler = { preventExtensions: () => true }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) + assertThrowsInstanceOf(() => Object.preventExtensions(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js new file mode 100644 index 000000000..01395624e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions4.js @@ -0,0 +1,6 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the object refuses to be made non-extensible +var handler = { preventExtensions: () => false }; +for (let p of [new Proxy({}, handler), Proxy.revocable({}, handler).proxy]) + assertThrowsInstanceOf(() => Object.preventExtensions(p), TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions5.js b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions5.js new file mode 100644 index 000000000..e839ffa7b --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyPreventExtensions5.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { preventExtensions: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => Object.preventExtensions(holder.proxy), TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyRevoke.js b/js/src/jit-test/tests/proxy/testDirectProxyRevoke.js new file mode 100644 index 000000000..420fe3eaf --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyRevoke.js @@ -0,0 +1,45 @@ +load(libdir + "asserts.js"); + +// Test for various properties demanded of Proxy.revocable +var holder = Proxy.revocable({}, {}); + +// The returned object must inherit from Object.prototype +assertEq(Object.getPrototypeOf(holder), Object.prototype); + +assertDeepEq(Object.getOwnPropertyNames(holder), [ 'proxy', 'revoke' ]); + +// The revocation function must inherit from Function.prototype +assertEq(Object.getPrototypeOf(holder.revoke), Function.prototype); + +// Proxy.revoke should return unique objects from the same opcode call. +var proxyLog = [] +var revokerLog = [] +var holderLog = [] + +function addUnique(l, v) +{ + assertEq(l.indexOf(v), -1); + l.push(v); +} + +for (let i = 0; i < 5; i++) { + holder = Proxy.revocable({}, {}); + addUnique(holderLog, holder); + addUnique(revokerLog, holder.revoke); + addUnique(proxyLog, holder.proxy); +} + +// The provided revoke function should revoke only the 1 proxy +var p = proxyLog.pop(); +var r = revokerLog.pop(); + +// Works before, but not after. This is mostly a token. There are +// testDirectProxy* tests to check each trap revokes properly. +p.foo; +assertEq(r(), undefined, "Revoke trap must return undefined"); +assertThrowsInstanceOf(() => p.foo, TypeError); +assertEq(r(), undefined, "Revoke trap must return undefined"); + +// None of this should throw, since these proxies are unrevoked. +for (let i = 0; i < proxyLog.length; i++) + proxyLog[i].foo; diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet1.js b/js/src/jit-test/tests/proxy/testDirectProxySet1.js new file mode 100644 index 000000000..ddc1bc5dd --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet1.js @@ -0,0 +1,18 @@ +// Forward to the target if the trap is not defined +var target = { + foo: 'bar' +}; +for (let p of [new Proxy(target, {}), Proxy.revocable(target, {}).proxy]) { + // The sets from the first iteration will affect target, but it doesn't + // matter, since the effectiveness of the foo sets is still observable. + p.foo = 'baz'; + assertEq(target.foo, 'baz'); + p['foo'] = 'buz'; + assertEq(target.foo, 'buz'); + + var sym = Symbol.for('quux'); + p[sym] = sym; + assertEq(target[sym], sym); + // Reset for second iteration + target[sym] = undefined; +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet10.js b/js/src/jit-test/tests/proxy/testDirectProxySet10.js new file mode 100644 index 000000000..287cced8f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet10.js @@ -0,0 +1,60 @@ +// Assigning to a non-existing property of a plain object defines that +// property on that object, even if a proxy is on the proto chain. + +// Create an object that behaves just like obj except it throws (instead of +// returning undefined) if you try to get a property that doesn't exist. +function throwIfNoSuchProperty(obj) { + return new Proxy(obj, { + get(t, id) { + if (id in t) + return t[id]; + throw new Error("no such handler method: " + id); + } + }); +} + +// Use a touchy object as our proxy handler in this test. +var hits = 0, savedDesc = undefined; +var touchyHandler = throwIfNoSuchProperty({ + set: undefined +}); +var target = {}; +var proto = new Proxy(target, touchyHandler); +var receiver = Object.create(proto); + +// This assignment `receiver.x = 2` results in a series of [[Set]] calls, +// starting with: +// +// - receiver.[[Set]]() +// - receiver is an ordinary object. +// - This looks for an own property "x" on receiver. There is none. +// - So it walks the prototype chain, doing a tail-call to: +// - proto.[[Set]]() +// - proto is a proxy. +// - This does handler.[[Get]]("set") to look for a set trap +// (that's why we need `set: undefined` on the handler, above) +// - Since there's no "set" handler, it tail-calls: +// - target.[[Set]]() +// - ordinary +// - no own property "x" +// - tail call to: +// - Object.prototype.[[Set]]() +// - ordinary +// - no own property "x" +// - We're at the end of the line: there's nothing left on the proto chain. +// - So at last we call: +// - receiver.[[DefineOwnProperty]]() +// - ordinary +// - creates the property +// +// Got all that? Let's try it. +// +receiver.x = 2; +assertEq(receiver.x, 2); + +var desc = Object.getOwnPropertyDescriptor(receiver, "x"); +assertEq(desc.enumerable, true); +assertEq(desc.configurable, true); +assertEq(desc.writable, true); +assertEq(desc.value, 2); + diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet2.js b/js/src/jit-test/tests/proxy/testDirectProxySet2.js new file mode 100644 index 000000000..af940f6bc --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet2.js @@ -0,0 +1,23 @@ +/* + * Call the trap with the handler as the this value, the target as the first + * argument, the name of the property as the second argument, the value as the + * third argument, and the receiver as the fourth argument + */ +var target = {}; +for (var key of ['foo', Symbol.for('quux')]) { + var handler = { }; + for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + handler.set = function (target1, name, val, receiver) { + assertEq(this, handler); + assertEq(target1, target); + assertEq(name, key); + assertEq(val, 'baz'); + assertEq(receiver, p); + called = true; + } + + var called = false; + p[key] = 'baz'; + assertEq(called, true); + } +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet3.js b/js/src/jit-test/tests/proxy/testDirectProxySet3.js new file mode 100644 index 000000000..94674aed6 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet3.js @@ -0,0 +1,14 @@ +load(libdir + "asserts.js"); + +// Throw a TypeError if the trap sets a non-writable, non-configurable property +for (var key of ['foo', Symbol.for('quux')]) { + var target = {}; + Object.defineProperty(target, key, { + value: 'bar', + writable: false, + configurable: false + }); + var handler = { set: () => true }; + for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => p[key] = 'baz', TypeError); +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet4.js b/js/src/jit-test/tests/proxy/testDirectProxySet4.js new file mode 100644 index 000000000..760d1099d --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet4.js @@ -0,0 +1,17 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the trap sets a non-configurable accessor property that + * doest not have a setter + */ +var target = {}; +Object.defineProperty(target, 'foo', { + get: function () { + return 'bar' + }, + configurable: false +}); + +var handler = { set: () => true }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) + assertThrowsInstanceOf(() => p['foo'] = 'baz', TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet5.js b/js/src/jit-test/tests/proxy/testDirectProxySet5.js new file mode 100644 index 000000000..d4adcda0f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet5.js @@ -0,0 +1,13 @@ +// Reflect side-effects from the trap +var target = { + foo: 'bar' +}; + +var handler = { set: (target, name) => target[name] = 'qux' }; +for (let p of [new Proxy(target, handler), Proxy.revocable(target, handler).proxy]) { + p['foo'] = 'baz'; + assertEq(target['foo'], 'qux'); + + // reset for second iteration + target['foo'] = 'bar'; +} diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet6.js b/js/src/jit-test/tests/proxy/testDirectProxySet6.js new file mode 100644 index 000000000..3b5f66c2a --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet6.js @@ -0,0 +1,12 @@ +load(libdir + "asserts.js"); +// Revoked proxies should throw before calling the handler + +var called = false; +var target = {}; +var handler = { set: () => called = true }; +var holder = Proxy.revocable(target, handler); + +holder.revoke(); + +assertThrowsInstanceOf(() => holder.proxy.foo = 'bar', TypeError); +assertEq(called, false); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet7.js b/js/src/jit-test/tests/proxy/testDirectProxySet7.js new file mode 100644 index 000000000..9376670e4 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet7.js @@ -0,0 +1,20 @@ +// Assigning to a proxy with no set handler calls the defineProperty handler +// when no such property already exists. + +var hits = 0; +var t = {}; +var p = new Proxy(t, { + defineProperty(t, id, desc) { hits++; return true; } +}); +p.x = 1; +assertEq(hits, 1); +assertEq("x" in t, false); + +// Same thing, but the receiver is a plain object inheriting from p: +var receiver = Object.create(p); +hits = 0; +receiver.x = 2; +assertEq(hits, 0); +assertEq("x" in t, false); +assertEq(receiver.hasOwnProperty("x"), true); +assertEq(receiver.x, 2); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet8.js b/js/src/jit-test/tests/proxy/testDirectProxySet8.js new file mode 100644 index 000000000..905769bbd --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet8.js @@ -0,0 +1,21 @@ +// Assigning to a proxy with no set handler calls the defineProperty handler +// when an existing inherited data property already exists. + +var hits = 0; +var proto = {x: 1}; +var t = Object.create(proto); +var p = new Proxy(t, { + defineProperty(t, id, desc) { hits++; return true; } +}); +p.x = 2; +assertEq(hits, 1); +assertEq(proto.x, 1); +assertEq(t.hasOwnProperty("x"), false); + +// Same thing, but the receiver is a plain object inheriting from p: +var receiver = Object.create(p); +receiver.x = 2; +assertEq(hits, 1); +assertEq(t.hasOwnProperty("x"), false); +assertEq(receiver.hasOwnProperty("x"), true); +assertEq(receiver.x, 2); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySet9.js b/js/src/jit-test/tests/proxy/testDirectProxySet9.js new file mode 100644 index 000000000..7a08b3c0c --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySet9.js @@ -0,0 +1,20 @@ +// Assigning to a proxy with no set handler calls the defineProperty handler +// when an existing own data property already exists on the target. + +var t = {x: 1}; +var p = new Proxy(t, { + defineProperty(t, id, desc) { + hits++; + + // ES6 draft rev 28 (2014 Oct 14) 9.1.9 step 5.e.i. + // Since the property already exists, the system only changes + // the value. desc is otherwise empty. + assertEq(Object.getOwnPropertyNames(desc).join(","), "value"); + assertEq(desc.value, 42); + return true; + } +}); +var hits = 0; +p.x = 42; +assertEq(hits, 1); +assertEq(t.x, 1); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetArray1.js b/js/src/jit-test/tests/proxy/testDirectProxySetArray1.js new file mode 100644 index 000000000..27d8489c4 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetArray1.js @@ -0,0 +1,25 @@ +// Assigning to a missing array element (a hole) via a proxy with no set handler +// calls the defineProperty handler. + +function test(id) { + var arr = [, 1, 2, 3]; + var p = new Proxy(arr, { + defineProperty(t, id, desc) { + hits++; + assertEq(desc.value, "ponies"); + assertEq(desc.enumerable, true); + assertEq(desc.configurable, true); + assertEq(desc.writable, true); + return true; + } + }); + var hits = 0; + p[id] = "ponies"; + assertEq(hits, 1); + assertEq(id in arr, false); + assertEq(arr.length, 4); +} + +test(0); +test(4); +test("str"); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetArray2.js b/js/src/jit-test/tests/proxy/testDirectProxySetArray2.js new file mode 100644 index 000000000..1025aa04f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetArray2.js @@ -0,0 +1,12 @@ +// Direct proxies pass through the receiver argument to [[Set]] to their targets. +// This also tests that an ordinary object's [[Set]] method can change the length +// of an array passed as the receiver. + +load(libdir + "asserts.js"); + +var a = [0, 1, 2, 3]; +var p = new Proxy({}, {}); +Reflect.set(p, "length", 2, a); +assertEq("length" in p, false); +assertEq(a.length, 2); +assertDeepEq(a, [0, 1]); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetArray3.js b/js/src/jit-test/tests/proxy/testDirectProxySetArray3.js new file mode 100644 index 000000000..44bbcd99b --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetArray3.js @@ -0,0 +1,21 @@ +// Assigning to the length property of a proxy to an array +// calls the proxy's defineProperty handler. + +var a = [0, 1, 2, 3]; +var p = new Proxy(a, { + defineProperty(t, id, desc) { + hits++; + + // ES6 draft rev 28 (2014 Oct 14) 9.1.9 step 5.e.i. + // Since the property already exists, the system only changes + // the value. desc is otherwise empty. + assertEq(Object.getOwnPropertyNames(desc).join(","), "value"); + assertEq(desc.value, 2); + return true; + } +}); +var hits = 0; +p.length = 2; +assertEq(hits, 1); +assertEq(a.length, 4); +assertEq(a[2], 2); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetArray4.js b/js/src/jit-test/tests/proxy/testDirectProxySetArray4.js new file mode 100644 index 000000000..524ddca45 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetArray4.js @@ -0,0 +1,24 @@ +// Assigning to an existing array element via a proxy with no set handler +// calls the defineProperty handler. + +function test(arr) { + var p = new Proxy(arr, { + defineProperty(t, id, desc) { + hits++; + + // ES6 draft rev 28 (2014 Oct 14) 9.1.9 step 5.e.i. + // Since the property already exists, the system only changes + // the value. desc is otherwise empty. + assertEq(Object.getOwnPropertyNames(desc).join(","), "value"); + assertEq(desc.value, "ponies"); + return true; + } + }); + var hits = 0; + p[0] = "ponies"; + assertEq(hits, 1); + assertEq(arr[0], 123); +} + +test([123]); +test(new Int32Array([123])); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetFailure.js b/js/src/jit-test/tests/proxy/testDirectProxySetFailure.js new file mode 100644 index 000000000..3646fc69b --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetFailure.js @@ -0,0 +1,32 @@ +// Test handling of false return from a handler.set() hook. + +load(libdir + "asserts.js"); + +var obj = {x: 1}; +var p = new Proxy(obj, { + set(target, key, value, receiver) { return false; } +}); + +// Failing to set a property is a no-op in non-strict code. +assertEq(p.x = 2, 2); +assertEq(obj.x, 1); + +// It's a TypeError in strict mode code. +assertThrowsInstanceOf(() => { "use strict"; p.x = 2; }, TypeError); +assertEq(obj.x, 1); + +// Even if the value doesn't change. +assertThrowsInstanceOf(() => { "use strict"; p.x = 1; }, TypeError); +assertEq(obj.x, 1); + +// Even if the target property doesn't already exist. +assertThrowsInstanceOf(() => { "use strict"; p.z = 1; }, TypeError); +assertEq("z" in obj, false); + +// [].sort() mutates its operand only by doing strict [[Set]] calls. +var arr = ["not", "already", "in", "order"]; +var p2 = new Proxy(arr, { + set(target, key, value, receiver) { return false; } +}); +assertThrowsInstanceOf(() => p2.sort(), TypeError); +assertDeepEq(arr, ["not", "already", "in", "order"]); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetInherited.js b/js/src/jit-test/tests/proxy/testDirectProxySetInherited.js new file mode 100644 index 000000000..9c8c88093 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetInherited.js @@ -0,0 +1,23 @@ +// When assigning to an object with a proxy is on the prototype chain, +// the proxy's set handler is called. + +var C = {}; +var B = new Proxy(C, { + get() { throw "FAIL"; }, + getOwnPropertyDescriptor() { throw "FAIL"; }, + has() { throw "FAIL"; }, + defineProperty() { throw "FAIL"; }, + set(target, id, value, receiver) { + hits++; + assertEq(target, C); + assertEq(id, "x"); + assertEq(value, 3); + assertEq(receiver, A); + return true; + } +}); +var A = Object.create(B); + +var hits = 0; +A.x = 3; +assertEq(hits, 1); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetNested.js b/js/src/jit-test/tests/proxy/testDirectProxySetNested.js new file mode 100644 index 000000000..20df6bbec --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetNested.js @@ -0,0 +1,15 @@ +// The receiver argument is passed through proxies with no "set" handler. + +var hits; +var a = new Proxy({}, { + set(t, id, value, receiver) { + assertEq(id, "prop"); + assertEq(value, 3); + assertEq(receiver, b); + hits++; + } +}); +var b = new Proxy(a, {}); +hits = 0; +b.prop = 3; +assertEq(hits, 1); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetNested2.js b/js/src/jit-test/tests/proxy/testDirectProxySetNested2.js new file mode 100644 index 000000000..1825ec259 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetNested2.js @@ -0,0 +1,16 @@ +// The receiver argument is passed through prototype chains and proxies with no "set" handler. + +var hits; +var a = new Proxy({}, { + set(t, id, value, receiver) { + assertEq(id, "prop"); + assertEq(value, 3); + assertEq(receiver, b); + hits++; + } +}); +var b = Object.create(Object.create(new Proxy(Object.create(new Proxy(a, {})), {}))); +hits = 0; +b.prop = 3; +assertEq(hits, 1); +assertEq(b.prop, undefined); diff --git a/js/src/jit-test/tests/proxy/testDirectProxySetReceiverLookup.js b/js/src/jit-test/tests/proxy/testDirectProxySetReceiverLookup.js new file mode 100644 index 000000000..e626d805a --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxySetReceiverLookup.js @@ -0,0 +1,57 @@ +// Assigning to a proxy with no set handler causes the proxy's +// getOwnPropertyDescriptor handler to be called just before defineProperty +// in some cases. (ES6 draft rev 28, 2014 Oct 14, 9.1.9 step 5.c.) + +var attrs = ["configurable", "enumerable", "writable", "value", "get", "set"]; + +function test(target, id, existingDesc, expectedDesc) { + var log = ""; + var p = new Proxy(target, { + getOwnPropertyDescriptor(t, idarg) { + assertEq(idarg, id); + log += "g"; + return existingDesc; + }, + defineProperty(t, idarg, desc) { + assertEq(idarg, id); + for (var attr of attrs) { + var args = uneval([target, id, existingDesc]).slice(1, -1); + assertEq(attr in desc, attr in expectedDesc, + `test(${args}), checking existence of desc.${attr}`); + assertEq(desc[attr], expectedDesc[attr], + `test(${args}), checking value of desc.${attr}`); + } + log += "d"; + return true; + } + }); + p[id] = "pizza"; + assertEq(log, "gd"); +} + +var fullDesc = { + configurable: true, + enumerable: true, + writable: true, + value: "pizza" +}; +var valueOnlyDesc = { + value: "pizza" +}; +var sealedDesc = { + configurable: false, + enumerable: true, + writable: true, + value: "pizza" +}; + +test({}, "x", undefined, fullDesc); +test({}, "x", fullDesc, valueOnlyDesc); +test({x: 1}, "x", undefined, fullDesc); +test({x: 1}, "x", fullDesc, valueOnlyDesc); +test(Object.seal({x: 1}), "x", sealedDesc, valueOnlyDesc); +test(Object.create({x: 1}), "x", undefined, fullDesc); +test([0, 1, 2], "2", undefined, fullDesc); +test([0, 1, 2], "2", fullDesc, valueOnlyDesc); +test([0, 1, 2], "3", undefined, fullDesc); +test([0, 1, 2], "3", fullDesc, valueOnlyDesc); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty1.js b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty1.js new file mode 100644 index 000000000..d2c9c18c9 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty1.js @@ -0,0 +1,19 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the current descriptor is non-configurable and the trap + * returns a configurable descriptor + */ +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: false +}); +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + configurable: true + }; + } + }), 'foo'); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty2.js b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty2.js new file mode 100644 index 000000000..b027c6880 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty2.js @@ -0,0 +1,39 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the enumerable fields of the current descriptor and the + * descriptor returned by the trap are the boolean negation of each other + */ +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: false, + enumerable: true +}); +var caught = false; +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + configurable: false, + enumerable: false + }; + } + }), 'foo'); +}, TypeError); + +var target = {}; +Object.defineProperty(target, 'foo', { + configurable: false, + enumerable: false +}); +var caught = false; +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + configurable: false, + enumerable: true + }; + } + }), 'foo'); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty3.js b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty3.js new file mode 100644 index 000000000..34654d3ce --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty3.js @@ -0,0 +1,43 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if the current descriptor is a data descriptor and the + * descriptor returned by the trap is not, or vice versa, and the current + * descriptor is non-configurable + */ +var target = {}; +Object.defineProperty(target, 'foo', { + value: 'bar', + configurable: false +}); +var caught = false; +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + get: function () { + return 'bar'; + }, + configurable: false + }; + } + }), 'foo'); +}, TypeError); + +var target = {}; +Object.defineProperty(target, 'foo', { + value: function () { + return 'bar'; + }, + configurable: false +}); +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + value: 'bar', + configurable: false + }; + } + }), 'foo'); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty4.js b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty4.js new file mode 100644 index 000000000..966bd364e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty4.js @@ -0,0 +1,24 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if both the current descriptor and the descriptor returned + * by the trap are data descriptors, the current descriptor is non-configurable + * and non-writable, and the descriptor returned by the trap is writable. + */ +var target = {}; +Object.defineProperty(target, 'foo', { + value: 'bar', + writable: false, + configurable: false +}); +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + value: 'bar', + writable: true, + configurable: false + }; + } + }), 'foo'); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty5.js b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty5.js new file mode 100644 index 000000000..0cc3cb8e0 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty5.js @@ -0,0 +1,26 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if both the current descriptor and the descriptor returned + * by the trap are data descriptors, the current descriptor is non-configurable + * and non-writable, and the descriptor returned by the trap does not have the + * same value. + */ +var target = {}; +Object.defineProperty(target, 'foo', { + value: 'bar', + writable: false, + configurable: false +}); +var caught = false; +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + value: 'baz', + writable: false, + configurable: false + }; + } + }), 'foo'); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty6.js b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty6.js new file mode 100644 index 000000000..485044c6f --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty6.js @@ -0,0 +1,28 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if both the current descriptor and the descriptor returned + * by the trap are accessor descriptors, the current descriptor is + * non-configurable, and the descriptor returned by the trap has a different + * setter. + */ +var target = {}; +Object.defineProperty(target, 'foo', { + set: function (value) {i + target.foo = 'bar'; + }, + configurable: false +}); +var caught = false; +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + set: function (value) { + target.foo = 'baz'; + }, + configurable: false + }; + } + }), 'foo'); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty7.js b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty7.js new file mode 100644 index 000000000..3c945c131 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyValidateProperty7.js @@ -0,0 +1,28 @@ +load(libdir + "asserts.js"); + +/* + * Throw a TypeError if both the current descriptor and the descriptor returned + * by the trap are accessor descriptors, the current descriptor is + * non-configurable, and the descriptor returned by the trap has a different + * getter. + */ +var target = {}; +Object.defineProperty(target, 'foo', { + get: function () { + return 'bar'; + }, + configurable: false +}); +var caught = false; +assertThrowsInstanceOf(function () { + Object.getOwnPropertyDescriptor(new Proxy(target, { + getOwnPropertyDescriptor: function (target, name) { + return { + get: function () { + return 'baz'; + }, + configurable: false + }; + } + }), 'foo'); +}, TypeError); diff --git a/js/src/jit-test/tests/proxy/testDirectProxyWithForEach.js b/js/src/jit-test/tests/proxy/testDirectProxyWithForEach.js new file mode 100644 index 000000000..7581e409e --- /dev/null +++ b/js/src/jit-test/tests/proxy/testDirectProxyWithForEach.js @@ -0,0 +1,6 @@ +var proxy = new Proxy(['a', 'b', 'c'], {}); +var a = []; +for each (x in proxy) { + a.push(x); +} +assertEq(a.toString(), 'a,b,c'); diff --git a/js/src/jit-test/tests/proxy/testTestIntegrityLevel.js b/js/src/jit-test/tests/proxy/testTestIntegrityLevel.js new file mode 100644 index 000000000..4defb5f60 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testTestIntegrityLevel.js @@ -0,0 +1,30 @@ +// isSealed/isFrozen should short-circuit. + +var count = 0; +var target = Object.preventExtensions({a: 1, b: 2, c: 3}); +var p = new Proxy(target, { + getOwnPropertyDescriptor(t, id) { + count++; + return Object.getOwnPropertyDescriptor(t, id); + } +}); +assertEq(Object.isSealed(p), false); +assertEq(count, 1); + +count = 0; +assertEq(Object.isFrozen(p), false); +assertEq(count, 1); + +Object.seal(target); +count = 0; +assertEq(Object.isSealed(p), true); +assertEq(count, 3); + +count = 0; +assertEq(Object.isFrozen(p), false); +assertEq(count, 1); + +Object.freeze(target); +count = 0; +assertEq(Object.isFrozen(p), true); +assertEq(count, 3); diff --git a/js/src/jit-test/tests/proxy/testWrapWithProtoSet.js b/js/src/jit-test/tests/proxy/testWrapWithProtoSet.js new file mode 100644 index 000000000..913918657 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testWrapWithProtoSet.js @@ -0,0 +1,8 @@ +// A scripted proxy can delegate a [[Set]] along to a target +// that's a different kind of proxy. + +var target = {}; +var wrapper = wrapWithProto(target, null); +var p = new Proxy(wrapper, {}); +p.prop = 3; +assertEq(target.prop, 3); diff --git a/js/src/jit-test/tests/proxy/testWrapperGetInherited.js b/js/src/jit-test/tests/proxy/testWrapperGetInherited.js new file mode 100644 index 000000000..1ff767e92 --- /dev/null +++ b/js/src/jit-test/tests/proxy/testWrapperGetInherited.js @@ -0,0 +1,18 @@ +// Getting a property O.X, inherited from a transparent cross-compartment wrapper W +// that wraps a Proxy P. + +var g = newGlobal(); +var target = {} +var P = new Proxy(target, { + get(t, id, r) { + assertEq(t, target); + assertEq(id, "X"); + assertEq(r, wO); + return "vega"; + } +}); + +g.W = P; +g.eval("var O = Object.create(W);"); +var wO = g.O; +assertEq(g.eval("O.X"), "vega"); |