Disallow escape sequences in contextual keywords (#9618)

* Disallow escape sequences in async

* Disallow escape sequences in get, set and async in class

* invalid escape tests

* Update whitelist

* tests for async in parens

* Add test for invalid newline between params and arrow

* Move canInsertSemilcolon() into shouldPArseAsyncArrow
This commit is contained in:
Daniel Tschinder 2019-03-05 17:20:36 -08:00 committed by GitHub
parent 349c0d4836
commit 29999007f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 530 additions and 22 deletions

View File

@ -2,8 +2,8 @@ var fn = async (
arg
) => {}
async (x)
=> {}
async (x) =>
{}
async x => {}

View File

@ -524,12 +524,21 @@ export default class ExpressionParser extends LValParser {
startLoc: Position,
noCalls?: ?boolean,
): N.Expression {
const maybeAsyncArrow = this.atPossibleAsync(base);
const state = {
optionalChainMember: false,
stop: false,
};
do {
base = this.parseSubscript(base, startPos, startLoc, noCalls, state);
base = this.parseSubscript(
base,
startPos,
startLoc,
noCalls,
state,
maybeAsyncArrow,
);
} while (!state.stop);
return base;
}
@ -544,6 +553,7 @@ export default class ExpressionParser extends LValParser {
startLoc: Position,
noCalls: ?boolean,
state: N.ParseSubscriptState,
maybeAsyncArrow: boolean,
): N.Expression {
if (!noCalls && this.eat(tt.doubleColon)) {
const node = this.startNodeAt(startPos, startLoc);
@ -575,13 +585,8 @@ export default class ExpressionParser extends LValParser {
this.expect(tt.bracketR);
return this.finishNode(node, "OptionalMemberExpression");
} else if (this.eat(tt.parenL)) {
const possibleAsync = this.atPossibleAsync(base);
node.callee = base;
node.arguments = this.parseCallExpressionArguments(
tt.parenR,
possibleAsync,
);
node.arguments = this.parseCallExpressionArguments(tt.parenR, false);
node.optional = true;
return this.finishNode(node, "OptionalCallExpression");
} else {
@ -620,7 +625,6 @@ export default class ExpressionParser extends LValParser {
this.state.yieldPos = 0;
this.state.awaitPos = 0;
const possibleAsync = this.atPossibleAsync(base);
this.next();
let node = this.startNodeAt(startPos, startLoc);
@ -631,7 +635,7 @@ export default class ExpressionParser extends LValParser {
node.arguments = this.parseCallExpressionArguments(
tt.parenR,
possibleAsync,
maybeAsyncArrow,
base.type === "Import",
base.type !== "Super",
);
@ -641,7 +645,7 @@ export default class ExpressionParser extends LValParser {
this.finishOptionalCallExpression(node);
}
if (possibleAsync && this.shouldParseAsyncArrow()) {
if (maybeAsyncArrow && this.shouldParseAsyncArrow()) {
state.stop = true;
this.checkCommaAfterRestFromSpread();
@ -704,11 +708,11 @@ export default class ExpressionParser extends LValParser {
atPossibleAsync(base: N.Expression): boolean {
return (
!this.state.containsEsc &&
this.state.potentialArrowAt === base.start &&
base.type === "Identifier" &&
base.name === "async" &&
!this.canInsertSemicolon()
this.state.lastTokEnd === base.end &&
!this.canInsertSemicolon() &&
this.state.input.slice(base.start, base.end) === "async"
);
}
@ -791,7 +795,7 @@ export default class ExpressionParser extends LValParser {
}
shouldParseAsyncArrow(): boolean {
return this.match(tt.arrow);
return this.match(tt.arrow) && !this.canInsertSemicolon();
}
parseAsyncArrowFromCallExpression(
@ -891,6 +895,7 @@ export default class ExpressionParser extends LValParser {
return this.parseFunction(node, undefined, true);
} else if (
canBeArrow &&
!containsEsc &&
id.name === "async" &&
this.match(tt.name) &&
!this.canInsertSemicolon()

View File

@ -1321,6 +1321,7 @@ export default class StatementParser extends ExpressionParser {
return;
}
const containsEsc = this.state.containsEsc;
const key = this.parseClassPropertyName(member);
const isPrivate = key.type === "PrivateName";
// Check the key is not a computed expression or string literal.
@ -1371,7 +1372,12 @@ export default class StatementParser extends ExpressionParser {
} else {
this.pushClassProperty(classBody, publicProp);
}
} else if (isSimple && key.name === "async" && !this.isLineTerminator()) {
} else if (
isSimple &&
key.name === "async" &&
!containsEsc &&
!this.isLineTerminator()
) {
// an async method
const isGenerator = this.eat(tt.star);
@ -1407,6 +1413,7 @@ export default class StatementParser extends ExpressionParser {
} else if (
isSimple &&
(key.name === "get" || key.name === "set") &&
!containsEsc &&
!(this.match(tt.star) && this.isLineTerminator())
) {
// `get\n*` is an uninitialized property named 'get' followed by a generator.

View File

@ -2619,6 +2619,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
startLoc: Position,
noCalls: ?boolean,
subscriptState: N.ParseSubscriptState,
maybeAsyncArrow: boolean,
): N.Expression {
if (this.match(tt.questionDot) && this.isLookaheadRelational("<")) {
this.expectPlugin("optionalChaining");
@ -2671,6 +2672,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
startLoc,
noCalls,
subscriptState,
maybeAsyncArrow,
);
}

View File

@ -1498,6 +1498,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
startLoc: Position,
noCalls: ?boolean,
state: N.ParseSubscriptState,
maybeAsyncArrow: boolean,
): N.Expression {
if (!this.hasPrecedingLineBreak() && this.match(tt.bang)) {
this.state.exprAllowed = false;
@ -1560,7 +1561,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
if (result) return result;
}
return super.parseSubscript(base, startPos, startLoc, noCalls, state);
return super.parseSubscript(
base,
startPos,
startLoc,
noCalls,
state,
maybeAsyncArrow,
);
}
parseNewArguments(node: N.NewExpression): void {

View File

@ -0,0 +1 @@
({ ge\u0074 x() {} })

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:12)"
}

View File

@ -0,0 +1 @@
(\u0061sync ())

View File

@ -0,0 +1,87 @@
{
"type": "File",
"start": 0,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 15
}
},
"program": {
"type": "Program",
"start": 0,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 15
}
},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 15,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 15
}
},
"expression": {
"type": "CallExpression",
"start": 1,
"end": 14,
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 14
}
},
"callee": {
"type": "Identifier",
"start": 1,
"end": 11,
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 11
},
"identifierName": "async"
},
"name": "async"
},
"arguments": [],
"extra": {
"parenthesized": true,
"parenStart": 0
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1 @@
class X { ge\u0074 x() {} }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:19)"
}

View File

@ -0,0 +1 @@
class X { se\u0074 x(value) {} }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:19)"
}

