function assert(b) { if (!b) throw new Error("Bad assertion"); } { let error = null; let target = { }; let handler = { ownKeys: function() { error = new Error; throw error; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e === error); } assert(threw); } } { let error = null; let target = { }; let handler = { get ownKeys() { error = new Error; throw error; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e === error); } assert(threw); } } { let target = { x: 40 }; let called = false; let handler = { ownKeys: function(theTarget) { called = true; return ["1", 2, 3]; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e.toString() === "TypeError: Proxy handler's 'ownKeys' method must return an array-like object containing only Strings and Symbols"); } assert(threw); assert(called); called = false; } } { let target = { }; Object.defineProperty(target, "x", { configurable: false, enumerable: true, value: 400 }); let called = false; let handler = { ownKeys: function(theTarget) { called = true; return []; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e.toString() === "TypeError: Proxy object's 'target' has the non-configurable property 'x' that was not in the result from the 'ownKeys' trap"); } assert(threw); assert(called); called = false; } } { let target = { }; Object.defineProperty(target, "x", { configurable: true, enumerable: true, value: 400 }); Object.preventExtensions(target); let called = false; let handler = { ownKeys: function(theTarget) { called = true; return []; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e.toString() === "TypeError: Proxy object's non-extensible 'target' has configurable property 'x' that was not in the result from the 'ownKeys' trap"); } assert(threw); assert(called); called = false; } } { let target = { }; Object.defineProperty(target, "x", { configurable: true, enumerable: true, value: 400 }); Object.preventExtensions(target); let called = false; let handler = { ownKeys: function(theTarget) { called = true; return ["x", "y"]; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e.toString() === "TypeError: Proxy handler's 'ownKeys' method returned a key that was not present in its non-extensible target"); } assert(threw); assert(called); called = false; } } { let target = {}; let called1 = false; let called2 = false; Object.defineProperty(target, 'a', { value: 42, configurable: false }); let p1 = new Proxy(target, { ownKeys() { called1 = true; return ['a', 'a']; } }); let p2 = new Proxy(p1, { ownKeys() { called2 = true; return ['a']; } }); for (let i = 0; i < 500; i++) { // FIXME: we may update the spec to make this test not throw. // see: https://github.com/tc39/ecma262/pull/594 let threw = false; try { Reflect.ownKeys(p2); } catch(e) { assert(e.toString() === "TypeError: Proxy object's 'target' has the non-configurable property 'a' that was not in the result from the 'ownKeys' trap"); threw = true; } assert(threw); assert(called1); assert(called2); } } { let target = {}; let called1 = false; let called2 = false; Object.defineProperty(target, 'a', { value: 42, configurable: true }); Object.preventExtensions(target); let p1 = new Proxy(target, { ownKeys() { called1 = true; return ['a', 'a']; } }); let p2 = new Proxy(p1, { ownKeys() { called2 = true; return ['a']; } }); for (let i = 0; i < 500; i++) { // FIXME: we may update the spec to make this test not throw. // see: https://github.com/tc39/ecma262/pull/594 let threw = false; try { Reflect.ownKeys(p2); } catch(e) { assert(e.toString() === "TypeError: Proxy object's non-extensible 'target' has configurable property 'a' that was not in the result from the 'ownKeys' trap"); threw = true; } assert(threw); assert(called1); assert(called2); } } { let target = { }; Object.defineProperty(target, "x", { configurable: true, enumerable: true, value: 400 }); Object.preventExtensions(target); let called = false; let handler = { ownKeys: function(theTarget) { called = true; return ["x", "x"]; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { Object.keys(proxy); assert(called); called = false; } } { let target = { }; let handler = { ownKeys: 45 }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e.toString() === "TypeError: 'ownKeys' property of a Proxy's handler should be callable"); } assert(threw); } } function shallowEq(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } { let target = { x: 40 }; let called = false; let arr = ["a", "b", "c"]; let handler = { ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let result = Object.keys(proxy); assert(result !== arr); assert(shallowEq(result, [])); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let arr = ["a", "b", "c"]; let handler = { getOwnPropertyDescriptor: function(theTarget, propertyName) { if (arr.indexOf(propertyName) >= 0) { return { enumerable: true, configurable: true }; } return Reflect.getOwnPropertyDescriptor(theTarget, propertyName); }, ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let result = Object.keys(proxy); assert(result !== arr); assert(shallowEq(result, arr)); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let arr = ["a", "b", "c"]; let handler = { ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let result = Reflect.ownKeys(proxy); assert(result !== arr); assert(shallowEq(result, arr)); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let result = Object.getOwnPropertySymbols(proxy); assert(shallowEq(result, [s1, s2])); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { getOwnPropertyDescriptor(theTarget, propertyName) { if (arr.indexOf(propertyName) >= 0) { return { enumerable: true, configurable: true } } return Reflect.getOwnPropertyDescriptor(theTarget, propertyName); }, ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let result = Object.keys(proxy); assert(shallowEq(result, ["a", "b", "c"])); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let result = Reflect.ownKeys(proxy); assert(shallowEq(result, ["a", "b", "c", s1, s2])); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { getOwnPropertyDescriptor: () => { return { enumerable: true, configurable: true } }, ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let set = new Set; for (let p in proxy) set.add(p); assert(set.size === 3); assert(set.has("a")); assert(set.has("b")); assert(set.has("c")); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { getOwnPropertyDescriptor: () => { return { enumerable: true, configurable: true } }, ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let set = new Set; for (let p in proxy) set.add(p); if (i === 40) { // Make sure we don't cache the result. arr.push("d"); } assert(set.size === i > 40 ? 4 : 3); assert(set.has("a")); assert(set.has("b")); assert(set.has("c")); if (i > 40) assert(set.has("d")); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { getOwnPropertyDescriptor: () => { return { enumerable: true, configurable: true } }, ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); let proxyish = Object.create(proxy, { d: { enumerable: true, configurable: true } }); for (let i = 0; i < 500; i++) { let set = new Set; for (let p in proxyish) set.add(p); assert(set.size === 4); assert(set.has("a")); assert(set.has("b")); assert(set.has("c")); assert(set.has("d")); assert(called); called = false; } } { let target = { x: 40 }; let called = false; let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { getOwnPropertyDescriptor: () => { return { enumerable: true, configurable: true } }, ownKeys: function(theTarget) { called = true; return arr; } }; let proxy = new Proxy(target, handler); let proxyish = Object.create(proxy, { d: { enumerable: true, configurable: true } }); for (let i = 0; i < 500; i++) { let set = new Set; for (let p in proxyish) set.add(p); assert(set.size === 4); assert(set.has("a")); assert(set.has("b")); assert(set.has("c")); assert(set.has("d")); assert(called); called = false; } } { let called = false; let target = {x: 20, y: 40}; let handler = { ownKeys: null }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let keys = Object.keys(proxy); assert(keys.indexOf("x") !== -1); assert(keys.indexOf("y") !== -1); } } { let called = false; let target = new Proxy({}, { ownKeys: function(theTarget) { called = true; return Reflect.ownKeys(theTarget); } }); let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { ownKeys: function(theTarget) { return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let keys = Object.keys(proxy); assert(called); called = false; } } { let error = null; let target = new Proxy({}, { ownKeys: function(theTarget) { error = new Error; throw error; } }); let s1 = Symbol(); let s2 = Symbol(); let arr = ["a", "b", s1, "c", s2]; let handler = { ownKeys: function(theTarget) { return arr; } }; let proxy = new Proxy(target, handler); for (let i = 0; i < 500; i++) { let threw = false; try { Object.keys(proxy); } catch(e) { threw = true; assert(e === error); } assert(threw); error = null; } }