Merge pull request #12007 from webpack/bugfix/11990

fix: check statements in path for asi safe
This commit is contained in:
Tobias Koppers 2020-11-26 06:48:58 +01:00 committed by GitHub
commit 612f28a780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 12 deletions

View File

@ -1551,6 +1551,18 @@ class JavascriptParser extends Parser {
this.prevStatement = this.statementPath.pop();
}
/**
* Walks a statements that is nested within a parent statement
* and can potentially be a non-block statement.
* This enforces the nested statement to never be in ASI position.
* @param {StatementNode} statement the nested statement
* @returns {void}
*/
walkNestedStatement(statement) {
this.prevStatement = undefined;
this.walkStatement(statement);
}
// Real Statements
preWalkBlockStatement(statement) {
this.preWalkStatements(statement.body);
@ -1581,15 +1593,15 @@ class JavascriptParser extends Parser {
const result = this.hooks.statementIf.call(statement);
if (result === undefined) {
this.walkExpression(statement.test);
this.walkStatement(statement.consequent);
this.walkNestedStatement(statement.consequent);
if (statement.alternate) {
this.walkStatement(statement.alternate);
this.walkNestedStatement(statement.alternate);
}
} else {
if (result) {
this.walkStatement(statement.consequent);
this.walkNestedStatement(statement.consequent);
} else if (statement.alternate) {
this.walkStatement(statement.alternate);
this.walkNestedStatement(statement.alternate);
}
}
}
@ -1604,7 +1616,7 @@ class JavascriptParser extends Parser {
const result = hook.call(statement);
if (result === true) return;
}
this.walkStatement(statement.body);
this.walkNestedStatement(statement.body);
}
preWalkWithStatement(statement) {
@ -1613,7 +1625,7 @@ class JavascriptParser extends Parser {
walkWithStatement(statement) {
this.walkExpression(statement.object);
this.walkStatement(statement.body);
this.walkNestedStatement(statement.body);
}
preWalkSwitchStatement(statement) {
@ -1661,7 +1673,7 @@ class JavascriptParser extends Parser {
walkWhileStatement(statement) {
this.walkExpression(statement.test);
this.walkStatement(statement.body);
this.walkNestedStatement(statement.body);
}
preWalkDoWhileStatement(statement) {
@ -1669,7 +1681,7 @@ class JavascriptParser extends Parser {
}
walkDoWhileStatement(statement) {
this.walkStatement(statement.body);
this.walkNestedStatement(statement.body);
this.walkExpression(statement.test);
}
@ -1687,6 +1699,7 @@ class JavascriptParser extends Parser {
if (statement.init) {
if (statement.init.type === "VariableDeclaration") {
this.blockPreWalkVariableDeclaration(statement.init);
this.prevStatement = undefined;
this.walkStatement(statement.init);
} else {
this.walkExpression(statement.init);
@ -1706,7 +1719,7 @@ class JavascriptParser extends Parser {
this.prevStatement = prev;
this.walkStatements(body.body);
} else {
this.walkStatement(body);
this.walkNestedStatement(body);
}
});
}
@ -1735,7 +1748,7 @@ class JavascriptParser extends Parser {
this.prevStatement = prev;
this.walkStatements(body.body);
} else {
this.walkStatement(body);
this.walkNestedStatement(body);
}
});
}
@ -1767,7 +1780,7 @@ class JavascriptParser extends Parser {
this.prevStatement = prev;
this.walkStatements(body.body);
} else {
this.walkStatement(body);
this.walkNestedStatement(body);
}
});
}
@ -3376,9 +3389,14 @@ class JavascriptParser extends Parser {
const currentStatement = this.statementPath[this.statementPath.length - 1];
if (currentStatement === undefined) throw new Error("Not in statement");
return (
// Either asking directly for the end position of the current statement
(currentStatement.range[1] === pos && this.semicolons.has(pos)) ||
// Or asking for the start position of the current statement,
// here we have to check multiple things
(currentStatement.range[0] === pos &&
// is there a previous statement which might be relevant?
this.prevStatement !== undefined &&
// is the end position of the previous statement an ASI position?
this.semicolons.has(this.prevStatement.range[1]))
);
}

View File

@ -1 +1,11 @@
export function a() {}
let count = 0;
export function callme() {
count++;
}
export function getCount() {
return count;
}

View File

@ -1,4 +1,4 @@
import {a as b} from "./a";
import {a as b, callme, getCount} from "./a";
import * as c from "./b";
function donotcallme() {
@ -12,4 +12,34 @@ it("should respect asi flag", () => {
b();
(donotcallme)
c;
var i = 0
for (;i < 10;i++) callme()
var i = 0
for (;i < 10;(function() {
i++
})()) callme()
var i = 0
for (;i < 2;i++) {
(donotcallme)
b();
}
var i = 0
if (i++) callme()
var i = 1
if (i)
(donotcallme)
else
callme()
var i = 0
while (i++ < 4) callme()
do (donotcallme)
while (i++ < 4) callme()
var i = 0
while (i++ < 4) (function () {
var i = 4
return callme()
})()
expect(getCount()).toBe(29)
});

7
types.d.ts vendored
View File

@ -4162,6 +4162,13 @@ declare class JavascriptParser extends Parser {
preWalkStatement(statement?: any): void;
blockPreWalkStatement(statement?: any): void;
walkStatement(statement?: any): void;
/**
* Walks a statements that is nested within a parent statement
* and can potentially be a non-block statement.
* This enforces the nested statement to never be in ASI position.
*/
walkNestedStatement(statement: Statement): void;
preWalkBlockStatement(statement?: any): void;
walkBlockStatement(statement?: any): void;
walkExpressionStatement(statement?: any): void;