Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"use strict";
// Test that onEnumProperties returns the expected data
// when passing `ignoreNonIndexedProperties` and `ignoreIndexedProperties` options
// with various objects. (See Bug 1403065)
const DO_NOT_CHECK_VALUE = Symbol();
Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
});
add_task(
threadFrontTest(async ({ threadFront, debuggee, client }) => {
debuggee.eval(
// These arguments are tested.
// eslint-disable-next-line no-unused-vars
function stopMe(arg1) {
debugger;
}.toString()
);
const testCases = [
{
evaledObject: { a: 10 },
expectedIndexedProperties: [],
expectedNonIndexedProperties: [["a", 10]],
},
{
evaledObject: { length: 10 },
expectedIndexedProperties: [],
expectedNonIndexedProperties: [["length", 10]],
},
{
evaledObject: { a: 10, 0: "indexed" },
expectedIndexedProperties: [["0", "indexed"]],
expectedNonIndexedProperties: [["a", 10]],
},
{
evaledObject: { 1: 1, length: 42, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", 42],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: 2.34, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", 2.34],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: -0, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", -0],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: -10, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", -10],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: true, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", true],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: null, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", DO_NOT_CHECK_VALUE],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: Math.pow(2, 53), a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", 9007199254740992],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: "fake", a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", "fake"],
["a", 10],
],
},
{
evaledObject: { 1: 1, length: Infinity, a: 10 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [
["length", DO_NOT_CHECK_VALUE],
["a", 10],
],
},
{
evaledObject: { 0: 0, length: 0 },
expectedIndexedProperties: [["0", 0]],
expectedNonIndexedProperties: [["length", 0]],
},
{
evaledObject: { 0: 0, 1: 1, length: 1 },
expectedIndexedProperties: [
["0", 0],
["1", 1],
],
expectedNonIndexedProperties: [["length", 1]],
},
{
evaledObject: { length: 0 },
expectedIndexedProperties: [],
expectedNonIndexedProperties: [["length", 0]],
},
{
evaledObject: { 1: 1 },
expectedIndexedProperties: [["1", 1]],
expectedNonIndexedProperties: [],
},
{
evaledObject: { a: 1, [2 ** 32 - 2]: 2, [2 ** 32 - 1]: 3 },
expectedIndexedProperties: [["4294967294", 2]],
expectedNonIndexedProperties: [
["a", 1],
["4294967295", 3],
],
},
{
evaledObject: `(() => {
x = [12, 42];
x.foo = 90;
return x;
})()`,
expectedIndexedProperties: [
["0", 12],
["1", 42],
],
expectedNonIndexedProperties: [
["length", 2],
["foo", 90],
],
},
{
evaledObject: `(() => {
x = [12, 42];
x.length = 3;
return x;
})()`,
expectedIndexedProperties: [
["0", 12],
["1", 42],
["2", undefined],
],
expectedNonIndexedProperties: [["length", 3]],
},
{
evaledObject: `(() => {
x = [12, 42];
x.length = 1;
return x;
})()`,
expectedIndexedProperties: [["0", 12]],
expectedNonIndexedProperties: [["length", 1]],
},
{
evaledObject: `(() => {
x = [, 42,,];
x.foo = 90;
return x;
})()`,
expectedIndexedProperties: [
["0", undefined],
["1", 42],
["2", undefined],
],
expectedNonIndexedProperties: [
["length", 3],
["foo", 90],
],
},
{
evaledObject: `(() => {
x = Array(2);
x.foo = "bar";
x.bar = "foo";
return x;
})()`,
expectedIndexedProperties: [
["0", undefined],
["1", undefined],
],
expectedNonIndexedProperties: [
["length", 2],
["foo", "bar"],
["bar", "foo"],
],
},
{
evaledObject: `(() => {
x = new Int8Array(new ArrayBuffer(2));
x.foo = "bar";
x.bar = "foo";
return x;
})()`,
expectedIndexedProperties: [
["0", 0],
["1", 0],
],
expectedNonIndexedProperties: [
["foo", "bar"],
["bar", "foo"],
["length", 2],
["buffer", DO_NOT_CHECK_VALUE],
["byteLength", 2],
["byteOffset", 0],
],
},
{
evaledObject: `(() => {
x = new Int8Array([1, 2]);
Object.defineProperty(x, 'length', {value: 0});
return x;
})()`,
expectedIndexedProperties: [
["0", 1],
["1", 2],
],
expectedNonIndexedProperties: [
["length", 0],
["buffer", DO_NOT_CHECK_VALUE],
["byteLength", 2],
["byteOffset", 0],
],
},
{
evaledObject: `(() => {
x = new Int32Array([1, 2]);
Object.setPrototypeOf(x, null);
return x;
})()`,
expectedIndexedProperties: [
["0", 1],
["1", 2],
],
expectedNonIndexedProperties: [],
},
{
evaledObject: `(() => {
return new (class extends Int8Array {})([1, 2]);
})()`,
expectedIndexedProperties: [
["0", 1],
["1", 2],
],
expectedNonIndexedProperties: [
["length", 2],
["buffer", DO_NOT_CHECK_VALUE],
["byteLength", 2],
["byteOffset", 0],
],
},
];
for (const test of testCases) {
await test_object_grip(debuggee, client, threadFront, test);
}
})
);
async function test_object_grip(
debuggee,
dbgClient,
threadFront,
testData = {}
) {
const {
evaledObject,
expectedIndexedProperties,
expectedNonIndexedProperties,
} = testData;
const packet = await executeOnNextTickAndWaitForPause(eval_code, threadFront);
const [grip] = packet.frame.arguments;
const objClient = threadFront.pauseGrip(grip);
info(`
Check enumProperties response for
${
typeof evaledObject === "string"
? evaledObject
: JSON.stringify(evaledObject)
}
`);
// Checks the result of enumProperties.
let response = await objClient.enumProperties({
ignoreNonIndexedProperties: true,
});
await check_enum_properties(response, expectedIndexedProperties);
response = await objClient.enumProperties({
ignoreIndexedProperties: true,
});
await check_enum_properties(response, expectedNonIndexedProperties);
await threadFront.resume();
function eval_code() {
// Be sure to run debuggee code in its own HTML 'task', so that when we call
// the onDebuggerStatement hook, the test's own microtasks don't get suspended
// along with the debuggee's.
do_timeout(0, () => {
debuggee.eval(`
stopMe(${
typeof evaledObject === "string"
? evaledObject
: JSON.stringify(evaledObject)
});
`);
});
}
}
async function check_enum_properties(iterator, expected = []) {
equal(
iterator.count,
expected.length,
"iterator.count has the expected value"
);
info("Check iterator.slice response for all properties");
const sliceResponse = await iterator.slice(0, iterator.count);
ok(
sliceResponse &&
Object.getOwnPropertyNames(sliceResponse).includes("ownProperties"),
"The response object has an ownProperties property"
);
const { ownProperties } = sliceResponse;
const names = Object.getOwnPropertyNames(ownProperties);
equal(
names.length,
expected.length,
"The response has the expected number of properties"
);
for (let i = 0; i < names.length; i++) {
const name = names[i];
const [key, value] = expected[i];
equal(name, key, "Property has the expected name");
const property = ownProperties[name];
if (value === DO_NOT_CHECK_VALUE) {
return;
}
if (value === undefined) {
equal(
property,
undefined,
`Response has no value for the "${key}" property`
);
} else {
const propValue = property.hasOwnProperty("value")
? property.value
: property.getterValue;
equal(propValue, value, `Property "${key}" has the expected value`);
}
}
}