Skip to content

Commit 47f2601

Browse files
authored
Merge pull request webpack#6261 from ooflorent/top_level_this
Rewrite top level `this` to `undefined` in Harmony modules
2 parents 4162471 + a550a8c commit 47f2601

File tree

5 files changed

+78
-2
lines changed

5 files changed

+78
-2
lines changed

lib/Parser.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,7 @@ class Parser extends Tapable {
947947
for(const param of statement.params)
948948
this.walkPattern(param);
949949
this.inScope(statement.params, () => {
950+
this.scope.topLevelScope = false;
950951
if(statement.body.type === "BlockStatement") {
951952
this.detectStrictMode(statement.body.body);
952953
this.prewalkStatement(statement.body);
@@ -1263,6 +1264,9 @@ class Parser extends Tapable {
12631264
case "TemplateLiteral":
12641265
this.walkTemplateLiteral(expression);
12651266
break;
1267+
case "ThisExpression":
1268+
this.walkThisExpression(expression);
1269+
break;
12661270
case "UnaryExpression":
12671271
this.walkUnaryExpression(expression);
12681272
break;
@@ -1306,6 +1310,7 @@ class Parser extends Tapable {
13061310
for(const param of expression.params)
13071311
this.walkPattern(param);
13081312
this.inScope(expression.params, () => {
1313+
this.scope.topLevelScope = false;
13091314
if(expression.body.type === "BlockStatement") {
13101315
this.detectStrictMode(expression.body.body);
13111316
this.prewalkStatement(expression.body);
@@ -1557,6 +1562,13 @@ class Parser extends Tapable {
15571562
this.walkExpression(expression.property);
15581563
}
15591564

1565+
walkThisExpression(expression) {
1566+
const expressionHook = this.hooks.expression.get("this");
1567+
if(expressionHook !== undefined) {
1568+
expressionHook.call(expression);
1569+
}
1570+
}
1571+
15601572
walkIdentifier(expression) {
15611573
if(!this.scope.definitions.has(expression.name)) {
15621574
const hook = this.hooks.expression.get(this.scope.renames.get(expression.name) || expression.name);
@@ -1571,6 +1583,7 @@ class Parser extends Tapable {
15711583
inScope(params, fn) {
15721584
const oldScope = this.scope;
15731585
this.scope = {
1586+
topLevelScope: oldScope.topLevelScope,
15741587
inTry: false,
15751588
inShorthand: false,
15761589
isStrict: oldScope.isStrict,
@@ -1778,6 +1791,7 @@ class Parser extends Tapable {
17781791
const oldState = this.state;
17791792
const oldComments = this.comments;
17801793
this.scope = {
1794+
topLevelScope: true,
17811795
inTry: false,
17821796
inShorthand: false,
17831797
isStrict: false,

lib/dependencies/HarmonyModulesPlugin.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const NullFactory = require("../NullFactory");
1919
const HarmonyDetectionParserPlugin = require("./HarmonyDetectionParserPlugin");
2020
const HarmonyImportDependencyParserPlugin = require("./HarmonyImportDependencyParserPlugin");
2121
const HarmonyExportDependencyParserPlugin = require("./HarmonyExportDependencyParserPlugin");
22+
const HarmonyTopLevelThisParserPlugin = require("./HarmonyTopLevelThisParserPlugin");
2223

2324
class HarmonyModulesPlugin {
2425
constructor(options) {
@@ -66,6 +67,7 @@ class HarmonyModulesPlugin {
6667
new HarmonyDetectionParserPlugin().apply(parser);
6768
new HarmonyImportDependencyParserPlugin(this.options).apply(parser);
6869
new HarmonyExportDependencyParserPlugin(this.options).apply(parser);
70+
new HarmonyTopLevelThisParserPlugin().apply(parser);
6971
};
7072

7173
normalModuleFactory.hooks.parser.for("javascript/auto").tap("HarmonyModulesPlugin", handler);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Florent Cailhol @ooflorent
4+
*/
5+
"use strict";
6+
7+
const ConstDependency = require("./ConstDependency");
8+
9+
class HarmonyTopLevelThisParserPlugin {
10+
apply(parser) {
11+
parser.hooks.expression.for("this").tap("HarmonyTopLevelThisParserPlugin", node => {
12+
if(!parser.scope.topLevelScope)
13+
return;
14+
const module = parser.state.module;
15+
const isHarmony = !!(module.buildMeta && module.buildMeta.exportsType);
16+
if(isHarmony) {
17+
const dep = new ConstDependency("undefined", node.range, false);
18+
dep.loc = node.loc;
19+
parser.state.current.addDependency(dep);
20+
}
21+
});
22+
}
23+
}
24+
25+
module.exports = HarmonyTopLevelThisParserPlugin;

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,21 @@ export {
1111
b
1212
}
1313

14+
export const that = this;
15+
export const returnThisArrow = () => this;
16+
export const returnThisMember = () => this.a;
17+
18+
export class C {
19+
constructor() {
20+
this.x = "bar";
21+
}
22+
foo() {
23+
return this.x;
24+
}
25+
}
26+
27+
export function D() {
28+
this.prop = () => "ok";
29+
}
30+
1431
export default returnThis;

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
"use strict";
22

3-
import d, {a, b as B} from "./abc";
3+
import d, {a, b as B, C as _C, D as _D, returnThisArrow, returnThisMember, that} from "./abc";
44

55
import * as abc from "./abc";
66

7+
it("should have this = undefined on harmony modules", function() {
8+
(typeof that).should.be.eql("undefined");
9+
(typeof abc.that).should.be.eql("undefined");
10+
(typeof returnThisArrow()).should.be.eql("undefined");
11+
(typeof abc.returnThisArrow()).should.be.eql("undefined");
12+
(function() {
13+
returnThisMember();
14+
}).should.throw();
15+
(function() {
16+
abc.returnThisMember();
17+
}).should.throw();
18+
});
19+
20+
it("should not break classes and functions", function() {
21+
(new _C).foo().should.be.eql("bar");
22+
(new _D).prop().should.be.eql("ok");
23+
});
24+
725
function x() { throw new Error("should not be executed"); }
826
it("should have this = undefined on imported non-strict functions", function() {
927
x
@@ -16,7 +34,7 @@ it("should have this = undefined on imported non-strict functions", function() {
1634
abc.a().should.be.type("object");
1735
x
1836
var thing = abc.a();
19-
Object.keys(thing).should.be.eql(["a", "b", "default"]);
37+
Object.keys(thing).should.be.eql(Object.keys(abc));
2038
});
2139

2240
import C2, { C } from "./new";

0 commit comments

Comments
 (0)