function assert(b) { if (!b) throw new Error("Bad assertion."); } function test(f) { for (let i = 0; i < 500; i++) f(); } var originalReflect = Reflect; var ownKeys = Reflect.ownKeys; var getOwnPropertyDescriptor = Reflect.getOwnPropertyDescriptor; function runTests() { test(function() { let ownKeysCalled = false; let getOwnPropertyDescriptorProps = []; let getProps = []; let enumerableCalled = false; let handler = { getOwnPropertyDescriptor: function(target, key) { getOwnPropertyDescriptorProps.push(key); switch(key) { case "foo": return { enumerable: true, configurable: true, value: 45 }; case "bar": return { enumerable: true, get enumerable() { enumerableCalled = true; return true; }, configurable: true, value: 50 } case "baz": return { enumerable: false, configurable: true, value: 50 } default: assert(false, "should not be reached."); break; } }, ownKeys: function(target) { ownKeysCalled = true; return ["foo", "bar", "baz"]; }, get: function(target, key) { getProps.push(key); switch(key) { case "foo": return 20; case "bar": return "bar"; default: assert(false, "should not be reached."); break; } } }; let proxy = new Proxy({}, handler); let foo = {}; Object.assign(foo, proxy); assert(enumerableCalled); assert(ownKeys(foo).length === 2); assert(ownKeys(foo)[0] === "foo"); assert(ownKeys(foo)[1] === "bar"); assert(foo.foo === 20); assert(foo.bar === "bar"); assert(ownKeysCalled); assert(getOwnPropertyDescriptorProps.length === 3); assert(getOwnPropertyDescriptorProps[0] === "foo"); assert(getOwnPropertyDescriptorProps[1] === "bar"); assert(getOwnPropertyDescriptorProps[2] === "baz"); assert(getProps.length === 2); assert(getProps[0] === "foo"); assert(getProps[1] === "bar"); }); let oldReflect = Reflect; Reflect = null; assert(Reflect === null); // Make sure Object.assign's use of Reflect is safe. test(function() { let ownKeysCalled = false; let getOwnPropertyDescriptorProps = []; let getProps = []; let enumerableCalled = false; let handler = { getOwnPropertyDescriptor: function(target, key) { getOwnPropertyDescriptorProps.push(key); switch(key) { case "foo": return { enumerable: true, configurable: true, value: 45 }; case "bar": return { get enumerable() { enumerableCalled = true; return true; }, configurable: true, value: 50 } case "baz": return { enumerable: false, configurable: true, value: 50 } default: assert(false, "should not be reached."); break; } }, ownKeys: function(target) { ownKeysCalled = true; return ["foo", "bar", "baz"]; }, get: function(target, key) { getProps.push(key); switch(key) { case "foo": return 20; case "bar": return "bar"; default: assert(false, "should not be reached."); break; } } }; let proxy = new Proxy({}, handler); let foo = {}; Object.assign(foo, proxy); assert(enumerableCalled); assert(ownKeys(foo).length === 2); assert(ownKeys(foo)[0] === "foo"); assert(ownKeys(foo)[1] === "bar"); assert(foo.foo === 20); assert(foo.bar === "bar"); assert(ownKeysCalled); assert(getOwnPropertyDescriptorProps.length === 3); assert(getOwnPropertyDescriptorProps[0] === "foo"); assert(getOwnPropertyDescriptorProps[1] === "bar"); assert(getOwnPropertyDescriptorProps[2] === "baz"); assert(getProps.length === 2); assert(getProps[0] === "foo"); assert(getProps[1] === "bar"); }); Reflect = oldReflect; } runTests(); Reflect.ownKeys = function () {}; Reflect.getOwnPropertyDescriptor = function () {}; runTests();