@@ -12,9 +12,9 @@ namespace ts {
12
12
13
13
export function transformES2017 ( context : TransformationContext ) {
14
14
const {
15
- startLexicalEnvironment,
16
15
resumeLexicalEnvironment,
17
- endLexicalEnvironment
16
+ endLexicalEnvironment,
17
+ hoistVariableDeclaration
18
18
} = context ;
19
19
20
20
const resolver = context . getEmitResolver ( ) ;
@@ -33,6 +33,8 @@ namespace ts {
33
33
*/
34
34
let enclosingSuperContainerFlags : NodeCheckFlags = 0 ;
35
35
36
+ let enclosingFunctionParameterNames : UnderscoreEscapedMap < true > ;
37
+
36
38
// Save the previous transformation hooks.
37
39
const previousOnEmitNode = context . onEmitNode ;
38
40
const previousOnSubstituteNode = context . onSubstituteNode ;
@@ -83,6 +85,108 @@ namespace ts {
83
85
}
84
86
}
85
87
88
+ function asyncBodyVisitor ( node : Node ) : VisitResult < Node > {
89
+ if ( isNodeWithPossibleHoistedDeclaration ( node ) ) {
90
+ switch ( node . kind ) {
91
+ case SyntaxKind . VariableStatement :
92
+ return visitVariableStatementInAsyncBody ( node ) ;
93
+ case SyntaxKind . ForStatement :
94
+ return visitForStatementInAsyncBody ( node ) ;
95
+ case SyntaxKind . ForInStatement :
96
+ return visitForInStatementInAsyncBody ( node ) ;
97
+ case SyntaxKind . ForOfStatement :
98
+ return visitForOfStatementInAsyncBody ( node ) ;
99
+ case SyntaxKind . CatchClause :
100
+ return visitCatchClauseInAsyncBody ( node ) ;
101
+ case SyntaxKind . Block :
102
+ case SyntaxKind . SwitchStatement :
103
+ case SyntaxKind . CaseBlock :
104
+ case SyntaxKind . CaseClause :
105
+ case SyntaxKind . DefaultClause :
106
+ case SyntaxKind . TryStatement :
107
+ case SyntaxKind . DoStatement :
108
+ case SyntaxKind . WhileStatement :
109
+ case SyntaxKind . IfStatement :
110
+ case SyntaxKind . WithStatement :
111
+ case SyntaxKind . LabeledStatement :
112
+ return visitEachChild ( node , asyncBodyVisitor , context ) ;
113
+ default :
114
+ return Debug . assertNever ( node , "Unhandled node." ) ;
115
+ }
116
+ }
117
+ return visitor ( node ) ;
118
+ }
119
+
120
+ function visitCatchClauseInAsyncBody ( node : CatchClause ) {
121
+ const catchClauseNames = createUnderscoreEscapedMap < true > ( ) ;
122
+ recordDeclarationName ( node . variableDeclaration , catchClauseNames ) ;
123
+
124
+ // names declared in a catch variable are block scoped
125
+ let catchClauseUnshadowedNames : UnderscoreEscapedMap < true > ;
126
+ catchClauseNames . forEach ( ( _ , escapedName ) => {
127
+ if ( enclosingFunctionParameterNames . has ( escapedName ) ) {
128
+ if ( ! catchClauseUnshadowedNames ) {
129
+ catchClauseUnshadowedNames = cloneMap ( enclosingFunctionParameterNames ) ;
130
+ }
131
+ catchClauseUnshadowedNames . delete ( escapedName ) ;
132
+ }
133
+ } ) ;
134
+
135
+ if ( catchClauseUnshadowedNames ) {
136
+ const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames ;
137
+ enclosingFunctionParameterNames = catchClauseUnshadowedNames ;
138
+ const result = visitEachChild ( node , asyncBodyVisitor , context ) ;
139
+ enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames ;
140
+ return result ;
141
+ }
142
+ else {
143
+ return visitEachChild ( node , asyncBodyVisitor , context ) ;
144
+ }
145
+ }
146
+
147
+ function visitVariableStatementInAsyncBody ( node : VariableStatement ) {
148
+ if ( isVariableDeclarationListWithCollidingName ( node . declarationList ) ) {
149
+ const expression = visitVariableDeclarationListWithCollidingNames ( node . declarationList , /*hasReceiver*/ false ) ;
150
+ return expression ? createStatement ( expression ) : undefined ;
151
+ }
152
+ return visitEachChild ( node , visitor , context ) ;
153
+ }
154
+
155
+ function visitForInStatementInAsyncBody ( node : ForInStatement ) {
156
+ return updateForIn (
157
+ node ,
158
+ isVariableDeclarationListWithCollidingName ( node . initializer )
159
+ ? visitVariableDeclarationListWithCollidingNames ( node . initializer , /*hasReceiver*/ true )
160
+ : visitNode ( node . initializer , visitor , isForInitializer ) ,
161
+ visitNode ( node . expression , visitor , isExpression ) ,
162
+ visitNode ( node . statement , asyncBodyVisitor , isStatement , liftToBlock )
163
+ ) ;
164
+ }
165
+
166
+ function visitForOfStatementInAsyncBody ( node : ForOfStatement ) {
167
+ return updateForOf (
168
+ node ,
169
+ visitNode ( node . awaitModifier , visitor , isToken ) ,
170
+ isVariableDeclarationListWithCollidingName ( node . initializer )
171
+ ? visitVariableDeclarationListWithCollidingNames ( node . initializer , /*hasReceiver*/ true )
172
+ : visitNode ( node . initializer , visitor , isForInitializer ) ,
173
+ visitNode ( node . expression , visitor , isExpression ) ,
174
+ visitNode ( node . statement , asyncBodyVisitor , isStatement , liftToBlock )
175
+ ) ;
176
+ }
177
+
178
+ function visitForStatementInAsyncBody ( node : ForStatement ) {
179
+ return updateFor (
180
+ node ,
181
+ isVariableDeclarationListWithCollidingName ( node . initializer )
182
+ ? visitVariableDeclarationListWithCollidingNames ( node . initializer , /*hasReceiver*/ false )
183
+ : visitNode ( node . initializer , visitor , isForInitializer ) ,
184
+ visitNode ( node . condition , visitor , isExpression ) ,
185
+ visitNode ( node . incrementor , visitor , isExpression ) ,
186
+ visitNode ( ( < ForStatement > node ) . statement , asyncBodyVisitor , isStatement , liftToBlock )
187
+ ) ;
188
+ }
189
+
86
190
/**
87
191
* Visits an AwaitExpression node.
88
192
*
@@ -197,6 +301,82 @@ namespace ts {
197
301
) ;
198
302
}
199
303
304
+ function recordDeclarationName ( { name } : ParameterDeclaration | VariableDeclaration | BindingElement , names : UnderscoreEscapedMap < true > ) {
305
+ if ( isIdentifier ( name ) ) {
306
+ names . set ( name . escapedText , true ) ;
307
+ }
308
+ else {
309
+ for ( const element of name . elements ) {
310
+ if ( ! isOmittedExpression ( element ) ) {
311
+ recordDeclarationName ( element , names ) ;
312
+ }
313
+ }
314
+ }
315
+ }
316
+
317
+ function isVariableDeclarationListWithCollidingName ( node : ForInitializer ) : node is VariableDeclarationList {
318
+ return node
319
+ && isVariableDeclarationList ( node )
320
+ && ! ( node . flags & NodeFlags . BlockScoped )
321
+ && forEach ( node . declarations , collidesWithParameterName ) ;
322
+ }
323
+
324
+ function visitVariableDeclarationListWithCollidingNames ( node : VariableDeclarationList , hasReceiver : boolean ) {
325
+ hoistVariableDeclarationList ( node ) ;
326
+
327
+ const variables = getInitializedVariables ( node ) ;
328
+ if ( variables . length === 0 ) {
329
+ if ( hasReceiver ) {
330
+ return visitNode ( convertToAssignmentElementTarget ( node . declarations [ 0 ] . name ) , visitor , isExpression ) ;
331
+ }
332
+ return undefined ;
333
+ }
334
+
335
+ return inlineExpressions ( map ( variables , transformInitializedVariable ) ) ;
336
+ }
337
+
338
+ function hoistVariableDeclarationList ( node : VariableDeclarationList ) {
339
+ forEach ( node . declarations , hoistVariable ) ;
340
+ }
341
+
342
+ function hoistVariable ( { name } : VariableDeclaration | BindingElement ) {
343
+ if ( isIdentifier ( name ) ) {
344
+ hoistVariableDeclaration ( name ) ;
345
+ }
346
+ else {
347
+ for ( const element of name . elements ) {
348
+ if ( ! isOmittedExpression ( element ) ) {
349
+ hoistVariable ( element ) ;
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ function transformInitializedVariable ( node : VariableDeclaration ) {
356
+ const converted = setSourceMapRange (
357
+ createAssignment (
358
+ convertToAssignmentElementTarget ( node . name ) ,
359
+ node . initializer
360
+ ) ,
361
+ node
362
+ ) ;
363
+ return visitNode ( converted , visitor , isExpression ) ;
364
+ }
365
+
366
+ function collidesWithParameterName ( { name } : VariableDeclaration | BindingElement ) : boolean {
367
+ if ( isIdentifier ( name ) ) {
368
+ return enclosingFunctionParameterNames . has ( name . escapedText ) ;
369
+ }
370
+ else {
371
+ for ( const element of name . elements ) {
372
+ if ( ! isOmittedExpression ( element ) && collidesWithParameterName ( element ) ) {
373
+ return true ;
374
+ }
375
+ }
376
+ }
377
+ return false ;
378
+ }
379
+
200
380
function transformAsyncFunctionBody ( node : MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression ) : FunctionBody ;
201
381
function transformAsyncFunctionBody ( node : ArrowFunction ) : ConciseBody ;
202
382
function transformAsyncFunctionBody ( node : FunctionLikeDeclaration ) : ConciseBody {
@@ -214,6 +394,13 @@ namespace ts {
214
394
// passed to `__awaiter` is executed inside of the callback to the
215
395
// promise constructor.
216
396
397
+ const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames ;
398
+ enclosingFunctionParameterNames = createUnderscoreEscapedMap < true > ( ) ;
399
+ for ( const parameter of node . parameters ) {
400
+ recordDeclarationName ( parameter , enclosingFunctionParameterNames ) ;
401
+ }
402
+
403
+ let result : ConciseBody ;
217
404
if ( ! isArrowFunction ) {
218
405
const statements : Statement [ ] = [ ] ;
219
406
const statementOffset = addPrologue ( statements , ( < Block > node . body ) . statements , /*ensureUseStrict*/ false , visitor ) ;
@@ -223,7 +410,7 @@ namespace ts {
223
410
context ,
224
411
hasLexicalArguments ,
225
412
promiseConstructor ,
226
- transformFunctionBodyWorker ( < Block > node . body , statementOffset )
413
+ transformAsyncFunctionBodyWorker ( < Block > node . body , statementOffset )
227
414
)
228
415
)
229
416
) ;
@@ -246,35 +433,36 @@ namespace ts {
246
433
}
247
434
}
248
435
249
- return block ;
436
+ result = block ;
250
437
}
251
438
else {
252
439
const expression = createAwaiterHelper (
253
440
context ,
254
441
hasLexicalArguments ,
255
442
promiseConstructor ,
256
- transformFunctionBodyWorker ( node . body )
443
+ transformAsyncFunctionBodyWorker ( node . body )
257
444
) ;
258
445
259
446
const declarations = endLexicalEnvironment ( ) ;
260
447
if ( some ( declarations ) ) {
261
448
const block = convertToFunctionBody ( expression ) ;
262
- return updateBlock ( block , setTextRange ( createNodeArray ( concatenate ( block . statements , declarations ) ) , block . statements ) ) ;
449
+ result = updateBlock ( block , setTextRange ( createNodeArray ( concatenate ( block . statements , declarations ) ) , block . statements ) ) ;
450
+ }
451
+ else {
452
+ result = expression ;
263
453
}
264
-
265
- return expression ;
266
454
}
455
+
456
+ enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames ;
457
+ return result ;
267
458
}
268
459
269
- function transformFunctionBodyWorker ( body : ConciseBody , start ?: number ) {
460
+ function transformAsyncFunctionBodyWorker ( body : ConciseBody , start ?: number ) {
270
461
if ( isBlock ( body ) ) {
271
- return updateBlock ( body , visitLexicalEnvironment ( body . statements , visitor , context , start ) ) ;
462
+ return updateBlock ( body , visitNodes ( body . statements , asyncBodyVisitor , isStatement , start ) ) ;
272
463
}
273
464
else {
274
- startLexicalEnvironment ( ) ;
275
- const visited = convertToFunctionBody ( visitNode ( body , visitor , isConciseBody ) ) ;
276
- const declarations = endLexicalEnvironment ( ) ;
277
- return updateBlock ( visited , setTextRange ( createNodeArray ( concatenate ( visited . statements , declarations ) ) , visited . statements ) ) ;
465
+ return convertToFunctionBody ( visitNode ( body , asyncBodyVisitor , isConciseBody ) ) ;
278
466
}
279
467
}
280
468
0 commit comments