View File

@ -0,0 +1 @@
class X { st\u0061tic y() {} }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:22)"
}

View File

@ -0,0 +1 @@
for (x \u006ff y) {}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \";\" (1:7)"
}

View File

@ -0,0 +1 @@
(function* () { y\u0069eld 10 })

View File

@ -0,0 +1,3 @@
{
"throws": "Can not use 'yield' as identifier inside a generator (1:16)"
}

View File

@ -0,0 +1 @@
le\u0074 x = 5

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \";\" (1:9)"
}

View File

@ -0,0 +1 @@
function f() { new.ta\u0072get; }

View File

@ -0,0 +1,3 @@
{
"throws": "The only valid meta property for new is new.target (1:19)"
}

View File

@ -0,0 +1 @@
export { X \u0061s Y }

View File

@ -0,0 +1,4 @@
{
"sourceType": "module",
"throws": "Unexpected token, expected \",\" (1:11)"
}

View File

@ -0,0 +1 @@
import X fro\u006d 'x'

View File

@ -0,0 +1,4 @@
{
"sourceType": "module",
"throws": "Unexpected token (1:9)"
}

View File

@ -0,0 +1 @@
class X { \u0061sync x() { await x } }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:21)"
}

View File

@ -0,0 +1 @@
({ \u0061sync x() { await x } })

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:14)"
}

View File

@ -0,0 +1 @@
class X { static \u0061sync x() { await x } }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (1:28)"
}

View File

@ -0,0 +1 @@
(async function() { aw\u0061it x })

View File

@ -0,0 +1,3 @@
{
"throws": "Can not use 'await' as identifier inside an async function (1:20)"
}

