diff options
Diffstat (limited to 'js/src/jit-test/tests/parser/script-source-extent.js')
-rw-r--r-- | js/src/jit-test/tests/parser/script-source-extent.js | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/parser/script-source-extent.js b/js/src/jit-test/tests/parser/script-source-extent.js new file mode 100644 index 0000000000..f473ee5891 --- /dev/null +++ b/js/src/jit-test/tests/parser/script-source-extent.js @@ -0,0 +1,337 @@ +// |jit-test| --enable-private-fields; --enable-private-methods + +// 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); + } + + // 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) { } }`, + ` ^------^ `, + ` ^----------------------------------`); |