michael@0: // Test ES6 Proxy trap compliance for Object.isExtensible() on exotic proxy michael@0: // objects. michael@0: var unsealed = {}; michael@0: var sealed = Object.seal({}); michael@0: var handler = {}; michael@0: michael@0: assertEq(Object.isExtensible(unsealed), true); michael@0: assertEq(Object.isExtensible(sealed), false); michael@0: michael@0: var targetSealed = new Proxy(sealed, handler); michael@0: var targetUnsealed = new Proxy(unsealed, handler); michael@0: michael@0: var handlerCalled = false; michael@0: michael@0: // without traps, forward to the target michael@0: // First, make sure we get the obvious answer on a non-exotic target. michael@0: assertEq(Object.isExtensible(targetSealed), false, "Must forward to target without hook."); michael@0: assertEq(Object.isExtensible(targetUnsealed), true, "Must forward to target without hook."); michael@0: michael@0: // Now, keep everyone honest. What if the target itself is a proxy? michael@0: function ensureCalled() { handlerCalled = true; return true; } michael@0: var proxyTarget = new Proxy({}, { isExtensible : ensureCalled }); michael@0: assertEq(Object.isExtensible(new Proxy(proxyTarget, {})), true, "Must forward to target without hook."); michael@0: assertEq(handlerCalled, true, "Must forward to target without hook."); michael@0: michael@0: // with traps, makes sure that the handler is called, and that we throw if the michael@0: // trap disagrees with the target michael@0: function testExtensible(obj, shouldThrow, expectedResult) michael@0: { michael@0: handlerCalled = false; michael@0: if (shouldThrow) michael@0: assertThrowsInstanceOf(function () { Object.isExtensible(obj); }, michael@0: TypeError, "Must throw if handler and target disagree."); michael@0: else michael@0: assertEq(Object.isExtensible(obj), expectedResult, "Must return the correct value."); michael@0: assertEq(handlerCalled, true, "Must call handler trap if present"); michael@0: } michael@0: michael@0: // What if the trap says it's necessarily sealed? michael@0: function fakeSealed() { handlerCalled = true; return false; } michael@0: handler.isExtensible = fakeSealed; michael@0: testExtensible(targetSealed, false, false); michael@0: testExtensible(targetUnsealed, true); michael@0: michael@0: // What if the trap says it's never sealed? michael@0: function fakeUnsealed() { handlerCalled = true; return true; } michael@0: handler.isExtensible = fakeUnsealed; michael@0: testExtensible(targetSealed, true); michael@0: testExtensible(targetUnsealed, false, true); michael@0: michael@0: // make sure we are able to prevent further extensions mid-flight and throw if the michael@0: // hook tries to lie. michael@0: function makeSealedTruth(target) { handlerCalled = true; Object.preventExtensions(target); return false; } michael@0: function makeSealedLie(target) { handlerCalled = true; Object.preventExtensions(target); return true; } michael@0: handler.isExtensible = makeSealedTruth; michael@0: testExtensible(new Proxy({}, handler), false, false); michael@0: handler.isExtensible = makeSealedLie; michael@0: testExtensible(new Proxy({}, handler), true); michael@0: michael@0: // What if the trap doesn't directly return a boolean? michael@0: function falseyNonBool() { handlerCalled = true; return undefined; } michael@0: handler.isExtensible = falseyNonBool; michael@0: testExtensible(targetSealed, false, false); michael@0: testExtensible(targetUnsealed, true); michael@0: michael@0: function truthyNonBool() { handlerCalled = true; return {}; } michael@0: handler.isExtensible = truthyNonBool; michael@0: testExtensible(targetSealed, true); michael@0: testExtensible(targetUnsealed, false, true); michael@0: michael@0: // What if the trap throws? michael@0: function ExtensibleError() { } michael@0: ExtensibleError.prototype = new Error(); michael@0: ExtensibleError.prototype.constructor = ExtensibleError; michael@0: function throwFromTrap() { throw new ExtensibleError(); } michael@0: handler.isExtensible = throwFromTrap; michael@0: michael@0: // exercise some other code paths and make sure that they invoke the trap and michael@0: // can handle the ensuing error. michael@0: assertThrowsInstanceOf(function () { Object.isExtensible(targetSealed); }, michael@0: ExtensibleError, "Must throw if the trap does."); michael@0: assertThrowsInstanceOf(function () { Object.isFrozen(targetSealed); }, michael@0: ExtensibleError, "Must throw if the trap does."); michael@0: assertThrowsInstanceOf(function () { Object.isSealed(targetSealed); }, michael@0: ExtensibleError, "Must throw if the trap does."); michael@0: michael@0: michael@0: // What if the trap likes to re-ask old questions? michael@0: function recurse() { return Object.isExtensible(targetSealed); } michael@0: handler.isExtensible = recurse; michael@0: assertThrowsInstanceOf(function () { Object.isExtensible(targetSealed); }, michael@0: InternalError, "Should allow and detect infinite recurison."); michael@0: michael@0: reportCompare(0, 0, "OK");