View File

@ -0,0 +1 @@
export \u0061sync function y() { await x }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \"{\" (1:7)"
}

View File

@ -0,0 +1 @@
export default \u0061sync function y() { await x }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \";\" (1:26)"
}

View File

@ -0,0 +1 @@
(\u0061sync x => { await x })

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:12)"
}

View File

@ -0,0 +1 @@
\u0061sync x => { await x }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \";\" (1:11)"
}

View File

@ -0,0 +1 @@
(\u0061sync function() { await x })

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \",\" (1:12)"
}

View File

@ -0,0 +1 @@
\u0061sync function() { await x }

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \";\" (1:11)"
}

View File

@ -0,0 +1,2 @@
async (x)
=> {}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (2:0)"
}

View File

@ -0,0 +1 @@
(async)(a) => {}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \";\" (1:11)"
}

View File

@ -0,0 +1 @@
(async) function x (a) {}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token, expected \";\" (1:8)"
}

View File

@ -0,0 +1,2 @@
async (f)
: t => { }

View File

@ -0,0 +1,152 @@
{
"type": "File",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"program": {
"type": "Program",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"expression": {
"type": "ArrowFunctionExpression",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"returnType": {
"type": "TypeAnnotation",
"start": 10,
"end": 13,
"loc": {
"start": {
"line": 2,
"column": 0
},
"end": {
"line": 2,
"column": 3
}
},
"typeAnnotation": {
"type": "GenericTypeAnnotation",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
}
},
"typeParameters": null,
"id": {
"type": "Identifier",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "t"
},
"name": "t"
}
}
},
"id": null,
"generator": false,
"async": true,
"params": [
{
"type": "Identifier",
"start": 7,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 8
},
"identifierName": "f"
},
"name": "f"
}
],
"body": {
"type": "BlockStatement",
"start": 17,
"end": 20,
"loc": {
"start": {
"line": 2,
"column": 7
},
"end": {
"line": 2,
"column": 10
}
},
"body": [],
"directives": []
}
}
}
],
"directives": []
}
}

View File

@ -0,0 +1,2 @@
async (f)
: t => { }

View File

@ -0,0 +1,151 @@
{
"type": "File",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"program": {
"type": "Program",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"sourceType": "module",
"interpreter": null,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"expression": {
"type": "ArrowFunctionExpression",
"start": 0,
"end": 20,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 2,
"column": 10
}
},
"returnType": {
"type": "TSTypeAnnotation",
"start": 10,
"end": 13,
"loc": {
"start": {
"line": 2,
"column": 0
},
"end": {
"line": 2,
"column": 3
}
},
"typeAnnotation": {
"type": "TSTypeReference",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
}
},
"typeName": {
"type": "Identifier",
"start": 12,
"end": 13,
"loc": {
"start": {
"line": 2,
"column": 2
},
"end": {
"line": 2,
"column": 3
},
"identifierName": "t"
},
"name": "t"
}
}
},
"id": null,
"generator": false,
"async": true,
"params": [
{
"type": "Identifier",
"start": 7,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 7
},
"end": {
"line": 1,
"column": 8
},
"identifierName": "f"
},
"name": "f"
}
],
"body": {
"type": "BlockStatement",
"start": 17,
"end": 20,
"loc": {
"start": {
"line": 2,
"column": 7
},
"end": {
"line": 2,
"column": 10
}
},
"body": [],
"directives": []
}
}
}
],
"directives": []
}
}

View File

@ -601,10 +601,6 @@ language/module-code/privatename-not-valid-earlyerr-module-3.js(default)
language/module-code/privatename-not-valid-earlyerr-module-3.js(strict mode)
language/module-code/privatename-not-valid-earlyerr-module-4.js(default)
language/module-code/privatename-not-valid-earlyerr-module-4.js(strict mode)
language/statements/class/async-gen-meth-escaped-async.js(default)
language/statements/class/async-gen-meth-escaped-async.js(strict mode)
language/statements/class/async-meth-escaped-async.js(default)
language/statements/class/async-meth-escaped-async.js(strict mode)
language/statements/class/elements/fields-duplicate-privatenames.js(default)
language/statements/class/elements/fields-duplicate-privatenames.js(strict mode)
language/statements/class/elements/fields-literal-name-static-propname-constructor.js(default)