script-source-extent.js (14578B)
1 // |jit-test| 2 3 // Test that the sourceStart/sourceEnd values of scripts match the current 4 // expectations. These values are internal details and slightly arbitrary so 5 // these tests expectations can be updated as needed. 6 // 7 // We don't check lineno/column here since they are reasonably robustly 8 // determined from source start offset. 9 10 // We use the Debugger API to introspect locate (possibly hidden) scripts and 11 // inspect their internal source coordinates. While we want new globals for each 12 // test case, they can share the same debuggee compartment. 13 let dbg = new Debugger(); 14 let debuggeeCompartment = newGlobal({newCompartment: true}); 15 16 // Some static class field initializer lambdas may be thrown away by GC. 17 gczeal(0); 18 19 function getScriptSourceExtent(source) { 20 // NOTE: We will _evaluate_ the source below which may introduce dynamic 21 // scripts which are also reported. This is intended so that we may test 22 // those cases too. 23 24 let g = newGlobal({sameCompartmentAs: debuggeeCompartment}); 25 dbg.addDebuggee(g); 26 27 g.evaluate(source); 28 29 // Use Debugger.findScripts to locate scripts, including hidden ones that 30 // are implementation details. 31 let scripts = dbg.findScripts(); 32 33 // Ignore the top-level script since it may or may not be GC'd and is 34 // otherwise uninteresting to us here. 35 scripts = scripts.filter(script => (script.sourceStart > 0) || script.isFunction); 36 37 // Sanity-check that line/column are consistent with sourceStart. If we need 38 // to test multi-line sources, this will need to be updated. 39 for (let script of scripts) { 40 assertEq(script.startLine, 1); 41 assertEq(script.startColumn, script.sourceStart + 1); 42 } 43 44 // Map each found script to a source extent string. 45 function getExtentString(script) { 46 let start = script.sourceStart; 47 let end = script.sourceStart + script.sourceLength; 48 let length = script.sourceLength; 49 50 let resultLength = source.length; 51 assertEq(start < resultLength, true); 52 assertEq(end <= resultLength, true); 53 54 // The result string takes one of following forms: 55 // ` ^ `, when start == end. 56 // ` ^--^ `, typical case. 57 // ` ^----`, when end == resultLength. 58 let result = " ".repeat(start) + "^"; 59 if (end > start) { 60 result += "^".padStart(length, "-"); 61 } 62 return result.padEnd(resultLength) 63 .substring(0, resultLength); 64 } 65 let results = scripts.map(getExtentString); 66 67 // Sort results since `findScripts` does not have deterministic ordering. 68 results.sort(); 69 70 dbg.removeDebuggee(g); 71 72 return results; 73 } 74 75 function testSourceExtent(source, ...expectations) { 76 let actual = getScriptSourceExtent(source); 77 78 // Check that strings of each array match. These should have been presorted. 79 assertEq(actual.length, expectations.length); 80 for (let i = 0; i < expectations.length; ++i) { 81 assertEq(actual[i], expectations[i]); 82 } 83 } 84 85 //////////////////// 86 87 // Function statements. 88 testSourceExtent(`function foo () { }`, 89 ` ^-----`); 90 testSourceExtent(`function foo (a) { }`, 91 ` ^------`); 92 testSourceExtent(`function foo (a, b) { }`, 93 ` ^---------`); 94 95 // Function expressions. 96 testSourceExtent(`let foo = function () { }`, 97 ` ^-----`); 98 testSourceExtent(`let foo = function (a) { }`, 99 ` ^------`); 100 testSourceExtent(`let foo = function (a, b) { }`, 101 ` ^---------`); 102 103 // Named function expressions. 104 testSourceExtent(`let foo = function bar () { }`, 105 ` ^-----`); 106 testSourceExtent(`let foo = function bar (a) { }`, 107 ` ^------`); 108 testSourceExtent(`let foo = function bar (a, b) { }`, 109 ` ^---------`); 110 111 // Arrow functions. 112 testSourceExtent(`let foo = x => { }`, 113 ` ^-------`); 114 testSourceExtent(`let foo = x => { };`, 115 ` ^-------^`); 116 testSourceExtent(`let foo = () => { }`, 117 ` ^--------`); 118 testSourceExtent(`let foo = (a, b) => { }`, 119 ` ^------------`); 120 testSourceExtent(`let foo = x => x`, 121 ` ^-----`); 122 testSourceExtent(`let foo = () => 0`, 123 ` ^------`); 124 125 // Async / Generator functions. 126 testSourceExtent(`function * foo () { }`, 127 ` ^-----`); 128 testSourceExtent(`async function foo () { }`, 129 ` ^-----`); 130 testSourceExtent(`async function * foo () { }`, 131 ` ^-----`); 132 133 // Async arrow functions. 134 testSourceExtent(`let foo = async x => { }`, 135 ` ^-------`); 136 testSourceExtent(`let foo = async () => { }`, 137 ` ^--------`); 138 139 // Basic inner functions. 140 testSourceExtent(`function foo() { function bar () {} }`, 141 ` ^----^ `, 142 ` ^------------------------`); 143 144 // Default parameter expressions. 145 // NOTE: Arrow function parser back-tracking may generate multiple scripts for 146 // the same source text. Delazification expects these copies to have correct 147 // range information for `skipInnerLazyFunction` to work. If syntax parsing is 148 // disabled (such as for coverage builds), these extra functions are not 149 // generated. 150 if (!isLcovEnabled()) { 151 testSourceExtent(`function foo(a = b => c) {}`, 152 ` ^-----^ `, 153 ` ^--------------`); 154 testSourceExtent(`let foo = (a = (b = c => 1) => 2) => 3;`, 155 ` ^-----^ `, 156 ` ^----------------^ `, 157 ` ^---------------------------^`); 158 } 159 160 // Object methods, getters, setters. 161 testSourceExtent(`let obj = { x () {} };`, 162 ` ^----^ `); 163 testSourceExtent(`let obj = { * x () {} };`, 164 ` ^----^ `); 165 testSourceExtent(`let obj = { async x () {} };`, 166 ` ^----^ `); 167 testSourceExtent(`let obj = { async * x () {} };`, 168 ` ^----^ `); 169 testSourceExtent(`let obj = { get x () {} };`, 170 ` ^----^ `); 171 testSourceExtent(`let obj = { set x (v) {} };`, 172 ` ^-----^ `); 173 testSourceExtent(`let obj = { x: function () {} };`, 174 ` ^----^ `); 175 testSourceExtent(`let obj = { x: y => z };`, 176 ` ^-----^ `); 177 178 // Classes without user-defined constructors. 179 testSourceExtent(` class C { } `, 180 ` ^----------^`); 181 testSourceExtent(` let C = class { } `, 182 ` ^--------^`); 183 testSourceExtent(` class C { }; class D extends C { } `, 184 ` ^--------------------^`, 185 ` ^----------^ `); 186 testSourceExtent(` class C { }; let D = class extends C { } `, 187 ` ^------------------^`, 188 ` ^----------^ `); 189 testSourceExtent(`let C = class extends class { } { }`, 190 ` ^--------^ `, 191 ` ^--------------------------`); 192 193 // Classes with user-defined constructors. 194 testSourceExtent(` class C { constructor() { } } `, 195 ` ^-----^ `); 196 testSourceExtent(` let C = class { constructor() { } } `, 197 ` ^-----^ `); 198 testSourceExtent(` class C { }; class D extends C { constructor() { } } `, 199 ` ^-----^ `, 200 ` ^----------^ `); 201 testSourceExtent(` class C { }; let D = class extends C { constructor() { } } `, 202 ` ^-----^ `, 203 ` ^----------^ `); 204 testSourceExtent(`let C = class extends class { } { constructor() { } }`, 205 ` ^-----^ `, 206 ` ^--------^ `); 207 208 // Class field initializers lambdas. 209 // NOTE: These are an implementation detail and may be optimized away in future. 210 testSourceExtent(`class C { field }`, 211 ` ^----^ `, 212 `^----------------`); 213 testSourceExtent(`class C { field; }`, 214 ` ^----^ `, 215 `^-----------------`); 216 testSourceExtent(`class C { "field" }`, 217 ` ^------^ `, 218 `^------------------`); 219 testSourceExtent(`class C { 0 }`, 220 ` ^^ `, 221 `^------------`); 222 testSourceExtent(`class C { [1n] }`, 223 ` ^---^ `, 224 `^---------------`); 225 testSourceExtent(`class C { field = 1 }`, 226 ` ^--------^ `, 227 `^--------------------`); 228 testSourceExtent(`class C { "field" = 1 }`, 229 ` ^----------^ `, 230 `^----------------------`); 231 testSourceExtent(`class C { 0 = 1 }`, 232 ` ^----^ `, 233 `^----------------`); 234 testSourceExtent(`class C { [1n] = 1}`, 235 ` ^-------^`, 236 `^------------------`); 237 238 // Static class field initializer lambdas. 239 // NOTE: These are an implementation detail and may be optimized away in future. 240 testSourceExtent(`class C { static field }`, 241 ` ^----^ `, 242 `^-----------------------`); 243 testSourceExtent(`class C { static field; }`, 244 ` ^----^ `, 245 `^------------------------`); 246 testSourceExtent(`class C { static field = 1 }`, 247 ` ^--------^ `, 248 `^---------------------------`); 249 testSourceExtent(`class C { static [0] = 1 }`, 250 ` ^------^ `, 251 `^-------------------------`); 252 253 // Static class methods, getters, setters. 254 testSourceExtent(`class C { static mtd() {} }`, 255 ` ^----^ `, 256 `^--------------------------`); 257 testSourceExtent(`class C { static * mtd() {} }`, 258 ` ^----^ `, 259 `^----------------------------`); 260 testSourceExtent(`class C { static async mtd() {} }`, 261 ` ^----^ `, 262 `^--------------------------------`); 263 testSourceExtent(`class C { static async * mtd() {} }`, 264 ` ^----^ `, 265 `^----------------------------------`); 266 testSourceExtent(`class C { static get prop() {} }`, 267 ` ^----^ `, 268 `^-------------------------------`); 269 testSourceExtent(`class C { static get [0]() {} }`, 270 ` ^----^ `, 271 `^------------------------------`); 272 testSourceExtent(`class C { static set prop(v) {} }`, 273 ` ^-----^ `, 274 `^--------------------------------`); 275 276 // Private class field initializer lambdas. 277 // NOTE: These are an implementation detail and may be optimized away in future. 278 testSourceExtent(`class C { #field }`, 279 ` ^-----^ `, 280 `^-----------------`); 281 testSourceExtent(`class C { #field = 1 }`, 282 ` ^---------^ `, 283 `^---------------------`); 284 testSourceExtent(`class C { static #field }`, 285 ` ^-----^ `, 286 `^------------------------`); 287 testSourceExtent(`class C { static #field = 1 }`, 288 ` ^---------^ `, 289 `^----------------------------`); 290 291 // Private class methods, getters, setters. 292 // NOTE: These generate both a field initializer lambda and a method script. 293 testSourceExtent(` class C { #field() { } }`, 294 ` ^-----^ `, 295 ` ^-----------------------`); 296 testSourceExtent(` class C { get #field() { } }`, 297 ` ^-----^ `, 298 ` ^---------------^ `, 299 ` ^---------------------------`); 300 testSourceExtent(` class C { set #field(v) { } }`, 301 ` ^------^ `, 302 ` ^----------------^ `, 303 ` ^----------------------------`); 304 testSourceExtent(` class C { * #field() { } }`, 305 ` ^-----^ `, 306 ` ^-------------------------`); 307 testSourceExtent(` class C { async #field() { } }`, 308 ` ^-----^ `, 309 ` ^-----------------------------`); 310 testSourceExtent(` class C { async * #field() { } }`, 311 ` ^-----^ `, 312 ` ^-------------------------------`); 313 314 // Private static class methods. 315 testSourceExtent(` class C { static #mtd() { } }`, 316 ` ^-----^ `, 317 ` ^----------------------------`); 318 testSourceExtent(` class C { static * #mtd() { } }`, 319 ` ^-----^ `, 320 ` ^------------------------------`); 321 testSourceExtent(` class C { static async #mtd() { } }`, 322 ` ^-----^ `, 323 ` ^----------------------------------`); 324 testSourceExtent(` class C { static async * #mtd() { } }`, 325 ` ^-----^ `, 326 ` ^------------------------------------`); 327 testSourceExtent(` class C { static get #prop() { } }`, 328 ` ^-----^ `, 329 ` ^---------------------------------`); 330 testSourceExtent(` class C { static set #prop(v) { } }`, 331 ` ^------^ `, 332 ` ^----------------------------------`); 333 334 // Static Class Blocks 335 testSourceExtent(` class C { static { 10; } }`, 336 ` ^-------------^ `, 337 ` ^-------------------------`);