From aea4fd97b5c3ce0d4c58778493e5620c37716bf8 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 21 Jan 2020 18:25:26 +0100 Subject: [PATCH] do not evaluate constants in asm.js, fix IIFE mode detection fixes #10283 --- lib/ConstPlugin.js | 5 +++++ lib/javascript/JavascriptParser.js | 26 +++++++++++++++++--------- test/Compiler.test.js | 20 ++++++++++++++++++++ test/fixtures/asmjs.js | 22 ++++++++++++++++++++++ 4 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 test/fixtures/asmjs.js diff --git a/lib/ConstPlugin.js b/lib/ConstPlugin.js index ce0d167d4..d58210daa 100644 --- a/lib/ConstPlugin.js +++ b/lib/ConstPlugin.js @@ -131,6 +131,7 @@ class ConstPlugin { const handler = parser => { parser.hooks.statementIf.tap("ConstPlugin", statement => { + if (parser.scope.isAsmJs) return; const param = parser.evaluateExpression(statement.test); const bool = param.asBool(); if (typeof bool === "boolean") { @@ -201,6 +202,7 @@ class ConstPlugin { parser.hooks.expressionConditionalOperator.tap( "ConstPlugin", expression => { + if (parser.scope.isAsmJs) return; const param = parser.evaluateExpression(expression.test); const bool = param.asBool(); if (typeof bool === "boolean") { @@ -236,6 +238,7 @@ class ConstPlugin { parser.hooks.expressionLogicalOperator.tap( "ConstPlugin", expression => { + if (parser.scope.isAsmJs) return; if ( expression.operator === "&&" || expression.operator === "||" @@ -321,6 +324,7 @@ class ConstPlugin { parser.hooks.evaluateIdentifier .for("__resourceQuery") .tap("ConstPlugin", expr => { + if (parser.scope.isAsmJs) return; if (!parser.state.module) return; return evaluateToString(getQuery(parser.state.module.resource))( expr @@ -329,6 +333,7 @@ class ConstPlugin { parser.hooks.expression .for("__resourceQuery") .tap("ConstPlugin", expr => { + if (parser.scope.isAsmJs) return; if (!parser.state.module) return; const dep = new CachedConstDependency( JSON.stringify(getQuery(parser.state.module.resource)), diff --git a/lib/javascript/JavascriptParser.js b/lib/javascript/JavascriptParser.js index a9e3a0bd8..16be7b439 100644 --- a/lib/javascript/JavascriptParser.js +++ b/lib/javascript/JavascriptParser.js @@ -78,6 +78,7 @@ class VariableInfo { * @property {boolean | "arrow"} topLevelScope * @property {boolean} inShorthand * @property {boolean} isStrict + * @property {boolean} isAsmJs * @property {boolean} inTry */ @@ -1455,7 +1456,7 @@ class JavascriptParser extends Parser { this.walkPattern(param); } if (statement.body.type === "BlockStatement") { - this.detectStrictMode(statement.body.body); + this.detectMode(statement.body.body); this.prewalkStatement(statement.body); this.walkStatement(statement.body); } else { @@ -1935,7 +1936,7 @@ class JavascriptParser extends Parser { this.walkPattern(param); } if (expression.body.type === "BlockStatement") { - this.detectStrictMode(expression.body.body); + this.detectMode(expression.body.body); this.prewalkStatement(expression.body); this.walkStatement(expression.body); } else { @@ -1953,7 +1954,7 @@ class JavascriptParser extends Parser { this.walkPattern(param); } if (expression.body.type === "BlockStatement") { - this.detectStrictMode(expression.body.body); + this.detectMode(expression.body.body); this.prewalkStatement(expression.body); this.walkStatement(expression.body); } else { @@ -2172,6 +2173,7 @@ class JavascriptParser extends Parser { this.setVariable(params[i].name, varInfo); } if (functionExpression.body.type === "BlockStatement") { + this.detectMode(functionExpression.body.body); this.prewalkStatement(functionExpression.body); this.walkStatement(functionExpression.body); } else { @@ -2495,6 +2497,7 @@ class JavascriptParser extends Parser { inTry: false, inShorthand: false, isStrict: oldScope.isStrict, + isAsmJs: oldScope.isAsmJs, definitions: oldScope.definitions.createChild() }; @@ -2516,6 +2519,7 @@ class JavascriptParser extends Parser { inTry: false, inShorthand: false, isStrict: oldScope.isStrict, + isAsmJs: oldScope.isAsmJs, definitions: oldScope.definitions.createChild() }; @@ -2539,6 +2543,7 @@ class JavascriptParser extends Parser { inTry: oldScope.inTry, inShorthand: false, isStrict: oldScope.isStrict, + isAsmJs: oldScope.isAsmJs, definitions: oldScope.definitions.createChild() }; @@ -2547,15 +2552,17 @@ class JavascriptParser extends Parser { this.scope = oldScope; } - detectStrictMode(statements) { - const isStrict = + detectMode(statements) { + const isLiteral = statements.length >= 1 && statements[0].type === "ExpressionStatement" && - statements[0].expression.type === "Literal" && - statements[0].expression.value === "use strict"; - if (isStrict) { + statements[0].expression.type === "Literal"; + if (isLiteral && statements[0].expression.value === "use strict") { this.scope.isStrict = true; } + if (isLiteral && statements[0].expression.value === "use asm") { + this.scope.isAsmJs = true; + } } enterPatterns(patterns, onIdent) { @@ -2790,6 +2797,7 @@ class JavascriptParser extends Parser { inTry: false, inShorthand: false, isStrict: false, + isAsmJs: false, definitions: new StackedMap() }; /** @type {ParserState} */ @@ -2800,7 +2808,7 @@ class JavascriptParser extends Parser { this.lastStatementEndPos = NaN; this.statementStartPos = NaN; if (this.hooks.program.call(ast, comments) === undefined) { - this.detectStrictMode(ast.body); + this.detectMode(ast.body); this.prewalkStatements(ast.body); this.blockPrewalkStatements(ast.body); this.walkStatements(ast.body); diff --git a/test/Compiler.test.js b/test/Compiler.test.js index 28e848bab..008f0efcb 100644 --- a/test/Compiler.test.js +++ b/test/Compiler.test.js @@ -170,6 +170,26 @@ describe("Compiler", () => { done(); }); }); + + it("should not evaluate constants in asm.js", done => { + compile("./asmjs", {}, (stats, files) => { + expect(Object.keys(files)).toEqual(["/main.js"]); + const bundle = files["/main.js"]; + expect(bundle).toMatch('"use asm";'); + expect(bundle).toMatch("101"); + expect(bundle).toMatch("102"); + expect(bundle).toMatch("103"); + expect(bundle).toMatch("104"); + expect(bundle).toMatch("105"); + expect(bundle).not.toMatch("106"); + expect(bundle).not.toMatch("107"); + expect(bundle).not.toMatch("108"); + expect(bundle).toMatch("109"); + expect(bundle).toMatch("110"); + done(); + }); + }); + describe("methods", () => { let compiler; beforeEach(() => { diff --git a/test/fixtures/asmjs.js b/test/fixtures/asmjs.js new file mode 100644 index 000000000..59d2101f7 --- /dev/null +++ b/test/fixtures/asmjs.js @@ -0,0 +1,22 @@ +module.exports = function a() { + function b() { + "use asm"; + if (0 == 0) { + return 1 == 1 ? 101 : 102; + } else { + return 0 == 1 ? 103 : 104; + } + } + function c() { + if (0 == 0) { + return 1 == 1 ? 105 : 106; + } else { + return 0 == 1 ? 107 : 108; + } + } + var d = (function() { + "use asm"; + return 1 == 1 ? 109 : 110; + })(); + return b() + c() + d; +};