Skip to content

Commit a550a8c

Browse files
committed
Rewrite top level this to undefined in Harmony modules
1 parent d60a9f5 commit a550a8c

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.prewalkStatement(statement.body);
952953
this.walkStatement(statement.body);
@@ -1272,6 +1273,9 @@ class Parser extends Tapable {
12721273
case "TemplateLiteral":
12731274
this.walkTemplateLiteral(expression);
12741275
break;
1276+
case "ThisExpression":
1277+
this.walkThisExpression(expression);
1278+
break;
12751279
case "UnaryExpression":
12761280
this.walkUnaryExpression(expression);
12771281
break;
@@ -1315,6 +1319,7 @@ class Parser extends Tapable {
13151319
for(const param of expression.params)
13161320
this.walkPattern(param);
13171321
this.inScope(expression.params, () => {
1322+
this.scope.topLevelScope = false;
13181323
if(expression.body.type === "BlockStatement") {
13191324
this.prewalkStatement(expression.body);
13201325
this.walkStatement(expression.body);
@@ -1564,6 +1569,13 @@ class Parser extends Tapable {
15641569
this.walkExpression(expression.property);
15651570
}
15661571

1572+
walkThisExpression(expression) {
1573+
const expressionHook = this.hooks.expression.get("this");
1574+
if(expressionHook !== undefined) {
1575+
expressionHook.call(expression);
1576+
}
1577+
}
1578+
15671579
walkIdentifier(expression) {
15681580
if(!this.scope.definitions.has(expression.name)) {
15691581
const hook = this.hooks.expression.for(this.scope.renames.get(expression.name) || expression.name);
@@ -1578,6 +1590,7 @@ class Parser extends Tapable {
15781590
inScope(params, fn) {
15791591
const oldScope = this.scope;
15801592
this.scope = {
1593+
topLevelScope: oldScope.topLevelScope,
15811594
inTry: false,
15821595
inShorthand: false,
15831596
definitions: oldScope.definitions.createChild(),
@@ -1774,6 +1787,7 @@ class Parser extends Tapable {
17741787
const oldState = this.state;
17751788
const oldComments = this.comments;
17761789
this.scope = {
1790+
topLevelScope: true,
17771791
inTry: false,
17781792
definitions: new StackedSetMap(),
17791793
renames: new StackedSetMap()

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)