Merge pull request #5747 from Microsoft/thisWithLetConst

correctly capture 'this' when converting loops into functions
This commit is contained in:
Vladimir Matveev 2015-11-22 21:53:20 -08:00
commit fbaba90ac9
9 changed files with 874 additions and 0 deletions

View File

@ -402,6 +402,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
*/
argumentsName?: string;
/*
* alias for 'this' from the calling code stack frame in case if this was used inside the converted loop
*/
thisName?: string;
/*
* list of non-block scoped variable declarations that appear inside converted loop
* such variable declarations should be moved outside the loop body
@ -1992,6 +1997,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.LexicalThis) {
write("_this");
}
else if (convertedLoopState) {
write(convertedLoopState.thisName || (convertedLoopState.thisName = makeUniqueName("this")));
}
else {
write("this");
}
@ -3322,6 +3330,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
convertedLoopState.argumentsName = convertedOuterLoopState.argumentsName;
}
if (convertedOuterLoopState.thisName) {
// outer loop has already used 'this' so we've already have some name to alias it
// use the same name in all nested loops
convertedLoopState.thisName = convertedOuterLoopState.thisName;
}
if (convertedOuterLoopState.hoistedLocalVariables) {
// we've already collected some non-block scoped variable declarations in enclosing loop
// use the same storage in nested loop
@ -3351,6 +3365,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
writeLine();
}
}
if (convertedLoopState.thisName) {
// if alias for this is set
if (convertedOuterLoopState) {
// pass it to outer converted loop
convertedOuterLoopState.thisName = convertedLoopState.thisName;
}
else {
// this is top level converted loop so we need to create an alias for 'this' here
// NOTE:
// if converted loops were all nested in arrow function then we'll always emit '_this' so convertedLoopState.thisName will not be set.
// If it is set this means that all nested loops are not nested in arrow function and it is safe to capture 'this'.
write(`var ${convertedLoopState.thisName} = this;`);
writeLine();
}
}
if (convertedLoopState.hoistedLocalVariables) {
// if hoistedLocalVariables !== undefined this means that we've possibly collected some variable declarations to be hoisted later

View File

@ -0,0 +1,124 @@
//// [capturedLetConstInLoop10.ts]
class A {
foo() {
for (let x of [0]) {
let f = function() { return x; };
this.bar(f());
}
}
bar(a: number) {
}
baz() {
for (let x of [1]) {
let a = function() { return x; };
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
this.bar(a());
}
}
baz2() {
for (let x of [1]) {
let a = function() { return x; };
this.bar(a());
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
}
}
}
class B {
foo() {
let a =
() => {
for (let x of [0]) {
let f = () => x;
this.bar(f());
}
}
}
bar(a: number) {
}
}
//// [capturedLetConstInLoop10.js]
var A = (function () {
function A() {
}
A.prototype.foo = function () {
var _loop_1 = function(x) {
var f = function () { return x; };
this_1.bar(f());
};
var this_1 = this;
for (var _i = 0, _a = [0]; _i < _a.length; _i++) {
var x = _a[_i];
_loop_1(x);
}
};
A.prototype.bar = function (a) {
};
A.prototype.baz = function () {
var _loop_2 = function(x) {
var a = function () { return x; };
var _loop_3 = function(y) {
var b = function () { return y; };
this_2.bar(b());
};
for (var _i = 0, _a = [1]; _i < _a.length; _i++) {
var y = _a[_i];
_loop_3(y);
}
this_2.bar(a());
};
var this_2 = this;
for (var _b = 0, _c = [1]; _b < _c.length; _b++) {
var x = _c[_b];
_loop_2(x);
}
};
A.prototype.baz2 = function () {
var _loop_4 = function(x) {
var a = function () { return x; };
this_3.bar(a());
var _loop_5 = function(y) {
var b = function () { return y; };
this_3.bar(b());
};
for (var _i = 0, _a = [1]; _i < _a.length; _i++) {
var y = _a[_i];
_loop_5(y);
}
};
var this_3 = this;
for (var _b = 0, _c = [1]; _b < _c.length; _b++) {
var x = _c[_b];
_loop_4(x);
}
};
return A;
})();
var B = (function () {
function B() {
}
B.prototype.foo = function () {
var _this = this;
var a = function () {
var _loop_6 = function(x) {
var f = function () { return x; };
_this.bar(f());
};
for (var _i = 0, _a = [0]; _i < _a.length; _i++) {
var x = _a[_i];
_loop_6(x);
}
};
};
B.prototype.bar = function (a) {
};
return B;
})();

View File

@ -0,0 +1,119 @@
=== tests/cases/compiler/capturedLetConstInLoop10.ts ===
class A {
>A : Symbol(A, Decl(capturedLetConstInLoop10.ts, 0, 0))
foo() {
>foo : Symbol(foo, Decl(capturedLetConstInLoop10.ts, 0, 9))
for (let x of [0]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 2, 16))
let f = function() { return x; };
>f : Symbol(f, Decl(capturedLetConstInLoop10.ts, 3, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 2, 16))
this.bar(f());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>f : Symbol(f, Decl(capturedLetConstInLoop10.ts, 3, 15))
}
}
bar(a: number) {
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10.ts, 7, 8))
}
baz() {
>baz : Symbol(baz, Decl(capturedLetConstInLoop10.ts, 8, 5))
for (let x of [1]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 11, 16))
let a = function() { return x; };
>a : Symbol(a, Decl(capturedLetConstInLoop10.ts, 12, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 11, 16))
for (let y of [1]) {
>y : Symbol(y, Decl(capturedLetConstInLoop10.ts, 13, 20))
let b = function() { return y; };
>b : Symbol(b, Decl(capturedLetConstInLoop10.ts, 14, 19))
>y : Symbol(y, Decl(capturedLetConstInLoop10.ts, 13, 20))
this.bar(b());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>b : Symbol(b, Decl(capturedLetConstInLoop10.ts, 14, 19))
}
this.bar(a());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10.ts, 12, 15))
}
}
baz2() {
>baz2 : Symbol(baz2, Decl(capturedLetConstInLoop10.ts, 19, 5))
for (let x of [1]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 21, 16))
let a = function() { return x; };
>a : Symbol(a, Decl(capturedLetConstInLoop10.ts, 22, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 21, 16))
this.bar(a());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10.ts, 22, 15))
for (let y of [1]) {
>y : Symbol(y, Decl(capturedLetConstInLoop10.ts, 24, 20))
let b = function() { return y; };
>b : Symbol(b, Decl(capturedLetConstInLoop10.ts, 25, 19))
>y : Symbol(y, Decl(capturedLetConstInLoop10.ts, 24, 20))
this.bar(b());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 6, 5))
>b : Symbol(b, Decl(capturedLetConstInLoop10.ts, 25, 19))
}
}
}
}
class B {
>B : Symbol(B, Decl(capturedLetConstInLoop10.ts, 30, 1))
foo() {
>foo : Symbol(foo, Decl(capturedLetConstInLoop10.ts, 32, 9))
let a =
>a : Symbol(a, Decl(capturedLetConstInLoop10.ts, 34, 11))
() => {
for (let x of [0]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 36, 24))
let f = () => x;
>f : Symbol(f, Decl(capturedLetConstInLoop10.ts, 37, 23))
>x : Symbol(x, Decl(capturedLetConstInLoop10.ts, 36, 24))
this.bar(f());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 41, 5))
>this : Symbol(B, Decl(capturedLetConstInLoop10.ts, 30, 1))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 41, 5))
>f : Symbol(f, Decl(capturedLetConstInLoop10.ts, 37, 23))
}
}
}
bar(a: number) {
>bar : Symbol(bar, Decl(capturedLetConstInLoop10.ts, 41, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10.ts, 42, 8))
}
}

View File

@ -0,0 +1,151 @@
=== tests/cases/compiler/capturedLetConstInLoop10.ts ===
class A {
>A : A
foo() {
>foo : () => void
for (let x of [0]) {
>x : number
>[0] : number[]
>0 : number
let f = function() { return x; };
>f : () => number
>function() { return x; } : () => number
>x : number
this.bar(f());
>this.bar(f()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>f() : number
>f : () => number
}
}
bar(a: number) {
>bar : (a: number) => void
>a : number
}
baz() {
>baz : () => void
for (let x of [1]) {
>x : number
>[1] : number[]
>1 : number
let a = function() { return x; };
>a : () => number
>function() { return x; } : () => number
>x : number
for (let y of [1]) {
>y : number
>[1] : number[]
>1 : number
let b = function() { return y; };
>b : () => number
>function() { return y; } : () => number
>y : number
this.bar(b());
>this.bar(b()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>b() : number
>b : () => number
}
this.bar(a());
>this.bar(a()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>a() : number
>a : () => number
}
}
baz2() {
>baz2 : () => void
for (let x of [1]) {
>x : number
>[1] : number[]
>1 : number
let a = function() { return x; };
>a : () => number
>function() { return x; } : () => number
>x : number
this.bar(a());
>this.bar(a()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>a() : number
>a : () => number
for (let y of [1]) {
>y : number
>[1] : number[]
>1 : number
let b = function() { return y; };
>b : () => number
>function() { return y; } : () => number
>y : number
this.bar(b());
>this.bar(b()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>b() : number
>b : () => number
}
}
}
}
class B {
>B : B
foo() {
>foo : () => void
let a =
>a : () => void
() => {
>() => { for (let x of [0]) { let f = () => x; this.bar(f()); } } : () => void
for (let x of [0]) {
>x : number
>[0] : number[]
>0 : number
let f = () => x;
>f : () => number
>() => x : () => number
>x : number
this.bar(f());
>this.bar(f()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>f() : number
>f : () => number
}
}
}
bar(a: number) {
>bar : (a: number) => void
>a : number
}
}

View File

@ -0,0 +1,90 @@
//// [capturedLetConstInLoop10_ES6.ts]
class A {
foo() {
for (let x of [0]) {
let f = function() { return x; };
this.bar(f());
}
}
bar(a: number) {
}
baz() {
for (let x of [1]) {
let a = function() { return x; };
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
this.bar(a());
}
}
baz2() {
for (let x of [1]) {
let a = function() { return x; };
this.bar(a());
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
}
}
}
class B {
foo() {
let a =
() => {
for (let x of [0]) {
let f = () => x;
this.bar(f());
}
}
}
bar(a: number) {
}
}
//// [capturedLetConstInLoop10_ES6.js]
class A {
foo() {
for (let x of [0]) {
let f = function () { return x; };
this.bar(f());
}
}
bar(a) {
}
baz() {
for (let x of [1]) {
let a = function () { return x; };
for (let y of [1]) {
let b = function () { return y; };
this.bar(b());
}
this.bar(a());
}
}
baz2() {
for (let x of [1]) {
let a = function () { return x; };
this.bar(a());
for (let y of [1]) {
let b = function () { return y; };
this.bar(b());
}
}
}
}
class B {
foo() {
let a = () => {
for (let x of [0]) {
let f = () => x;
this.bar(f());
}
};
}
bar(a) {
}
}

View File

@ -0,0 +1,119 @@
=== tests/cases/compiler/capturedLetConstInLoop10_ES6.ts ===
class A {
>A : Symbol(A, Decl(capturedLetConstInLoop10_ES6.ts, 0, 0))
foo() {
>foo : Symbol(foo, Decl(capturedLetConstInLoop10_ES6.ts, 0, 9))
for (let x of [0]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 2, 16))
let f = function() { return x; };
>f : Symbol(f, Decl(capturedLetConstInLoop10_ES6.ts, 3, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 2, 16))
this.bar(f());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10_ES6.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>f : Symbol(f, Decl(capturedLetConstInLoop10_ES6.ts, 3, 15))
}
}
bar(a: number) {
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10_ES6.ts, 7, 8))
}
baz() {
>baz : Symbol(baz, Decl(capturedLetConstInLoop10_ES6.ts, 8, 5))
for (let x of [1]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 11, 16))
let a = function() { return x; };
>a : Symbol(a, Decl(capturedLetConstInLoop10_ES6.ts, 12, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 11, 16))
for (let y of [1]) {
>y : Symbol(y, Decl(capturedLetConstInLoop10_ES6.ts, 13, 20))
let b = function() { return y; };
>b : Symbol(b, Decl(capturedLetConstInLoop10_ES6.ts, 14, 19))
>y : Symbol(y, Decl(capturedLetConstInLoop10_ES6.ts, 13, 20))
this.bar(b());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10_ES6.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>b : Symbol(b, Decl(capturedLetConstInLoop10_ES6.ts, 14, 19))
}
this.bar(a());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10_ES6.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10_ES6.ts, 12, 15))
}
}
baz2() {
>baz2 : Symbol(baz2, Decl(capturedLetConstInLoop10_ES6.ts, 19, 5))
for (let x of [1]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 21, 16))
let a = function() { return x; };
>a : Symbol(a, Decl(capturedLetConstInLoop10_ES6.ts, 22, 15))
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 21, 16))
this.bar(a());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10_ES6.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10_ES6.ts, 22, 15))
for (let y of [1]) {
>y : Symbol(y, Decl(capturedLetConstInLoop10_ES6.ts, 24, 20))
let b = function() { return y; };
>b : Symbol(b, Decl(capturedLetConstInLoop10_ES6.ts, 25, 19))
>y : Symbol(y, Decl(capturedLetConstInLoop10_ES6.ts, 24, 20))
this.bar(b());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>this : Symbol(A, Decl(capturedLetConstInLoop10_ES6.ts, 0, 0))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 6, 5))
>b : Symbol(b, Decl(capturedLetConstInLoop10_ES6.ts, 25, 19))
}
}
}
}
class B {
>B : Symbol(B, Decl(capturedLetConstInLoop10_ES6.ts, 30, 1))
foo() {
>foo : Symbol(foo, Decl(capturedLetConstInLoop10_ES6.ts, 32, 9))
let a =
>a : Symbol(a, Decl(capturedLetConstInLoop10_ES6.ts, 34, 11))
() => {
for (let x of [0]) {
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 36, 24))
let f = () => x;
>f : Symbol(f, Decl(capturedLetConstInLoop10_ES6.ts, 37, 23))
>x : Symbol(x, Decl(capturedLetConstInLoop10_ES6.ts, 36, 24))
this.bar(f());
>this.bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 41, 5))
>this : Symbol(B, Decl(capturedLetConstInLoop10_ES6.ts, 30, 1))
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 41, 5))
>f : Symbol(f, Decl(capturedLetConstInLoop10_ES6.ts, 37, 23))
}
}
}
bar(a: number) {
>bar : Symbol(bar, Decl(capturedLetConstInLoop10_ES6.ts, 41, 5))
>a : Symbol(a, Decl(capturedLetConstInLoop10_ES6.ts, 42, 8))
}
}

View File

@ -0,0 +1,151 @@
=== tests/cases/compiler/capturedLetConstInLoop10_ES6.ts ===
class A {
>A : A
foo() {
>foo : () => void
for (let x of [0]) {
>x : number
>[0] : number[]
>0 : number
let f = function() { return x; };
>f : () => number
>function() { return x; } : () => number
>x : number
this.bar(f());
>this.bar(f()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>f() : number
>f : () => number
}
}
bar(a: number) {
>bar : (a: number) => void
>a : number
}
baz() {
>baz : () => void
for (let x of [1]) {
>x : number
>[1] : number[]
>1 : number
let a = function() { return x; };
>a : () => number
>function() { return x; } : () => number
>x : number
for (let y of [1]) {
>y : number
>[1] : number[]
>1 : number
let b = function() { return y; };
>b : () => number
>function() { return y; } : () => number
>y : number
this.bar(b());
>this.bar(b()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>b() : number
>b : () => number
}
this.bar(a());
>this.bar(a()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>a() : number
>a : () => number
}
}
baz2() {
>baz2 : () => void
for (let x of [1]) {
>x : number
>[1] : number[]
>1 : number
let a = function() { return x; };
>a : () => number
>function() { return x; } : () => number
>x : number
this.bar(a());
>this.bar(a()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>a() : number
>a : () => number
for (let y of [1]) {
>y : number
>[1] : number[]
>1 : number
let b = function() { return y; };
>b : () => number
>function() { return y; } : () => number
>y : number
this.bar(b());
>this.bar(b()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>b() : number
>b : () => number
}
}
}
}
class B {
>B : B
foo() {
>foo : () => void
let a =
>a : () => void
() => {
>() => { for (let x of [0]) { let f = () => x; this.bar(f()); } } : () => void
for (let x of [0]) {
>x : number
>[0] : number[]
>0 : number
let f = () => x;
>f : () => number
>() => x : () => number
>x : number
this.bar(f());
>this.bar(f()) : void
>this.bar : (a: number) => void
>this : this
>bar : (a: number) => void
>f() : number
>f : () => number
}
}
}
bar(a: number) {
>bar : (a: number) => void
>a : number
}
}

View File

@ -0,0 +1,45 @@
class A {
foo() {
for (let x of [0]) {
let f = function() { return x; };
this.bar(f());
}
}
bar(a: number) {
}
baz() {
for (let x of [1]) {
let a = function() { return x; };
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
this.bar(a());
}
}
baz2() {
for (let x of [1]) {
let a = function() { return x; };
this.bar(a());
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
}
}
}
class B {
foo() {
let a =
() => {
for (let x of [0]) {
let f = () => x;
this.bar(f());
}
}
}
bar(a: number) {
}
}

View File

@ -0,0 +1,46 @@
// @target: ES6
class A {
foo() {
for (let x of [0]) {
let f = function() { return x; };
this.bar(f());
}
}
bar(a: number) {
}
baz() {
for (let x of [1]) {
let a = function() { return x; };
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
this.bar(a());
}
}
baz2() {
for (let x of [1]) {
let a = function() { return x; };
this.bar(a());
for (let y of [1]) {
let b = function() { return y; };
this.bar(b());
}
}
}
}
class B {
foo() {
let a =
() => {
for (let x of [0]) {
let f = () => x;
this.bar(f());
}
}
}
bar(a: number) {
}
}