summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/parser/script-source-extent.js
diff options
context:
space:
mode:
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.js337
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) { } }`,
+ ` ^------^ `,
+ ` ^----------------------------------`);