test_objectgrips-20.js (10751B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test that onEnumProperties returns the expected data 7 // when passing `ignoreNonIndexedProperties` and `ignoreIndexedProperties` options 8 // with various objects. (See Bug 1403065) 9 10 const DO_NOT_CHECK_VALUE = Symbol(); 11 12 Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); 13 registerCleanupFunction(() => { 14 Services.prefs.clearUserPref("security.allow_eval_with_system_principal"); 15 }); 16 17 add_task( 18 threadFrontTest(async ({ threadFront, debuggee }) => { 19 debuggee.eval( 20 // These arguments are tested. 21 // eslint-disable-next-line no-unused-vars 22 function stopMe(arg1) { 23 debugger; 24 }.toString() 25 ); 26 27 const testCases = [ 28 { 29 evaledObject: { a: 10 }, 30 expectedIndexedProperties: [], 31 expectedNonIndexedProperties: [["a", 10]], 32 }, 33 { 34 evaledObject: { length: 10 }, 35 expectedIndexedProperties: [], 36 expectedNonIndexedProperties: [["length", 10]], 37 }, 38 { 39 evaledObject: { a: 10, 0: "indexed" }, 40 expectedIndexedProperties: [["0", "indexed"]], 41 expectedNonIndexedProperties: [["a", 10]], 42 }, 43 { 44 evaledObject: { 1: 1, length: 42, a: 10 }, 45 expectedIndexedProperties: [["1", 1]], 46 expectedNonIndexedProperties: [ 47 ["length", 42], 48 ["a", 10], 49 ], 50 }, 51 { 52 evaledObject: { 1: 1, length: 2.34, a: 10 }, 53 expectedIndexedProperties: [["1", 1]], 54 expectedNonIndexedProperties: [ 55 ["length", 2.34], 56 ["a", 10], 57 ], 58 }, 59 { 60 evaledObject: { 1: 1, length: -0, a: 10 }, 61 expectedIndexedProperties: [["1", 1]], 62 expectedNonIndexedProperties: [ 63 ["length", -0], 64 ["a", 10], 65 ], 66 }, 67 { 68 evaledObject: { 1: 1, length: -10, a: 10 }, 69 expectedIndexedProperties: [["1", 1]], 70 expectedNonIndexedProperties: [ 71 ["length", -10], 72 ["a", 10], 73 ], 74 }, 75 { 76 evaledObject: { 1: 1, length: true, a: 10 }, 77 expectedIndexedProperties: [["1", 1]], 78 expectedNonIndexedProperties: [ 79 ["length", true], 80 ["a", 10], 81 ], 82 }, 83 { 84 evaledObject: { 1: 1, length: null, a: 10 }, 85 expectedIndexedProperties: [["1", 1]], 86 expectedNonIndexedProperties: [ 87 ["length", DO_NOT_CHECK_VALUE], 88 ["a", 10], 89 ], 90 }, 91 { 92 evaledObject: { 1: 1, length: Math.pow(2, 53), a: 10 }, 93 expectedIndexedProperties: [["1", 1]], 94 expectedNonIndexedProperties: [ 95 ["length", 9007199254740992], 96 ["a", 10], 97 ], 98 }, 99 { 100 evaledObject: { 1: 1, length: "fake", a: 10 }, 101 expectedIndexedProperties: [["1", 1]], 102 expectedNonIndexedProperties: [ 103 ["length", "fake"], 104 ["a", 10], 105 ], 106 }, 107 { 108 evaledObject: { 1: 1, length: Infinity, a: 10 }, 109 expectedIndexedProperties: [["1", 1]], 110 expectedNonIndexedProperties: [ 111 ["length", DO_NOT_CHECK_VALUE], 112 ["a", 10], 113 ], 114 }, 115 { 116 evaledObject: { 0: 0, length: 0 }, 117 expectedIndexedProperties: [["0", 0]], 118 expectedNonIndexedProperties: [["length", 0]], 119 }, 120 { 121 evaledObject: { 0: 0, 1: 1, length: 1 }, 122 expectedIndexedProperties: [ 123 ["0", 0], 124 ["1", 1], 125 ], 126 expectedNonIndexedProperties: [["length", 1]], 127 }, 128 { 129 evaledObject: { length: 0 }, 130 expectedIndexedProperties: [], 131 expectedNonIndexedProperties: [["length", 0]], 132 }, 133 { 134 evaledObject: { 1: 1 }, 135 expectedIndexedProperties: [["1", 1]], 136 expectedNonIndexedProperties: [], 137 }, 138 { 139 evaledObject: { a: 1, [2 ** 32 - 2]: 2, [2 ** 32 - 1]: 3 }, 140 expectedIndexedProperties: [["4294967294", 2]], 141 expectedNonIndexedProperties: [ 142 ["a", 1], 143 ["4294967295", 3], 144 ], 145 }, 146 { 147 evaledObject: `(() => { 148 x = [12, 42]; 149 x.foo = 90; 150 return x; 151 })()`, 152 expectedIndexedProperties: [ 153 ["0", 12], 154 ["1", 42], 155 ], 156 expectedNonIndexedProperties: [ 157 ["length", 2], 158 ["foo", 90], 159 ], 160 }, 161 { 162 evaledObject: `(() => { 163 x = [12, 42]; 164 x.length = 3; 165 return x; 166 })()`, 167 expectedIndexedProperties: [ 168 ["0", 12], 169 ["1", 42], 170 ["2", undefined], 171 ], 172 expectedNonIndexedProperties: [["length", 3]], 173 }, 174 { 175 evaledObject: `(() => { 176 x = [12, 42]; 177 x.length = 1; 178 return x; 179 })()`, 180 expectedIndexedProperties: [["0", 12]], 181 expectedNonIndexedProperties: [["length", 1]], 182 }, 183 { 184 evaledObject: `(() => { 185 x = [, 42,,]; 186 x.foo = 90; 187 return x; 188 })()`, 189 expectedIndexedProperties: [ 190 ["0", undefined], 191 ["1", 42], 192 ["2", undefined], 193 ], 194 expectedNonIndexedProperties: [ 195 ["length", 3], 196 ["foo", 90], 197 ], 198 }, 199 { 200 evaledObject: `(() => { 201 x = Array(2); 202 x.foo = "bar"; 203 x.bar = "foo"; 204 return x; 205 })()`, 206 expectedIndexedProperties: [ 207 ["0", undefined], 208 ["1", undefined], 209 ], 210 expectedNonIndexedProperties: [ 211 ["length", 2], 212 ["foo", "bar"], 213 ["bar", "foo"], 214 ], 215 }, 216 { 217 evaledObject: `(() => { 218 x = new Int8Array(new ArrayBuffer(2)); 219 x.foo = "bar"; 220 x.bar = "foo"; 221 return x; 222 })()`, 223 expectedIndexedProperties: [ 224 ["0", 0], 225 ["1", 0], 226 ], 227 expectedNonIndexedProperties: [ 228 ["foo", "bar"], 229 ["bar", "foo"], 230 ["length", 2], 231 ["buffer", DO_NOT_CHECK_VALUE], 232 ["byteLength", 2], 233 ["byteOffset", 0], 234 ], 235 }, 236 { 237 evaledObject: `(() => { 238 x = new Int8Array([1, 2]); 239 Object.defineProperty(x, 'length', {value: 0}); 240 return x; 241 })()`, 242 expectedIndexedProperties: [ 243 ["0", 1], 244 ["1", 2], 245 ], 246 expectedNonIndexedProperties: [ 247 ["length", 0], 248 ["buffer", DO_NOT_CHECK_VALUE], 249 ["byteLength", 2], 250 ["byteOffset", 0], 251 ], 252 }, 253 { 254 evaledObject: `(() => { 255 x = new Int32Array([1, 2]); 256 Object.setPrototypeOf(x, null); 257 return x; 258 })()`, 259 expectedIndexedProperties: [ 260 ["0", 1], 261 ["1", 2], 262 ], 263 expectedNonIndexedProperties: [], 264 }, 265 { 266 evaledObject: `(() => { 267 return new (class extends Int8Array {})([1, 2]); 268 })()`, 269 expectedIndexedProperties: [ 270 ["0", 1], 271 ["1", 2], 272 ], 273 expectedNonIndexedProperties: [ 274 ["length", 2], 275 ["buffer", DO_NOT_CHECK_VALUE], 276 ["byteLength", 2], 277 ["byteOffset", 0], 278 ], 279 }, 280 { 281 evaledObject: `(() => { 282 x = new BigInt64Array(2); 283 x[0] = 42n; 284 x[1] = 5886014448488689n; 285 return x; 286 })()`, 287 expectedIndexedProperties: [ 288 ["0", { type: "BigInt", text: "42" }], 289 ["1", { type: "BigInt", text: "5886014448488689" }], 290 ], 291 expectedNonIndexedProperties: [ 292 ["length", 2], 293 ["buffer", DO_NOT_CHECK_VALUE], 294 ["byteLength", 2], 295 ["byteOffset", 0], 296 ], 297 }, 298 ]; 299 300 for (const test of testCases) { 301 await test_object_grip(debuggee, threadFront, test); 302 } 303 }) 304 ); 305 306 async function test_object_grip(debuggee, threadFront, testData = {}) { 307 const { 308 evaledObject, 309 expectedIndexedProperties, 310 expectedNonIndexedProperties, 311 } = testData; 312 313 const packet = await executeOnNextTickAndWaitForPause(eval_code, threadFront); 314 315 const [grip] = packet.frame.arguments; 316 317 const objClient = threadFront.pauseGrip(grip); 318 319 info(` 320 Check enumProperties response for 321 ${ 322 typeof evaledObject === "string" 323 ? evaledObject 324 : JSON.stringify(evaledObject) 325 } 326 `); 327 328 // Checks the result of enumProperties. 329 let response = await objClient.enumProperties({ 330 ignoreNonIndexedProperties: true, 331 }); 332 await check_enum_properties(response, expectedIndexedProperties); 333 334 response = await objClient.enumProperties({ 335 ignoreIndexedProperties: true, 336 }); 337 await check_enum_properties(response, expectedNonIndexedProperties); 338 339 await threadFront.resume(); 340 341 function eval_code() { 342 // Be sure to run debuggee code in its own HTML 'task', so that when we call 343 // the onDebuggerStatement hook, the test's own microtasks don't get suspended 344 // along with the debuggee's. 345 do_timeout(0, () => { 346 debuggee.eval(` 347 stopMe(${ 348 typeof evaledObject === "string" 349 ? evaledObject 350 : JSON.stringify(evaledObject) 351 }); 352 `); 353 }); 354 } 355 } 356 357 async function check_enum_properties(iterator, expected = []) { 358 equal( 359 iterator.count, 360 expected.length, 361 "iterator.count has the expected value" 362 ); 363 364 info("Check iterator.slice response for all properties"); 365 const sliceResponse = await iterator.slice(0, iterator.count); 366 ok( 367 sliceResponse && 368 Object.getOwnPropertyNames(sliceResponse).includes("ownProperties"), 369 "The response object has an ownProperties property" 370 ); 371 372 const { ownProperties } = sliceResponse; 373 const names = Object.getOwnPropertyNames(ownProperties); 374 equal( 375 names.length, 376 expected.length, 377 "The response has the expected number of properties" 378 ); 379 for (let i = 0; i < names.length; i++) { 380 const name = names[i]; 381 const [key, value] = expected[i]; 382 equal(name, key, "Property has the expected name"); 383 const property = ownProperties[name]; 384 385 if (value === DO_NOT_CHECK_VALUE) { 386 return; 387 } 388 389 if (value === undefined) { 390 equal( 391 property, 392 undefined, 393 `Response has no value for the "${key}" property` 394 ); 395 } else { 396 const propValue = property.hasOwnProperty("value") 397 ? property.value 398 : property.getterValue; 399 Assert.deepEqual( 400 propValue, 401 value, 402 `Property "${key}" has the expected value` 403 ); 404 } 405 } 406 }