From 99523fa3d7cd7a9f63dcd1f28615471322667490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sat, 21 Sep 2019 02:32:11 +0200 Subject: [PATCH 1/3] Correctly disambiguate / after async fuctions --- .../babel-parser/src/parser/expression.js | 13 ++ .../input.js | 2 + .../output.json | 185 ++++++++++++++++++ .../context-regex-after-statement/input.js | 2 + .../context-regex-after-statement/output.json | 122 ++++++++++++ 5 files changed, 324 insertions(+) create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js create mode 100644 packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 5fd5c402efc9..db540d6e0720 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -19,6 +19,7 @@ // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser import { types as tt, type TokenType } from "../tokenizer/types"; +import { types as tc } from "../tokenizer/context"; import * as N from "../types"; import LValParser from "./lval"; import { @@ -949,6 +950,18 @@ export default class ExpressionParser extends LValParser { this.match(tt._function) && !this.canInsertSemicolon() ) { + if (this.state.context.pop() !== tc.functionStatement) { + // Since "async" is an identifier and normally identifiers + // can't be followed by expression, the tokenizer assumes + // that "function" starts a statement. + // Fixing it in the tokenizer would mean traking not only the + // previous token ("async"), but also the one before to know + // if its beforeExpr value. + // It's easier and more efficient to adjust the context here. + throw new Error("Internal error"); + } + this.state.context.push(tc.functionExpression); + this.next(); return this.parseFunction(node, undefined, true); } else if ( diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js new file mode 100644 index 000000000000..1415f9866a40 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/input.js @@ -0,0 +1,2 @@ +void async function fn() {} +/foo/g \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json new file mode 100644 index 000000000000..9bd2028bf81f --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-division-after-expression/output.json @@ -0,0 +1,185 @@ +{ + "type": "File", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "expression": { + "type": "BinaryExpression", + "start": 0, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "left": { + "type": "BinaryExpression", + "start": 0, + "end": 32, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 4 + } + }, + "left": { + "type": "UnaryExpression", + "start": 0, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 27 + } + }, + "operator": "void", + "prefix": true, + "argument": { + "type": "FunctionExpression", + "start": 5, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 5 + }, + "end": { + "line": 1, + "column": 27 + } + }, + "id": { + "type": "Identifier", + "start": 20, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 22 + }, + "identifierName": "fn" + }, + "name": "fn" + }, + "generator": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start": 25, + "end": 27, + "loc": { + "start": { + "line": 1, + "column": 25 + }, + "end": { + "line": 1, + "column": 27 + } + }, + "body": [], + "directives": [] + } + } + }, + "operator": "/", + "right": { + "type": "Identifier", + "start": 29, + "end": 32, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 4 + }, + "identifierName": "foo" + }, + "name": "foo" + } + }, + "operator": "/", + "right": { + "type": "Identifier", + "start": 33, + "end": 34, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 6 + }, + "identifierName": "g" + }, + "name": "g" + } + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js new file mode 100644 index 000000000000..beb4d4ea4675 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/input.js @@ -0,0 +1,2 @@ +async function fn() {} +/foo/g \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json new file mode 100644 index 000000000000..05504dd0734f --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2017/async-functions/context-regex-after-statement/output.json @@ -0,0 +1,122 @@ +{ + "type": "File", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "program": { + "type": "Program", + "start": 0, + "end": 29, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start": 0, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "id": { + "type": "Identifier", + "start": 15, + "end": 17, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 17 + }, + "identifierName": "fn" + }, + "name": "fn" + }, + "generator": false, + "async": true, + "params": [], + "body": { + "type": "BlockStatement", + "start": 20, + "end": 22, + "loc": { + "start": { + "line": 1, + "column": 20 + }, + "end": { + "line": 1, + "column": 22 + } + }, + "body": [], + "directives": [] + } + }, + { + "type": "ExpressionStatement", + "start": 23, + "end": 29, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "expression": { + "type": "RegExpLiteral", + "start": 23, + "end": 29, + "loc": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 6 + } + }, + "extra": { + "raw": "/foo/g" + }, + "pattern": "foo", + "flags": "g" + } + } + ], + "directives": [] + } +} \ No newline at end of file From a464a610cdc48830d533e3ce76f5b5e291b8b473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Wed, 25 Sep 2019 01:56:45 +0200 Subject: [PATCH 2/3] Typos [skip ci] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Huáng Jùnliàng --- packages/babel-parser/src/parser/expression.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index db540d6e0720..f6c1d0be227e 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -954,9 +954,9 @@ export default class ExpressionParser extends LValParser { // Since "async" is an identifier and normally identifiers // can't be followed by expression, the tokenizer assumes // that "function" starts a statement. - // Fixing it in the tokenizer would mean traking not only the + // Fixing it in the tokenizer would mean tracking not only the // previous token ("async"), but also the one before to know - // if its beforeExpr value. + // its beforeExpr value. // It's easier and more efficient to adjust the context here. throw new Error("Internal error"); } From f7a2421c05038782a846e1a6dd75316116986d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Wed, 25 Sep 2019 02:00:30 +0200 Subject: [PATCH 3/3] Code review --- packages/babel-parser/src/parser/expression.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index f6c1d0be227e..bd087591418a 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -19,7 +19,7 @@ // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser import { types as tt, type TokenType } from "../tokenizer/types"; -import { types as tc } from "../tokenizer/context"; +import { types as ct } from "../tokenizer/context"; import * as N from "../types"; import LValParser from "./lval"; import { @@ -950,7 +950,8 @@ export default class ExpressionParser extends LValParser { this.match(tt._function) && !this.canInsertSemicolon() ) { - if (this.state.context.pop() !== tc.functionStatement) { + const last = this.state.context.length - 1; + if (this.state.context[last] !== ct.functionStatement) { // Since "async" is an identifier and normally identifiers // can't be followed by expression, the tokenizer assumes // that "function" starts a statement. @@ -960,7 +961,7 @@ export default class ExpressionParser extends LValParser { // It's easier and more efficient to adjust the context here. throw new Error("Internal error"); } - this.state.context.push(tc.functionExpression); + this.state.context[last] = ct.functionExpression; this.next(); return this.parseFunction(node, undefined, true);