Bug 1406085 - Only consider array indices to be indexed properties.
MozReview-Commit-ID: AEH4BeFunxh
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -866,23 +866,23 @@ PropertyIteratorActor.prototype = {
PropertyIteratorActor.prototype.requestTypes = {
"names": PropertyIteratorActor.prototype.names,
"slice": PropertyIteratorActor.prototype.slice,
"all": PropertyIteratorActor.prototype.all,
};
function enumArrayProperties(objectActor, options) {
let length = DevToolsUtils.getProperty(objectActor.obj, "length");
- if (!isSafePositiveInteger(length)) {
+ if (!isUint32(length)) {
// Pseudo arrays are flagged as ArrayLike if they have
// subsequent indexed properties without having any length attribute.
length = 0;
let names = objectActor.obj.getOwnPropertyNames();
for (let key of names) {
- if (!isSafeIndex(key) || key != length++) {
+ if (!isArrayIndex(key) || key != length++) {
break;
}
}
}
return {
size: length,
propertyName(index) {
@@ -903,32 +903,32 @@ function enumObjectProperties(objectActo
// allowed: "cannot modify properties of a WrappedNative". See bug 952093.
}
if (options.ignoreNonIndexedProperties || options.ignoreIndexedProperties) {
let length = DevToolsUtils.getProperty(objectActor.obj, "length");
let sliceIndex;
const isLengthTrustworthy =
- isSafePositiveInteger(length)
- && (length > 0 && isSafeIndex(names[length - 1]))
- && !isSafeIndex(names[length]);
+ isUint32(length)
+ && (!length || isArrayIndex(names[length - 1]))
+ && !isArrayIndex(names[length]);
if (!isLengthTrustworthy) {
// The length property may not reflect what the object looks like, let's find
// where indexed properties end.
- if (!isSafeIndex(names[0])) {
+ if (!isArrayIndex(names[0])) {
// If the first item is not a number, this means there is no indexed properties
// in this object.
sliceIndex = 0;
} else {
sliceIndex = names.length;
while (sliceIndex > 0) {
- if (isSafeIndex(names[sliceIndex - 1])) {
+ if (isArrayIndex(names[sliceIndex - 1])) {
break;
}
sliceIndex--;
}
}
} else {
sliceIndex = length;
}
@@ -2527,38 +2527,40 @@ function isTypedArray(object) {
* The debuggee object to test.
* @return Boolean
*/
function isArray(object) {
return isTypedArray(object) || object.class === "Array";
}
/**
- * Returns true if the parameter is a safe positive integer.
+ * Returns true if the parameter can be stored as a 32-bit unsigned integer.
+ * If so, it will be suitable for use as the length of an array object.
*
* @param num Number
* The number to test.
* @return Boolean
*/
-function isSafePositiveInteger(num) {
- return Number.isSafeInteger(num) && 1 / num > 0;
+function isUint32(num) {
+ return num >>> 0 === num;
}
/**
* Returns true if the parameter is suitable to be an array index.
*
- * @param num Any
+ * @param str String
* @return Boolean
*/
-function isSafeIndex(str) {
- // Transform the parameter to a number using the Unary operator.
- let num = +str;
- return isSafePositiveInteger(num) &&
- // Check the string since unary can transform non number (boolean, null, …).
- num + "" === str;
+function isArrayIndex(str) {
+ // Transform the parameter to a 32-bit unsigned integer.
+ let num = str >>> 0;
+ // Check that the parameter is a canonical Uint32 index.
+ return num + "" === str &&
+ // Array indices cannot attain the maximum Uint32 value.
+ num != -1 >>> 0;
}
exports.ObjectActor = ObjectActor;
exports.PropertyIteratorActor = PropertyIteratorActor;
exports.LongStringActor = LongStringActor;
exports.createValueGrip = createValueGrip;
exports.stringIsLong = stringIsLong;
exports.longStringGrip = longStringGrip;
--- a/devtools/server/tests/unit/test_objectgrips-20.js
+++ b/devtools/server/tests/unit/test_objectgrips-20.js
@@ -88,16 +88,20 @@ async function run_test_with_server(serv
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]],
}, {