Skip to content

Commit 619e116

Browse files
committed
Change this capturing algorithm for converted loops
1 parent 95c3ecc commit 619e116

File tree

5 files changed

+104
-68
lines changed

5 files changed

+104
-68
lines changed

src/compiler/transformers/es6.ts

Lines changed: 44 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,9 @@ namespace ts {
165165
let currentNode: Node;
166166
let enclosingBlockScopeContainer: Node;
167167
let enclosingBlockScopeContainerParent: Node;
168-
let containingNonArrowFunction: FunctionLikeDeclaration | ClassElement;
169-
170-
/** Tracks the container that determines whether `super.x` is a static. */
171-
let superScopeContainer: FunctionLikeDeclaration | ClassElement;
168+
let enclosingFunction: FunctionLikeDeclaration;
169+
let enclosingNonArrowFunction: FunctionLikeDeclaration;
170+
let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement;
172171

173172
/**
174173
* Used to track if we are emitting body of the converted loop
@@ -182,11 +181,6 @@ namespace ts {
182181
*/
183182
let enabledSubstitutions: ES6SubstitutionFlags;
184183

185-
/**
186-
* This is used to determine whether we need to emit `_this` instead of `this`.
187-
*/
188-
let useCapturedThis: boolean;
189-
190184
return transformSourceFile;
191185

192186
function transformSourceFile(node: SourceFile) {
@@ -206,13 +200,13 @@ namespace ts {
206200
}
207201

208202
function saveStateAndInvoke<T>(node: Node, f: (node: Node) => T): T {
209-
const savedContainingNonArrowFunction = containingNonArrowFunction;
210-
const savedSuperScopeContainer = superScopeContainer;
211-
const savedCurrentParent = currentParent;
212-
const savedCurrentNode = currentNode;
203+
const savedEnclosingFunction = enclosingFunction;
204+
const savedEnclosingNonArrowFunction = enclosingNonArrowFunction;
205+
const savedEnclosingNonAsyncFunctionBody = enclosingNonAsyncFunctionBody;
213206
const savedEnclosingBlockScopeContainer = enclosingBlockScopeContainer;
214207
const savedEnclosingBlockScopeContainerParent = enclosingBlockScopeContainerParent;
215-
208+
const savedCurrentParent = currentParent;
209+
const savedCurrentNode = currentNode;
216210
const savedConvertedLoopState = convertedLoopState;
217211
if (nodeStartsNewLexicalEnvironment(node)) {
218212
// don't treat content of nodes that start new lexical environment as part of converted loop copy
@@ -223,12 +217,13 @@ namespace ts {
223217
const visited = f(node);
224218

225219
convertedLoopState = savedConvertedLoopState;
226-
containingNonArrowFunction = savedContainingNonArrowFunction;
227-
superScopeContainer = savedSuperScopeContainer;
228-
currentParent = savedCurrentParent;
229-
currentNode = savedCurrentNode;
220+
enclosingFunction = savedEnclosingFunction;
221+
enclosingNonArrowFunction = savedEnclosingNonArrowFunction;
222+
enclosingNonAsyncFunctionBody = savedEnclosingNonAsyncFunctionBody;
230223
enclosingBlockScopeContainer = savedEnclosingBlockScopeContainer;
231224
enclosingBlockScopeContainerParent = savedEnclosingBlockScopeContainerParent;
225+
currentParent = savedCurrentParent;
226+
currentNode = savedCurrentNode;
232227
return visited;
233228
}
234229

@@ -251,22 +246,13 @@ namespace ts {
251246
}
252247

253248
function visitorForConvertedLoopWorker(node: Node): VisitResult<Node> {
254-
const savedUseCapturedThis = useCapturedThis;
255-
256-
if (nodeStartsNewLexicalEnvironment(node)) {
257-
useCapturedThis = false;
258-
}
259-
260249
let result: VisitResult<Node>;
261-
262250
if (shouldCheckNode(node)) {
263251
result = visitJavaScript(node);
264252
}
265253
else {
266254
result = visitNodesInConvertedLoop(node);
267255
}
268-
269-
useCapturedThis = savedUseCapturedThis;
270256
return result;
271257
}
272258

@@ -409,30 +395,25 @@ namespace ts {
409395
}
410396

411397
function onBeforeVisitNode(node: Node) {
412-
const currentGrandparent = currentParent;
413-
currentParent = currentNode;
414-
currentNode = node;
415-
416-
if (currentParent) {
417-
if (isBlockScope(currentParent, currentGrandparent)) {
418-
enclosingBlockScopeContainer = currentParent;
419-
enclosingBlockScopeContainerParent = currentGrandparent;
398+
if (currentNode) {
399+
if (isBlockScope(currentNode, currentParent)) {
400+
enclosingBlockScopeContainer = currentNode;
401+
enclosingBlockScopeContainerParent = currentParent;
420402
}
421403

422-
switch (currentParent.kind) {
423-
case SyntaxKind.FunctionExpression:
424-
case SyntaxKind.Constructor:
425-
case SyntaxKind.MethodDeclaration:
426-
case SyntaxKind.GetAccessor:
427-
case SyntaxKind.SetAccessor:
428-
case SyntaxKind.FunctionDeclaration:
429-
containingNonArrowFunction = <FunctionLikeDeclaration>currentParent;
430-
if (!(containingNonArrowFunction.emitFlags & NodeEmitFlags.AsyncFunctionBody)) {
431-
superScopeContainer = containingNonArrowFunction;
404+
if (isFunctionLike(currentNode)) {
405+
enclosingFunction = currentNode;
406+
if (currentNode.kind !== SyntaxKind.ArrowFunction) {
407+
enclosingNonArrowFunction = currentNode;
408+
if (!(currentNode.emitFlags & NodeEmitFlags.AsyncFunctionBody)) {
409+
enclosingNonAsyncFunctionBody = currentNode;
432410
}
433-
break;
411+
}
434412
}
435413
}
414+
415+
currentParent = currentNode;
416+
currentNode = node;
436417
}
437418

438419
function visitSwitchStatement(node: SwitchStatement): SwitchStatement {
@@ -468,9 +449,8 @@ namespace ts {
468449

469450
function visitThisKeyword(node: Node): Node {
470451
Debug.assert(convertedLoopState !== undefined);
471-
472-
if (useCapturedThis) {
473-
// if useCapturedThis is true then 'this' keyword is contained inside an arrow function.
452+
if (enclosingFunction && enclosingFunction.kind === SyntaxKind.ArrowFunction) {
453+
// if the enclosing function is an ArrowFunction is then we use the captured 'this' keyword.
474454
convertedLoopState.containsLexicalThis = true;
475455
return node;
476456
}
@@ -1306,12 +1286,7 @@ namespace ts {
13061286
enableSubstitutionsForCapturedThis();
13071287
}
13081288

1309-
const savedUseCapturedThis = useCapturedThis;
1310-
useCapturedThis = true;
1311-
13121289
const func = transformFunctionLikeToExpression(node, /*location*/ node, /*name*/ undefined);
1313-
1314-
useCapturedThis = savedUseCapturedThis;
13151290
setNodeEmitFlags(func, NodeEmitFlags.CapturesThis);
13161291
return func;
13171292
}
@@ -1354,9 +1329,9 @@ namespace ts {
13541329
* @param name The name of the new FunctionExpression.
13551330
*/
13561331
function transformFunctionLikeToExpression(node: FunctionLikeDeclaration, location: TextRange, name: Identifier): FunctionExpression {
1357-
const savedContainingNonArrowFunction = containingNonArrowFunction;
1332+
const savedContainingNonArrowFunction = enclosingNonArrowFunction;
13581333
if (node.kind !== SyntaxKind.ArrowFunction) {
1359-
containingNonArrowFunction = node;
1334+
enclosingNonArrowFunction = node;
13601335
}
13611336

13621337
const expression = setOriginalNode(
@@ -1372,7 +1347,7 @@ namespace ts {
13721347
/*original*/ node
13731348
);
13741349

1375-
containingNonArrowFunction = savedContainingNonArrowFunction;
1350+
enclosingNonArrowFunction = savedContainingNonArrowFunction;
13761351
return expression;
13771352
}
13781353

@@ -2066,8 +2041,8 @@ namespace ts {
20662041
}
20672042

20682043
const isAsyncBlockContainingAwait =
2069-
containingNonArrowFunction
2070-
&& (containingNonArrowFunction.emitFlags & NodeEmitFlags.AsyncFunctionBody) !== 0
2044+
enclosingNonArrowFunction
2045+
&& (enclosingNonArrowFunction.emitFlags & NodeEmitFlags.AsyncFunctionBody) !== 0
20712046
&& (node.statement.transformFlags & TransformFlags.ContainsYield) !== 0;
20722047

20732048
let loopBodyFlags: NodeEmitFlags = 0;
@@ -2829,9 +2804,9 @@ namespace ts {
28292804
* Visits the `super` keyword
28302805
*/
28312806
function visitSuperKeyword(node: PrimaryExpression): LeftHandSideExpression {
2832-
return superScopeContainer
2833-
&& isClassElement(superScopeContainer)
2834-
&& !hasModifier(superScopeContainer, ModifierFlags.Static)
2807+
return enclosingNonAsyncFunctionBody
2808+
&& isClassElement(enclosingNonAsyncFunctionBody)
2809+
&& !hasModifier(enclosingNonAsyncFunctionBody, ModifierFlags.Static)
28352810
&& currentParent.kind !== SyntaxKind.CallExpression
28362811
? createPropertyAccess(createIdentifier("_super"), "prototype")
28372812
: createIdentifier("_super");
@@ -2856,17 +2831,16 @@ namespace ts {
28562831
* @param node The node to be printed.
28572832
*/
28582833
function onEmitNode(node: Node, emit: (node: Node) => void) {
2859-
const savedUseCapturedThis = useCapturedThis;
2834+
const savedEnclosingFunction = enclosingFunction;
28602835

28612836
if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && isFunctionLike(node)) {
2862-
// If we are tracking a captured `this`, push a bit that indicates whether the
2863-
// containing function is an arrow function.
2864-
useCapturedThis = (getNodeEmitFlags(node) & NodeEmitFlags.CapturesThis) !== 0;
2837+
// If we are tracking a captured `this`, keep track of the enclosing function.
2838+
enclosingFunction = node;
28652839
}
28662840

28672841
previousOnEmitNode(node, emit);
28682842

2869-
useCapturedThis = savedUseCapturedThis;
2843+
enclosingFunction = savedEnclosingFunction;
28702844
}
28712845

28722846
/**
@@ -2994,7 +2968,9 @@ namespace ts {
29942968
* @param node The ThisKeyword node.
29952969
*/
29962970
function substituteThisKeyword(node: PrimaryExpression): PrimaryExpression {
2997-
if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis && useCapturedThis) {
2971+
if (enabledSubstitutions & ES6SubstitutionFlags.CapturedThis
2972+
&& enclosingFunction
2973+
&& enclosingFunction.emitFlags & NodeEmitFlags.CapturesThis) {
29982974
return createIdentifier("_this", /*location*/ node);
29992975
}
30002976

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//// [blockScopedBindingCaptureThisInFunction.ts]
2+
// https://github.com/Microsoft/TypeScript/issues/11038
3+
() => function () {
4+
for (let someKey in {}) {
5+
this.helloWorld();
6+
() => someKey;
7+
}
8+
};
9+
10+
//// [blockScopedBindingCaptureThisInFunction.js]
11+
// https://github.com/Microsoft/TypeScript/issues/11038
12+
(function () { return function () {
13+
var _loop_1 = function (someKey) {
14+
this_1.helloWorld();
15+
(function () { return someKey; });
16+
};
17+
var this_1 = this;
18+
for (var someKey in {}) {
19+
_loop_1(someKey);
20+
}
21+
}; });
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/compiler/blockScopedBindingCaptureThisInFunction.ts ===
2+
// https://github.com/Microsoft/TypeScript/issues/11038
3+
() => function () {
4+
for (let someKey in {}) {
5+
>someKey : Symbol(someKey, Decl(blockScopedBindingCaptureThisInFunction.ts, 2, 12))
6+
7+
this.helloWorld();
8+
() => someKey;
9+
>someKey : Symbol(someKey, Decl(blockScopedBindingCaptureThisInFunction.ts, 2, 12))
10+
}
11+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/compiler/blockScopedBindingCaptureThisInFunction.ts ===
2+
// https://github.com/Microsoft/TypeScript/issues/11038
3+
() => function () {
4+
>() => function () { for (let someKey in {}) { this.helloWorld(); () => someKey; }} : () => () => void
5+
>function () { for (let someKey in {}) { this.helloWorld(); () => someKey; }} : () => void
6+
7+
for (let someKey in {}) {
8+
>someKey : string
9+
>{} : {}
10+
11+
this.helloWorld();
12+
>this.helloWorld() : any
13+
>this.helloWorld : any
14+
>this : any
15+
>helloWorld : any
16+
17+
() => someKey;
18+
>() => someKey : () => string
19+
>someKey : string
20+
}
21+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// https://github.com/Microsoft/TypeScript/issues/11038
2+
() => function () {
3+
for (let someKey in {}) {
4+
this.helloWorld();
5+
() => someKey;
6+
}
7+
};

0 commit comments

Comments
 (0)