Skip to content

Commit b3268a7

Browse files
authored
Do not emit 'this' for __awaiter helper at the top level of a strict-mode file. (microsoft#32823)
1 parent bf054ae commit b3268a7

23 files changed

+144
-75
lines changed

src/compiler/transformers/es2017.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ namespace ts {
4141
/** A set of node IDs for generated super accessors (variable statements). */
4242
const substitutedSuperAccessors: boolean[] = [];
4343

44+
let topLevel: boolean;
45+
4446
// Save the previous transformation hooks.
4547
const previousOnEmitNode = context.onEmitNode;
4648
const previousOnSubstituteNode = context.onSubstituteNode;
@@ -56,11 +58,26 @@ namespace ts {
5658
return node;
5759
}
5860

61+
topLevel = isEffectiveStrictModeSourceFile(node, compilerOptions);
5962
const visited = visitEachChild(node, visitor, context);
6063
addEmitHelpers(visited, context.readEmitHelpers());
6164
return visited;
6265
}
6366

67+
function doOutsideOfTopLevel<T, U>(cb: (value: T) => U, value: T) {
68+
if (topLevel) {
69+
topLevel = false;
70+
const result = cb(value);
71+
topLevel = true;
72+
return result;
73+
}
74+
return cb(value);
75+
}
76+
77+
function visitDefault(node: Node): VisitResult<Node> {
78+
return visitEachChild(node, visitor, context);
79+
}
80+
6481
function visitor(node: Node): VisitResult<Node> {
6582
if ((node.transformFlags & TransformFlags.ContainsES2017) === 0) {
6683
return node;
@@ -74,13 +91,13 @@ namespace ts {
7491
return visitAwaitExpression(<AwaitExpression>node);
7592

7693
case SyntaxKind.MethodDeclaration:
77-
return visitMethodDeclaration(<MethodDeclaration>node);
94+
return doOutsideOfTopLevel(visitMethodDeclaration, <MethodDeclaration>node);
7895

7996
case SyntaxKind.FunctionDeclaration:
80-
return visitFunctionDeclaration(<FunctionDeclaration>node);
97+
return doOutsideOfTopLevel(visitFunctionDeclaration, <FunctionDeclaration>node);
8198

8299
case SyntaxKind.FunctionExpression:
83-
return visitFunctionExpression(<FunctionExpression>node);
100+
return doOutsideOfTopLevel(visitFunctionExpression, <FunctionExpression>node);
84101

85102
case SyntaxKind.ArrowFunction:
86103
return visitArrowFunction(<ArrowFunction>node);
@@ -97,6 +114,13 @@ namespace ts {
97114
}
98115
return visitEachChild(node, visitor, context);
99116

117+
case SyntaxKind.GetAccessor:
118+
case SyntaxKind.SetAccessor:
119+
case SyntaxKind.Constructor:
120+
case SyntaxKind.ClassDeclaration:
121+
case SyntaxKind.ClassExpression:
122+
return doOutsideOfTopLevel(visitDefault, node);
123+
100124
default:
101125
return visitEachChild(node, visitor, context);
102126
}
@@ -433,6 +457,7 @@ namespace ts {
433457
createReturn(
434458
createAwaiterHelper(
435459
context,
460+
!topLevel,
436461
hasLexicalArguments,
437462
promiseConstructor,
438463
transformAsyncFunctionBodyWorker(<Block>node.body, statementOffset)
@@ -473,6 +498,7 @@ namespace ts {
473498
else {
474499
const expression = createAwaiterHelper(
475500
context,
501+
!topLevel,
476502
hasLexicalArguments,
477503
promiseConstructor,
478504
transformAsyncFunctionBodyWorker(node.body!)
@@ -786,7 +812,7 @@ namespace ts {
786812
};`
787813
};
788814

789-
function createAwaiterHelper(context: TransformationContext, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression | undefined, body: Block) {
815+
function createAwaiterHelper(context: TransformationContext, hasLexicalThis: boolean, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression | undefined, body: Block) {
790816
context.requestEmitHelper(awaiterHelper);
791817

792818
const generatorFunc = createFunctionExpression(
@@ -806,7 +832,7 @@ namespace ts {
806832
getUnscopedHelperName("__awaiter"),
807833
/*typeArguments*/ undefined,
808834
[
809-
createThis(),
835+
hasLexicalThis ? createThis() : createVoidZero(),
810836
hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(),
811837
promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(),
812838
generatorFunc

src/compiler/transformers/es2018.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace ts {
2626
let enabledSubstitutions: ESNextSubstitutionFlags;
2727
let enclosingFunctionFlags: FunctionFlags;
2828
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
29+
let topLevel: boolean;
2930

3031
/** Keeps track of property names accessed on super (`super.x`) within async functions. */
3132
let capturedSuperProperties: UnderscoreEscapedMap<true>;
@@ -42,6 +43,7 @@ namespace ts {
4243
}
4344

4445
exportedVariableStatement = false;
46+
topLevel = isEffectiveStrictModeSourceFile(node, compilerOptions);
4547
const visited = visitEachChild(node, visitor, context);
4648
addEmitHelpers(visited, context.readEmitHelpers());
4749
return visited;
@@ -62,6 +64,20 @@ namespace ts {
6264
return node;
6365
}
6466

67+
function doOutsideOfTopLevel<T, U>(cb: (value: T) => U, value: T) {
68+
if (topLevel) {
69+
topLevel = false;
70+
const result = cb(value);
71+
topLevel = true;
72+
return result;
73+
}
74+
return cb(value);
75+
}
76+
77+
function visitDefault(node: Node): VisitResult<Node> {
78+
return visitEachChild(node, visitor, context);
79+
}
80+
6581
function visitorWorker(node: Node, noDestructuringValue: boolean): VisitResult<Node> {
6682
if ((node.transformFlags & TransformFlags.ContainsES2018) === 0) {
6783
return node;
@@ -92,17 +108,17 @@ namespace ts {
92108
case SyntaxKind.VoidExpression:
93109
return visitVoidExpression(node as VoidExpression);
94110
case SyntaxKind.Constructor:
95-
return visitConstructorDeclaration(node as ConstructorDeclaration);
111+
return doOutsideOfTopLevel(visitConstructorDeclaration, node as ConstructorDeclaration);
96112
case SyntaxKind.MethodDeclaration:
97-
return visitMethodDeclaration(node as MethodDeclaration);
113+
return doOutsideOfTopLevel(visitMethodDeclaration, node as MethodDeclaration);
98114
case SyntaxKind.GetAccessor:
99-
return visitGetAccessorDeclaration(node as GetAccessorDeclaration);
115+
return doOutsideOfTopLevel(visitGetAccessorDeclaration, node as GetAccessorDeclaration);
100116
case SyntaxKind.SetAccessor:
101-
return visitSetAccessorDeclaration(node as SetAccessorDeclaration);
117+
return doOutsideOfTopLevel(visitSetAccessorDeclaration, node as SetAccessorDeclaration);
102118
case SyntaxKind.FunctionDeclaration:
103-
return visitFunctionDeclaration(node as FunctionDeclaration);
119+
return doOutsideOfTopLevel(visitFunctionDeclaration, node as FunctionDeclaration);
104120
case SyntaxKind.FunctionExpression:
105-
return visitFunctionExpression(node as FunctionExpression);
121+
return doOutsideOfTopLevel(visitFunctionExpression, node as FunctionExpression);
106122
case SyntaxKind.ArrowFunction:
107123
return visitArrowFunction(node as ArrowFunction);
108124
case SyntaxKind.Parameter:
@@ -121,6 +137,9 @@ namespace ts {
121137
hasSuperElementAccess = true;
122138
}
123139
return visitEachChild(node, visitor, context);
140+
case SyntaxKind.ClassDeclaration:
141+
case SyntaxKind.ClassExpression:
142+
return doOutsideOfTopLevel(visitDefault, node);
124143
default:
125144
return visitEachChild(node, visitor, context);
126145
}
@@ -754,7 +773,8 @@ namespace ts {
754773
node.body!,
755774
visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset)
756775
)
757-
)
776+
),
777+
!topLevel
758778
)
759779
);
760780

@@ -1057,7 +1077,7 @@ namespace ts {
10571077
};`
10581078
};
10591079

1060-
function createAsyncGeneratorHelper(context: TransformationContext, generatorFunc: FunctionExpression) {
1080+
function createAsyncGeneratorHelper(context: TransformationContext, generatorFunc: FunctionExpression, hasLexicalThis: boolean) {
10611081
context.requestEmitHelper(awaitHelper);
10621082
context.requestEmitHelper(asyncGeneratorHelper);
10631083

@@ -1068,7 +1088,7 @@ namespace ts {
10681088
getUnscopedHelperName("__asyncGenerator"),
10691089
/*typeArguments*/ undefined,
10701090
[
1071-
createThis(),
1091+
hasLexicalThis ? createThis() : createVoidZero(),
10721092
createIdentifier("arguments"),
10731093
generatorFunc
10741094
]

src/compiler/utilities.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,43 @@ namespace ts {
700700
return isExternalModule(node) || compilerOptions.isolatedModules || ((getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS) && !!node.commonJsModuleIndicator);
701701
}
702702

703+
/**
704+
* Returns whether the source file will be treated as if it were in strict mode at runtime.
705+
*/
706+
export function isEffectiveStrictModeSourceFile(node: SourceFile, compilerOptions: CompilerOptions) {
707+
// We can only verify strict mode for JS/TS files
708+
switch (node.scriptKind) {
709+
case ScriptKind.JS:
710+
case ScriptKind.TS:
711+
case ScriptKind.JSX:
712+
case ScriptKind.TSX:
713+
break;
714+
default:
715+
return false;
716+
}
717+
// Strict mode does not matter for declaration files.
718+
if (node.isDeclarationFile) {
719+
return false;
720+
}
721+
// If `alwaysStrict` is set, then treat the file as strict.
722+
if (getStrictOptionValue(compilerOptions, "alwaysStrict")) {
723+
return true;
724+
}
725+
// Starting with a "use strict" directive indicates the file is strict.
726+
if (startsWithUseStrict(node.statements)) {
727+
return true;
728+
}
729+
if (isExternalModule(node) || compilerOptions.isolatedModules) {
730+
// ECMAScript Modules are always strict.
731+
if (getEmitModuleKind(compilerOptions) >= ModuleKind.ES2015) {
732+
return true;
733+
}
734+
// Other modules are strict unless otherwise specified.
735+
return !compilerOptions.noImplicitUseStrict;
736+
}
737+
return false;
738+
}
739+
703740
export function isBlockScope(node: Node, parentNode: Node): boolean {
704741
switch (node.kind) {
705742
case SyntaxKind.SourceFile:

tests/baselines/reference/asyncAwaitIsolatedModules_es5.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
7777
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
7878
}
7979
};
80-
var _this = this;
8180
Object.defineProperty(exports, "__esModule", { value: true });
8281
var missing_1 = require("missing");
8382
function f0() {
@@ -110,25 +109,25 @@ var f6 = function () {
110109
return [2 /*return*/];
111110
}); });
112111
};
113-
var f7 = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
112+
var f7 = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
114113
return [2 /*return*/];
115114
}); }); };
116-
var f8 = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
115+
var f8 = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
117116
return [2 /*return*/];
118117
}); }); };
119-
var f9 = function () { return __awaiter(_this, void 0, missing_1.MyPromise, function () { return __generator(this, function (_a) {
118+
var f9 = function () { return __awaiter(void 0, void 0, missing_1.MyPromise, function () { return __generator(this, function (_a) {
120119
return [2 /*return*/];
121120
}); }); };
122-
var f10 = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
121+
var f10 = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
123122
return [2 /*return*/, p];
124123
}); }); };
125-
var f11 = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
124+
var f11 = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
126125
return [2 /*return*/, mp];
127126
}); }); };
128-
var f12 = function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
127+
var f12 = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
129128
return [2 /*return*/, mp];
130129
}); }); };
131-
var f13 = function () { return __awaiter(_this, void 0, missing_1.MyPromise, function () { return __generator(this, function (_a) {
130+
var f13 = function () { return __awaiter(void 0, void 0, missing_1.MyPromise, function () { return __generator(this, function (_a) {
132131
return [2 /*return*/, p];
133132
}); }); };
134133
var o = {

tests/baselines/reference/asyncAwaitIsolatedModules_es6.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ let f5 = function () {
6767
let f6 = function () {
6868
return __awaiter(this, void 0, void 0, function* () { });
6969
};
70-
let f7 = () => __awaiter(this, void 0, void 0, function* () { });
71-
let f8 = () => __awaiter(this, void 0, void 0, function* () { });
72-
let f9 = () => __awaiter(this, void 0, void 0, function* () { });
73-
let f10 = () => __awaiter(this, void 0, void 0, function* () { return p; });
74-
let f11 = () => __awaiter(this, void 0, void 0, function* () { return mp; });
75-
let f12 = () => __awaiter(this, void 0, void 0, function* () { return mp; });
76-
let f13 = () => __awaiter(this, void 0, void 0, function* () { return p; });
70+
let f7 = () => __awaiter(void 0, void 0, void 0, function* () { });
71+
let f8 = () => __awaiter(void 0, void 0, void 0, function* () { });
72+
let f9 = () => __awaiter(void 0, void 0, void 0, function* () { });
73+
let f10 = () => __awaiter(void 0, void 0, void 0, function* () { return p; });
74+
let f11 = () => __awaiter(void 0, void 0, void 0, function* () { return mp; });
75+
let f12 = () => __awaiter(void 0, void 0, void 0, function* () { return mp; });
76+
let f13 = () => __awaiter(void 0, void 0, void 0, function* () { return p; });
7777
let o = {
7878
m1() {
7979
return __awaiter(this, void 0, void 0, function* () { });

tests/baselines/reference/asyncFunctionsAcrossFiles.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
2727
};
2828
import { a } from './a';
2929
export const b = {
30-
f: () => __awaiter(this, void 0, void 0, function* () {
30+
f: () => __awaiter(void 0, void 0, void 0, function* () {
3131
yield a.f();
3232
})
3333
};
@@ -43,7 +43,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
4343
};
4444
import { b } from './b';
4545
export const a = {
46-
f: () => __awaiter(this, void 0, void 0, function* () {
46+
f: () => __awaiter(void 0, void 0, void 0, function* () {
4747
yield b.f();
4848
})
4949
};

tests/baselines/reference/exportDefaultAsyncFunction2.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
4343
step((generator = generator.apply(thisArg, _arguments || [])).next());
4444
});
4545
};
46-
export default () => __awaiter(this, void 0, void 0, function* () { return 0; });
46+
export default () => __awaiter(void 0, void 0, void 0, function* () { return 0; });
4747
//// [c.js]
4848
import { async } from 'asyncawait';
4949
export default async();

tests/baselines/reference/importCallExpressionAsyncES3AMD.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
6767
};
6868
define(["require", "exports"], function (require, exports) {
6969
"use strict";
70-
var _this = this;
7170
exports.__esModule = true;
7271
function fn() {
7372
return __awaiter(this, void 0, void 0, function () {
@@ -105,7 +104,7 @@ define(["require", "exports"], function (require, exports) {
105104
}());
106105
exports.cl1 = cl1;
107106
exports.obj = {
108-
m: function () { return __awaiter(_this, void 0, void 0, function () {
107+
m: function () { return __awaiter(void 0, void 0, void 0, function () {
109108
var req;
110109
return __generator(this, function (_a) {
111110
switch (_a.label) {
@@ -139,7 +138,7 @@ define(["require", "exports"], function (require, exports) {
139138
return cl2;
140139
}());
141140
exports.cl2 = cl2;
142-
exports.l = function () { return __awaiter(_this, void 0, void 0, function () {
141+
exports.l = function () { return __awaiter(void 0, void 0, void 0, function () {
143142
var req;
144143
return __generator(this, function (_a) {
145144
switch (_a.label) {

tests/baselines/reference/importCallExpressionAsyncES3CJS.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
6666
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
6767
}
6868
};
69-
var _this = this;
7069
exports.__esModule = true;
7170
function fn() {
7271
return __awaiter(this, void 0, void 0, function () {
@@ -104,7 +103,7 @@ var cl1 = /** @class */ (function () {
104103
}());
105104
exports.cl1 = cl1;
106105
exports.obj = {
107-
m: function () { return __awaiter(_this, void 0, void 0, function () {
106+
m: function () { return __awaiter(void 0, void 0, void 0, function () {
108107
var req;
109108
return __generator(this, function (_a) {
110109
switch (_a.label) {
@@ -138,7 +137,7 @@ var cl2 = /** @class */ (function () {
138137
return cl2;
139138
}());
140139
exports.cl2 = cl2;
141-
exports.l = function () { return __awaiter(_this, void 0, void 0, function () {
140+
exports.l = function () { return __awaiter(void 0, void 0, void 0, function () {
142141
var req;
143142
return __generator(this, function (_a) {
144143
switch (_a.label) {

0 commit comments

Comments
 (0)