Source code
Revision control
Copy as Markdown
Other Tools
// Test the VM operations that pop ObjectProtoHasNoIndexedPropsFuse and
// ArrayProtoHasNoIndexedPropsFuse.
function test(code, objectIntact, arrayIntact) {
var g = newGlobal();
g.evaluate(`
function check(objectIntact, arrayIntact) {
var state = getFuseState();
assertEq(state.ObjectProtoHasNoIndexedPropsFuse.intact, objectIntact);
assertEq(state.ArrayProtoHasNoIndexedPropsFuse.intact, arrayIntact);
}
check(true, true);
${code}
check(${objectIntact}, ${arrayIntact});
`);
}
// Popping the Object.prototype fuse also pops the Array.prototype fuse.
function testPopsObject(code) {
test(code, false, false);
}
function testPopsArray(code) {
test(code, true, false);
}
function testDoesNotPop(code) {
test(code, true, true);
}
// Dense elements.
testPopsArray("Array.prototype[0] = 1;");
testPopsObject("Object.prototype[0] = 1;");
testPopsArray("Array.prototype[3] = 1;");
testPopsArray("Object.defineProperty(Array.prototype, 0, {value: 1});");
testPopsObject("Object.defineProperty(Object.prototype, 0, {value: 1});");
testPopsArray("Array.prototype.push(1);");
testPopsObject("Array.prototype.push.call(Object.prototype, 1);");
testPopsArray("Array.prototype.unshift(1);");
testPopsArray("Array.prototype.splice(0, 0, 1, 2);");
testPopsArray("Array.prototype.fill.call(Object.assign(Array.prototype, {length: 2}), 0);");
testPopsArray("Reflect.set(Array.prototype, 0, 1);");
testPopsObject("Object.assign(Object.prototype, [1, 2]);");
testPopsObject("class B { constructor() { return Object.prototype; } } class D extends B { 0 = 1; } new D();");
testPopsArray("class B { constructor() { return Array.prototype; } } class D extends B { [1] = 1; } new D();");
// Sparse indexed properties.
testPopsArray("Array.prototype[1e9] = 1;");
testPopsObject("Object.prototype[4294967294] = 1;");
testPopsObject("Object.defineProperty(Object.prototype, 0, {get() { return 1; }});");
testPopsArray("Object.defineProperty(Array.prototype, 2, {get() { return 1; }});");
testPopsObject("Object.prototype.__defineSetter__(1, function() {});");
// Prototype changes.
testPopsArray("Object.setPrototypeOf(Array.prototype, null);");
testPopsArray("Object.setPrototypeOf(Array.prototype, {});");
testPopsArray("Array.prototype.__proto__ = Object.create(Object.prototype);");
// Popping the Array.prototype fuse doesn't affect the Object.prototype fuse.
testPopsArray("Array.prototype[0] = 1; Array.prototype[1e9] = 2; Object.setPrototypeOf(Array.prototype, null);");
testPopsObject("Array.prototype[0] = 1; Object.prototype[0] = 1;");
// Operations that don't add indexed properties to the prototypes.
testDoesNotPop("Array.prototype.length = 10;");
testDoesNotPop("Array.prototype[-1] = 1; Object.prototype[4294967295] = 1;");
testDoesNotPop("Object.prototype.foo = 1; Array.prototype.bar = 2;");
testDoesNotPop("Array.prototype.push.call(Array.prototype);");
testDoesNotPop("var arr = []; arr[0] = 1; arr.push(2); var obj = {}; obj[0] = 1;");
testDoesNotPop("Object.freeze(Array.prototype); Object.freeze(Object.prototype);");
testDoesNotPop("Object.preventExtensions(Array.prototype);");
testDoesNotPop("Object.setPrototypeOf(Object.prototype, null);");
testDoesNotPop("try { Object.setPrototypeOf(Object.prototype, {}); } catch {}");
testDoesNotPop("Object.setPrototypeOf(Array.prototype, Object.prototype);");
testDoesNotPop("Array.prototype.indexOf(1); Array.prototype.slice(); Array.prototype.concat([1]);");
testDoesNotPop("Object.setPrototypeOf(Object.create(Array.prototype), {})[0] = 1;");
// Indexed properties on prototypes in another realm don't affect this realm.
var g = newGlobal({sameCompartmentAs: this});
assertEq(getFuseState().ArrayProtoHasNoIndexedPropsFuse.intact, true);
g.Array.prototype[0] = 1;
assertEq(g.getFuseState().ArrayProtoHasNoIndexedPropsFuse.intact, false);
assertEq(g.getFuseState().ObjectProtoHasNoIndexedPropsFuse.intact, true);
assertEq(getFuseState().ArrayProtoHasNoIndexedPropsFuse.intact, true);
g = newGlobal({sameCompartmentAs: this});
g.Object.prototype[1e9] = 1;
assertEq(g.getFuseState().ObjectProtoHasNoIndexedPropsFuse.intact, false);
assertEq(g.getFuseState().ArrayProtoHasNoIndexedPropsFuse.intact, false);
assertEq(getFuseState().ObjectProtoHasNoIndexedPropsFuse.intact, true);
assertEq(getFuseState().ArrayProtoHasNoIndexedPropsFuse.intact, true);