diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/tests/non262/extensions/setImmutablePrototype.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | js/src/tests/non262/extensions/setImmutablePrototype.js | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/js/src/tests/non262/extensions/setImmutablePrototype.js b/js/src/tests/non262/extensions/setImmutablePrototype.js new file mode 100644 index 0000000000..9d107472e5 --- /dev/null +++ b/js/src/tests/non262/extensions/setImmutablePrototype.js @@ -0,0 +1,196 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var gTestfile = "setImmutablePrototype.js"; +//----------------------------------------------------------------------------- +var BUGNUMBER = 1052139; +var summary = + "Implement JSAPI and a shell function to prevent modifying an extensible " + + "object's [[Prototype]]"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +if (typeof evaluate !== "function") +{ + // Not totally faithful semantics, but approximately close enough for this + // test's purposes. + evaluate = eval; +} + +var usingRealSetImmutablePrototype = true; + +if (typeof setImmutablePrototype !== "function") +{ + usingRealSetImmutablePrototype = false; + + if (typeof SpecialPowers !== "undefined") + { + setImmutablePrototype = + SpecialPowers.Cu.getJSTestingFunctions().setImmutablePrototype; + } +} + +if (typeof wrap !== "function") +{ + // good enough + wrap = function(x) { return x; }; +} + +function setViaProtoSetter(obj, newProto) +{ + var setter = + Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").set; + setter.call(obj, newProto); +} + +function checkPrototypeMutationFailure(obj, desc) +{ + var initialProto = Object.getPrototypeOf(obj); + + // disconnected from any [[Prototype]] chains for use on any object at all + var newProto = Object.create(null); + + function tryMutate(func, method) + { + try + { + func(obj, newProto); + throw new Error(desc + ": no error thrown, prototype " + + (Object.getPrototypeOf(obj) === initialProto + ? "wasn't" + : "was") + + " changed"); + } + catch (e) + { + // Note: This is always a TypeError from *this* global object, because + // Object.setPrototypeOf and the __proto__ setter come from *this* + // global object. + assertEq(e instanceof TypeError, true, + desc + ": should have thrown TypeError setting [[Prototype]] " + + "via " + method + ", got " + e); + assertEq(Object.getPrototypeOf(obj), initialProto, + desc + ": shouldn't observe [[Prototype]] change"); + } + } + + tryMutate(Object.setPrototypeOf, "Object.setPrototypeOf"); + tryMutate(setViaProtoSetter, "__proto__ setter"); +} + +function runNormalTests(global) +{ + if (typeof setImmutablePrototype !== "function") + { + print("no testable setImmutablePrototype function available, skipping tests"); + return; + } + + // regular old object, non-null [[Prototype]] + + var emptyLiteral = global.evaluate("({})"); + assertEq(setImmutablePrototype(emptyLiteral), true); + checkPrototypeMutationFailure(emptyLiteral, "empty literal"); + + // regular old object, null [[Prototype]] + + var nullProto = global.Object.create(null); + assertEq(setImmutablePrototype(nullProto), true); + checkPrototypeMutationFailure(nullProto, "nullProto"); + + // Shocker: SpecialPowers's little mind doesn't understand proxies. Abort. + if (!usingRealSetImmutablePrototype) + return; + + // direct proxies + + var emptyTarget = global.evaluate("({})"); + var directProxy = new global.Proxy(emptyTarget, {}); + assertEq(setImmutablePrototype(directProxy), true); + checkPrototypeMutationFailure(directProxy, "direct proxy to empty target"); + checkPrototypeMutationFailure(emptyTarget, "empty target"); + + var anotherTarget = global.evaluate("({})"); + var anotherDirectProxy = new global.Proxy(anotherTarget, {}); + assertEq(setImmutablePrototype(anotherTarget), true); + checkPrototypeMutationFailure(anotherDirectProxy, "another direct proxy to empty target"); + checkPrototypeMutationFailure(anotherTarget, "another empty target"); + + var nestedTarget = global.evaluate("({})"); + var nestedProxy = new global.Proxy(new Proxy(nestedTarget, {}), {}); + assertEq(setImmutablePrototype(nestedProxy), true); + checkPrototypeMutationFailure(nestedProxy, "nested proxy to empty target"); + checkPrototypeMutationFailure(nestedTarget, "nested target"); + + // revocable proxies + + var revocableTarget = global.evaluate("({})"); + var revocable = global.Proxy.revocable(revocableTarget, {}); + assertEq(setImmutablePrototype(revocable.proxy), true); + checkPrototypeMutationFailure(revocableTarget, "revocable target"); + checkPrototypeMutationFailure(revocable.proxy, "revocable proxy"); + + assertEq(revocable.revoke(), undefined); + try + { + setImmutablePrototype(revocable.proxy); + throw new Error("expected to throw on revoked proxy"); + } + catch (e) + { + // Note: In the cross-compartment case, this is a TypeError from |global|, + // because the proxy's |setImmutablePrototype| method is what actually + // throws here. That's why we check for TypeError from either global. + // (Usually the method simply sets |*succeeded| to false and the + // caller handles or throws as needed after that. But not here.) + assertEq(e instanceof global.TypeError || e instanceof TypeError, true, + "expected TypeError, instead threw " + e); + } + + var anotherRevocableTarget = global.evaluate("({})"); + assertEq(setImmutablePrototype(anotherRevocableTarget), true); + checkPrototypeMutationFailure(anotherRevocableTarget, "another revocable target"); + + var anotherRevocable = global.Proxy.revocable(anotherRevocableTarget, {}); + checkPrototypeMutationFailure(anotherRevocable.proxy, "another revocable target"); + + assertEq(anotherRevocable.revoke(), undefined); + try + { + var rv = setImmutablePrototype(anotherRevocable.proxy); + throw new Error("expected to throw on another revoked proxy, returned " + rv); + } + catch (e) + { + // NOTE: Again from |global| or |this|, as above. + assertEq(e instanceof global.TypeError || e instanceof TypeError, true, + "expected TypeError, instead threw " + e); + } +} + +var global = this; +runNormalTests(global); // same-global + +if (typeof newGlobal === "function") +{ + var otherGlobal = newGlobal(); + var subsumingNothing = newGlobal({ principal: 0 }); + var subsumingEverything = newGlobal({ principal: ~0 }); + + runNormalTests(otherGlobal); // cross-global + runNormalTests(subsumingNothing); + runNormalTests(subsumingEverything); +} + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); |