Skip to content

Commit 236bbcf

Browse files
authored
Merge pull request webpack#7243 from webpack/fix/7213
Fix edge case when parsing top-level this on IIFE
2 parents 98d4d2e + da6f869 commit 236bbcf

File tree

3 files changed

+76
-51
lines changed

3 files changed

+76
-51
lines changed

lib/Parser.js

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,67 +1609,62 @@ class Parser extends Tapable {
16091609
this.walkClass(expression);
16101610
}
16111611

1612-
walkCallExpression(expression) {
1613-
let result;
1614-
1615-
const walkIIFE = (functionExpression, options, currentThis) => {
1616-
const renameArgOrThis = argOrThis => {
1617-
const renameIdentifier = this.getRenameIdentifier(argOrThis);
1618-
if (renameIdentifier) {
1619-
const hook = this.hooks.canRename.get(renameIdentifier);
1620-
if (hook !== undefined && hook.call(argOrThis)) {
1621-
const hook = this.hooks.rename.get(renameIdentifier);
1622-
if (hook === undefined || !hook.call(argOrThis))
1623-
return renameIdentifier;
1624-
}
1625-
}
1626-
this.walkExpression(argOrThis);
1627-
};
1628-
const wasTopLevel = this.scope.topLevelScope;
1629-
this.scope.topLevelScope = false;
1630-
const params = functionExpression.params;
1631-
const renameThis = currentThis ? renameArgOrThis(currentThis) : null;
1632-
const args = options.map(renameArgOrThis);
1633-
this.inScope(params.filter((identifier, idx) => !args[idx]), () => {
1634-
if (renameThis) {
1635-
this.scope.renames.set("this", renameThis);
1612+
_walkIIFE(functionExpression, options, currentThis) {
1613+
const renameArgOrThis = argOrThis => {
1614+
const renameIdentifier = this.getRenameIdentifier(argOrThis);
1615+
if (renameIdentifier) {
1616+
const hook = this.hooks.canRename.get(renameIdentifier);
1617+
if (hook !== undefined && hook.call(argOrThis)) {
1618+
const hook = this.hooks.rename.get(renameIdentifier);
1619+
if (hook === undefined || !hook.call(argOrThis))
1620+
return renameIdentifier;
16361621
}
1637-
for (let i = 0; i < args.length; i++) {
1638-
const param = args[i];
1639-
if (!param) continue;
1640-
if (!params[i] || params[i].type !== "Identifier") continue;
1641-
this.scope.renames.set(params[i].name, param);
1642-
}
1643-
if (functionExpression.body.type === "BlockStatement") {
1644-
this.prewalkStatement(functionExpression.body);
1645-
this.walkStatement(functionExpression.body);
1646-
} else this.walkExpression(functionExpression.body);
1647-
});
1648-
this.scope.topLevelScope = wasTopLevel;
1622+
}
1623+
this.walkExpression(argOrThis);
16491624
};
1625+
const params = functionExpression.params;
1626+
const renameThis = currentThis ? renameArgOrThis(currentThis) : null;
1627+
const args = options.map(renameArgOrThis);
1628+
const wasTopLevel = this.scope.topLevelScope;
1629+
this.scope.topLevelScope = false;
1630+
this.inScope(params.filter((identifier, idx) => !args[idx]), () => {
1631+
if (renameThis) {
1632+
this.scope.renames.set("this", renameThis);
1633+
}
1634+
for (let i = 0; i < args.length; i++) {
1635+
const param = args[i];
1636+
if (!param) continue;
1637+
if (!params[i] || params[i].type !== "Identifier") continue;
1638+
this.scope.renames.set(params[i].name, param);
1639+
}
1640+
if (functionExpression.body.type === "BlockStatement") {
1641+
this.prewalkStatement(functionExpression.body);
1642+
this.walkStatement(functionExpression.body);
1643+
} else this.walkExpression(functionExpression.body);
1644+
});
1645+
this.scope.topLevelScope = wasTopLevel;
1646+
}
1647+
1648+
walkCallExpression(expression) {
16501649
if (
16511650
expression.callee.type === "MemberExpression" &&
16521651
expression.callee.object.type === "FunctionExpression" &&
16531652
!expression.callee.computed &&
16541653
(expression.callee.property.name === "call" ||
16551654
expression.callee.property.name === "bind") &&
1656-
expression.arguments &&
16571655
expression.arguments.length > 0
16581656
) {
16591657
// (function(…) { }.call/bind(?, …))
1660-
walkIIFE(
1658+
this._walkIIFE(
16611659
expression.callee.object,
16621660
expression.arguments.slice(1),
16631661
expression.arguments[0]
16641662
);
1665-
} else if (
1666-
expression.callee.type === "FunctionExpression" &&
1667-
expression.arguments
1668-
) {
1663+
} else if (expression.callee.type === "FunctionExpression") {
16691664
// (function(…) { }(…))
1670-
walkIIFE(expression.callee, expression.arguments, null);
1665+
this._walkIIFE(expression.callee, expression.arguments, null);
16711666
} else if (expression.callee.type === "Import") {
1672-
result = this.hooks.importCall.call(expression);
1667+
let result = this.hooks.importCall.call(expression);
16731668
if (result === true) return;
16741669

16751670
if (expression.arguments) this.walkExpressions(expression.arguments);
@@ -1678,14 +1673,14 @@ class Parser extends Tapable {
16781673
if (callee.isIdentifier()) {
16791674
const callHook = this.hooks.call.get(callee.identifier);
16801675
if (callHook !== undefined) {
1681-
result = callHook.call(expression);
1676+
let result = callHook.call(expression);
16821677
if (result === true) return;
16831678
}
16841679
let identifier = callee.identifier.replace(/\.[^.]+$/, "");
16851680
if (identifier !== callee.identifier) {
16861681
const callAnyHook = this.hooks.callAnyMember.get(identifier);
16871682
if (callAnyHook !== undefined) {
1688-
result = callAnyHook.call(expression);
1683+
let result = callAnyHook.call(expression);
16891684
if (result === true) return;
16901685
}
16911686
}

test/configCases/parsing/harmony-this/index.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
import {extendThisClass, returnThisArrow, returnThisMember, that} from "./abc";
44
import d, {a, b as B, C as _C, D as _D, E, F, f1, f2, f3, G} from "./abc";
5+
import {bindThis, callThis, applyThis} from "./issue-7213";
56

67
import * as abc from "./abc";
78

8-
it("should have this = undefined on harmony modules", function() {
9+
it("should have this = undefined on harmony modules", () => {
910
expect((typeof that)).toBe("undefined");
1011
expect((typeof abc.that)).toBe("undefined");
1112
expect((typeof returnThisArrow())).toBe("undefined");
@@ -21,7 +22,7 @@ it("should have this = undefined on harmony modules", function() {
2122
}).toThrowError();
2223
});
2324

24-
it("should not break classes and functions", function() {
25+
it("should not break classes and functions", () => {
2526
expect((new _C).foo()).toBe("bar");
2627
expect((new _C).bar()).toBe("bar");
2728
expect((new _D).prop()).toBe("ok");
@@ -34,8 +35,11 @@ it("should not break classes and functions", function() {
3435
expect((new G("ok")).getX()).toBe("ok");
3536
});
3637

37-
function x() { throw new Error("should not be executed"); }
38-
it("should have this = undefined on imported non-strict functions", function() {
38+
function x() {
39+
throw new Error("should not be executed");
40+
}
41+
42+
it("should have this = undefined on imported non-strict functions", () => {
3943
x
4044
expect(d()).toBe("undefined");
4145
x
@@ -53,11 +57,17 @@ import C2, { C } from "./new";
5357

5458
import * as New from "./new";
5559

56-
it("should be possible to use new correctly", function() {
60+
it("should be possible to use new correctly", () => {
5761
x
5862
expect(new C()).toEqual({ok: true});
5963
x
6064
expect(new C2()).toEqual({ok: true});
6165
x
6266
expect(new New.C()).toEqual({ok: true});
6367
});
68+
69+
it("should not break Babel arrow function transform", () => {
70+
expect(bindThis()).toBe(undefined);
71+
expect(callThis).toBe(undefined);
72+
expect(applyThis).toBe(undefined);
73+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// This helper is taken from Babel
2+
function _newArrowCheck(innerThis, boundThis) {
3+
if (innerThis !== boundThis) {
4+
throw new TypeError("Cannot instantiate an arrow function");
5+
}
6+
}
7+
8+
let _this = this;
9+
export let bindThis = function() {
10+
_newArrowCheck(this, _this);
11+
return this
12+
}.bind(this);
13+
14+
export let callThis = function() {
15+
return this
16+
}.call(this)
17+
18+
export let applyThis = function() {
19+
return this
20+
}.apply(this)

0 commit comments

Comments
 (0)