// |jit-test| // Test that the sourceStart/sourceEnd values of scripts match the current // expectations. These values are internal details and slightly arbitrary so // these tests expectations can be updated as needed. // // We don't check lineno/column here since they are reasonably robustly // determined from source start offset. // We use the Debugger API to introspect locate (possibly hidden) scripts and // inspect their internal source coordinates. While we want new globals for each // test case, they can share the same debuggee compartment. let dbg = new Debugger(); let debuggeeCompartment = newGlobal({newCompartment: true}); // Some static class field initializer lambdas may be thrown away by GC. gczeal(0); function getScriptSourceExtent(source) { // NOTE: We will _evaluate_ the source below which may introduce dynamic // scripts which are also reported. This is intended so that we may test // those cases too. let g = newGlobal({sameCompartmentAs: debuggeeCompartment}); dbg.addDebuggee(g); g.evaluate(source); // Use Debugger.findScripts to locate scripts, including hidden ones that // are implementation details. let scripts = dbg.findScripts(); // Ignore the top-level script since it may or may not be GC'd and is // otherwise uninteresting to us here. scripts = scripts.filter(script => (script.sourceStart > 0) || script.isFunction); // Sanity-check that line/column are consistent with sourceStart. If we need // to test multi-line sources, this will need to be updated. for (let script of scripts) { assertEq(script.startLine, 1); assertEq(script.startColumn, script.sourceStart + 1); } // Map each found script to a source extent string. function getExtentString(script) { let start = script.sourceStart; let end = script.sourceStart + script.sourceLength; let length = script.sourceLength; let resultLength = source.length; assertEq(start < resultLength, true); assertEq(end <= resultLength, true); // The result string takes one of following forms: // ` ^ `, when start == end. // ` ^--^ `, typical case. // ` ^----`, when end == resultLength. let result = " ".repeat(start) + "^"; if (end > start) { result += "^".padStart(length, "-"); } return result.padEnd(resultLength) .substring(0, resultLength); } let results = scripts.map(getExtentString); // Sort results since `findScripts` does not have deterministic ordering. results.sort(); dbg.removeDebuggee(g); return results; } function testSourceExtent(source, ...expectations) { let actual = getScriptSourceExtent(source); // Check that strings of each array match. These should have been presorted. assertEq(actual.length, expectations.length); for (let i = 0; i < expectations.length; ++i) { assertEq(actual[i], expectations[i]); } } //////////////////// // Function statements. testSourceExtent(`function foo () { }`, ` ^-----`); testSourceExtent(`function foo (a) { }`, ` ^------`); testSourceExtent(`function foo (a, b) { }`, ` ^---------`); // Function expressions. testSourceExtent(`let foo = function () { }`, ` ^-----`); testSourceExtent(`let foo = function (a) { }`, ` ^------`); testSourceExtent(`let foo = function (a, b) { }`, ` ^---------`); // Named function expressions. testSourceExtent(`let foo = function bar () { }`, ` ^-----`); testSourceExtent(`let foo = function bar (a) { }`, ` ^------`); testSourceExtent(`let foo = function bar (a, b) { }`, ` ^---------`); // Arrow functions. testSourceExtent(`let foo = x => { }`, ` ^-------`); testSourceExtent(`let foo = x => { };`, ` ^-------^`); testSourceExtent(`let foo = () => { }`, ` ^--------`); testSourceExtent(`let foo = (a, b) => { }`, ` ^------------`); testSourceExtent(`let foo = x => x`, ` ^-----`); testSourceExtent(`let foo = () => 0`, ` ^------`); // Async / Generator functions. testSourceExtent(`function * foo () { }`, ` ^-----`); testSourceExtent(`async function foo () { }`, ` ^-----`); testSourceExtent(`async function * foo () { }`, ` ^-----`); // Async arrow functions. testSourceExtent(`let foo = async x => { }`, ` ^-------`); testSourceExtent(`let foo = async () => { }`, ` ^--------`); // Basic inner functions. testSourceExtent(`function foo() { function bar () {} }`, ` ^----^ `, ` ^------------------------`); // Default parameter expressions. // NOTE: Arrow function parser back-tracking may generate multiple scripts for // the same source text. Delazification expects these copies to have correct // range information for `skipInnerLazyFunction` to work. If syntax parsing is // disabled (such as for coverage builds), these extra functions are not // generated. if (!isLcovEnabled()) { testSourceExtent(`function foo(a = b => c) {}`, ` ^-----^ `, ` ^--------------`); testSourceExtent(`let foo = (a = (b = c => 1) => 2) => 3;`, ` ^-----^ `, ` ^----------------^ `, ` ^---------------------------^`); } // Object methods, getters, setters. testSourceExtent(`let obj = { x () {} };`, ` ^----^ `); testSourceExtent(`let obj = { * x () {} };`, ` ^----^ `); testSourceExtent(`let obj = { async x () {} };`, ` ^----^ `); testSourceExtent(`let obj = { async * x () {} };`, ` ^----^ `); testSourceExtent(`let obj = { get x () {} };`, ` ^----^ `); testSourceExtent(`let obj = { set x (v) {} };`, ` ^-----^ `); testSourceExtent(`let obj = { x: function () {} };`, ` ^----^ `); testSourceExtent(`let obj = { x: y => z };`, ` ^-----^ `); // Classes without user-defined constructors. testSourceExtent(` class C { } `, ` ^----------^`); testSourceExtent(` let C = class { } `, ` ^--------^`); testSourceExtent(` class C { }; class D extends C { } `, ` ^--------------------^`, ` ^----------^ `); testSourceExtent(` class C { }; let D = class extends C { } `, ` ^------------------^`, ` ^----------^ `); testSourceExtent(`let C = class extends class { } { }`, ` ^--------^ `, ` ^--------------------------`); // Classes with user-defined constructors. testSourceExtent(` class C { constructor() { } } `, ` ^-----^ `); testSourceExtent(` let C = class { constructor() { } } `, ` ^-----^ `); testSourceExtent(` class C { }; class D extends C { constructor() { } } `, ` ^-----^ `, ` ^----------^ `); testSourceExtent(` class C { }; let D = class extends C { constructor() { } } `, ` ^-----^ `, ` ^----------^ `); testSourceExtent(`let C = class extends class { } { constructor() { } }`, ` ^-----^ `, ` ^--------^ `); // Class field initializers lambdas. // NOTE: These are an implementation detail and may be optimized away in future. testSourceExtent(`class C { field }`, ` ^----^ `, `^----------------`); testSourceExtent(`class C { field; }`, ` ^----^ `, `^-----------------`); testSourceExtent(`class C { "field" }`, ` ^------^ `, `^------------------`); testSourceExtent(`class C { 0 }`, ` ^^ `, `^------------`); testSourceExtent(`class C { [1n] }`, ` ^---^ `, `^---------------`); testSourceExtent(`class C { field = 1 }`, ` ^--------^ `, `^--------------------`); testSourceExtent(`class C { "field" = 1 }`, ` ^----------^ `, `^----------------------`); testSourceExtent(`class C { 0 = 1 }`, ` ^----^ `, `^----------------`); testSourceExtent(`class C { [1n] = 1}`, ` ^-------^`, `^------------------`); // Static class field initializer lambdas. // NOTE: These are an implementation detail and may be optimized away in future. testSourceExtent(`class C { static field }`, ` ^----^ `, `^-----------------------`); testSourceExtent(`class C { static field; }`, ` ^----^ `, `^------------------------`); testSourceExtent(`class C { static field = 1 }`, ` ^--------^ `, `^---------------------------`); testSourceExtent(`class C { static [0] = 1 }`, ` ^------^ `, `^-------------------------`); // Static class methods, getters, setters. testSourceExtent(`class C { static mtd() {} }`, ` ^----^ `, `^--------------------------`); testSourceExtent(`class C { static * mtd() {} }`, ` ^----^ `, `^----------------------------`); testSourceExtent(`class C { static async mtd() {} }`, ` ^----^ `, `^--------------------------------`); testSourceExtent(`class C { static async * mtd() {} }`, ` ^----^ `, `^----------------------------------`); testSourceExtent(`class C { static get prop() {} }`, ` ^----^ `, `^-------------------------------`); testSourceExtent(`class C { static get [0]() {} }`, ` ^----^ `, `^------------------------------`); testSourceExtent(`class C { static set prop(v) {} }`, ` ^-----^ `, `^--------------------------------`); // Private class field initializer lambdas. // NOTE: These are an implementation detail and may be optimized away in future. testSourceExtent(`class C { #field }`, ` ^-----^ `, `^-----------------`); testSourceExtent(`class C { #field = 1 }`, ` ^---------^ `, `^---------------------`); testSourceExtent(`class C { static #field }`, ` ^-----^ `, `^------------------------`); testSourceExtent(`class C { static #field = 1 }`, ` ^---------^ `, `^----------------------------`); // Private class methods, getters, setters. // NOTE: These generate both a field initializer lambda and a method script. testSourceExtent(` class C { #field() { } }`, ` ^-----^ `, ` ^-----------------------`); testSourceExtent(` class C { get #field() { } }`, ` ^-----^ `, ` ^---------------^ `, ` ^---------------------------`); testSourceExtent(` class C { set #field(v) { } }`, ` ^------^ `, ` ^----------------^ `, ` ^----------------------------`); testSourceExtent(` class C { * #field() { } }`, ` ^-----^ `, ` ^-------------------------`); testSourceExtent(` class C { async #field() { } }`, ` ^-----^ `, ` ^-----------------------------`); testSourceExtent(` class C { async * #field() { } }`, ` ^-----^ `, ` ^-------------------------------`); // Private static class methods. testSourceExtent(` class C { static #mtd() { } }`, ` ^-----^ `, ` ^----------------------------`); testSourceExtent(` class C { static * #mtd() { } }`, ` ^-----^ `, ` ^------------------------------`); testSourceExtent(` class C { static async #mtd() { } }`, ` ^-----^ `, ` ^----------------------------------`); testSourceExtent(` class C { static async * #mtd() { } }`, ` ^-----^ `, ` ^------------------------------------`); testSourceExtent(` class C { static get #prop() { } }`, ` ^-----^ `, ` ^---------------------------------`); testSourceExtent(` class C { static set #prop(v) { } }`, ` ^------^ `, ` ^----------------------------------`); // Static Class Blocks testSourceExtent(` class C { static { 10; } }`, ` ^-------------^ `, ` ^-------------------------`);