forked from mirrors/gecko-dev
The analyzeInputString function tried to handle escape characters in strings, by having a dedicated ESCAPE state. Unfortunately, when in ESCAPE state, the code will always re-assign the NORMAL state. So if at some point the string was terminated, the code would assume we were entering a string state again. In the end, this would cause the autocomplete to fail, because the JSPropertyProvider would think that it was passed an unterminated string literal. This is fixed by introducing quote-specific escape state, so we know which state to get back in once the escape character is consumed. A few test cases are added to ensure this works as expected. Differential Revision: https://phabricator.services.mozilla.com/D98754
226 lines
6 KiB
JavaScript
226 lines
6 KiB
JavaScript
// Any copyright is dedicated to the Public Domain.
|
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
|
|
|
"use strict";
|
|
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
|
|
const {
|
|
analyzeInputString,
|
|
} = require("devtools/shared/webconsole/js-property-provider");
|
|
|
|
add_task(() => {
|
|
const tests = [
|
|
{
|
|
desc: "simple property access",
|
|
input: `var a = {b: 1};a.b`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `var a = {b: 1};a`,
|
|
lastStatement: "a.b",
|
|
mainExpression: `a`,
|
|
matchProp: `b`,
|
|
},
|
|
},
|
|
{
|
|
desc: "deep property access",
|
|
input: `a.b.c`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `a.b`,
|
|
lastStatement: "a.b.c",
|
|
mainExpression: `a.b`,
|
|
matchProp: `c`,
|
|
},
|
|
},
|
|
{
|
|
desc: "element access",
|
|
input: `a["b`,
|
|
expected: {
|
|
isElementAccess: true,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `a`,
|
|
lastStatement: `a["b`,
|
|
mainExpression: `a`,
|
|
matchProp: `"b`,
|
|
},
|
|
},
|
|
{
|
|
desc: "element access without quotes",
|
|
input: `a[b`,
|
|
expected: {
|
|
isElementAccess: true,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `a`,
|
|
lastStatement: `a[b`,
|
|
mainExpression: `a`,
|
|
matchProp: `b`,
|
|
},
|
|
},
|
|
{
|
|
desc: "simple optional chaining access",
|
|
input: `a?.b`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `a`,
|
|
lastStatement: `a?.b`,
|
|
mainExpression: `a`,
|
|
matchProp: `b`,
|
|
},
|
|
},
|
|
{
|
|
desc: "deep optional chaining access",
|
|
input: `a?.b?.c`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `a?.b`,
|
|
lastStatement: `a?.b?.c`,
|
|
mainExpression: `a?.b`,
|
|
matchProp: `c`,
|
|
},
|
|
},
|
|
{
|
|
desc: "optional chaining element access",
|
|
input: `a?.["b`,
|
|
expected: {
|
|
isElementAccess: true,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `a`,
|
|
lastStatement: `a?.["b`,
|
|
mainExpression: `a`,
|
|
matchProp: `"b`,
|
|
},
|
|
},
|
|
{
|
|
desc: "optional chaining element access without quotes",
|
|
input: `a?.[b`,
|
|
expected: {
|
|
isElementAccess: true,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `a`,
|
|
lastStatement: `a?.[b`,
|
|
mainExpression: `a`,
|
|
matchProp: `b`,
|
|
},
|
|
},
|
|
{
|
|
desc: "deep optional chaining element access with quotes",
|
|
input: `var a = {b: 1, c: ["."]}; a?.["b"]?.c?.["d[.`,
|
|
expected: {
|
|
isElementAccess: true,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `var a = {b: 1, c: ["."]}; a?.["b"]?.c`,
|
|
lastStatement: `a?.["b"]?.c?.["d[.`,
|
|
mainExpression: `a?.["b"]?.c`,
|
|
matchProp: `"d[.`,
|
|
},
|
|
},
|
|
{
|
|
desc: "literal arrays with newline",
|
|
input: `[1,2,3,\n4\n].`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `[1,2,3,\n4\n]`,
|
|
lastStatement: `[1,2,3,4].`,
|
|
mainExpression: `[1,2,3,4]`,
|
|
matchProp: ``,
|
|
},
|
|
},
|
|
{
|
|
desc: "number literal with newline",
|
|
input: `1\n.`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `1\n`,
|
|
lastStatement: `1\n.`,
|
|
mainExpression: `1`,
|
|
matchProp: ``,
|
|
},
|
|
},
|
|
{
|
|
desc: "string literal",
|
|
input: `"abc".`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `"abc"`,
|
|
lastStatement: `"abc".`,
|
|
mainExpression: `"abc"`,
|
|
matchProp: ``,
|
|
},
|
|
},
|
|
{
|
|
desc: "string literal containing backslash",
|
|
input: `"\\n".`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `"\\n"`,
|
|
lastStatement: `"\\n".`,
|
|
mainExpression: `"\\n"`,
|
|
matchProp: ``,
|
|
},
|
|
},
|
|
{
|
|
desc: "single quote string literal containing backslash",
|
|
input: `'\\r'.`,
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `'\\r'`,
|
|
lastStatement: `'\\r'.`,
|
|
mainExpression: `'\\r'`,
|
|
matchProp: ``,
|
|
},
|
|
},
|
|
{
|
|
desc: "template string literal containing backslash",
|
|
input: "`\\\\`.",
|
|
expected: {
|
|
isElementAccess: false,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: "`\\\\`",
|
|
lastStatement: "`\\\\`.",
|
|
mainExpression: "`\\\\`",
|
|
matchProp: ``,
|
|
},
|
|
},
|
|
{
|
|
desc: "unterminated double quote string literal",
|
|
input: `"\n`,
|
|
expected: {
|
|
err: "unterminated string literal",
|
|
},
|
|
},
|
|
{
|
|
desc: "unterminated single quote string literal",
|
|
input: `'\n`,
|
|
expected: {
|
|
err: "unterminated string literal",
|
|
},
|
|
},
|
|
{
|
|
desc: "optional chaining operator with spaces",
|
|
input: `test ?. ["propA"] ?. [0] ?. ["propB"] ?. ['to`,
|
|
expected: {
|
|
isElementAccess: true,
|
|
isPropertyAccess: true,
|
|
expressionBeforePropertyAccess: `test ?. ["propA"] ?. [0] ?. ["propB"] `,
|
|
lastStatement: `test ?. ["propA"] ?. [0] ?. ["propB"] ?. ['to`,
|
|
mainExpression: `test ?. ["propA"] ?. [0] ?. ["propB"]`,
|
|
matchProp: `'to`,
|
|
},
|
|
},
|
|
];
|
|
|
|
for (const { input, desc, expected } of tests) {
|
|
const result = analyzeInputString(input);
|
|
for (const [key, value] of Object.entries(expected)) {
|
|
Assert.equal(value, result[key], `${desc} | ${key} has expected value`);
|
|
}
|
|
}
|
|
});
|