@@ -24,6 +24,13 @@ namespace ts {
24
24
}
25
25
}
26
26
27
+ const enum TransformationState {
28
+ Uninitialized ,
29
+ Initialized ,
30
+ Completed ,
31
+ Disposed
32
+ }
33
+
27
34
const enum SyntaxKindFeatureFlags {
28
35
Substitution = 1 << 0 ,
29
36
EmitNotifications = 1 << 1 ,
@@ -83,14 +90,16 @@ namespace ts {
83
90
*/
84
91
export function transformFiles ( resolver : EmitResolver , host : EmitHost , sourceFiles : SourceFile [ ] , transformers : Transformer [ ] ) : TransformationResult {
85
92
const enabledSyntaxKindFeatures = new Array < SyntaxKindFeatureFlags > ( SyntaxKind . Count ) ;
86
- let lexicalEnvironmentDisabled = false ;
87
93
let lexicalEnvironmentVariableDeclarations : VariableDeclaration [ ] ;
88
94
let lexicalEnvironmentFunctionDeclarations : FunctionDeclaration [ ] ;
89
95
let lexicalEnvironmentVariableDeclarationsStack : VariableDeclaration [ ] [ ] = [ ] ;
90
96
let lexicalEnvironmentFunctionDeclarationsStack : FunctionDeclaration [ ] [ ] = [ ] ;
91
97
let lexicalEnvironmentStackOffset = 0 ;
92
98
let lexicalEnvironmentSuspended = false ;
93
99
let emitHelpers : EmitHelper [ ] ;
100
+ let onSubstituteNode : TransformationContext [ "onSubstituteNode" ] = ( _ , node ) => node ;
101
+ let onEmitNode : TransformationContext [ "onEmitNode" ] = ( hint , node , callback ) => callback ( hint , node ) ;
102
+ let state = TransformationState . Uninitialized ;
94
103
95
104
// The transformation context is provided to each transformer as part of transformer
96
105
// initialization.
@@ -106,27 +115,40 @@ namespace ts {
106
115
hoistFunctionDeclaration,
107
116
requestEmitHelper,
108
117
readEmitHelpers,
109
- onSubstituteNode : ( _ , node ) => node ,
110
118
enableSubstitution,
111
- isSubstitutionEnabled,
112
- onEmitNode : ( hint , node , callback ) => callback ( hint , node ) ,
113
119
enableEmitNotification,
114
- isEmitNotificationEnabled
120
+ isSubstitutionEnabled,
121
+ isEmitNotificationEnabled,
122
+ get onSubstituteNode ( ) { return onSubstituteNode } ,
123
+ set onSubstituteNode ( value ) {
124
+ Debug . assert ( state < TransformationState . Initialized , "Cannot modify transformation hooks after initialization has completed." ) ;
125
+ Debug . assert ( value !== undefined , "Value must not be 'undefined'" ) ;
126
+ onSubstituteNode = value ;
127
+ } ,
128
+ get onEmitNode ( ) { return onEmitNode } ,
129
+ set onEmitNode ( value ) {
130
+ Debug . assert ( state < TransformationState . Initialized , "Cannot modify transformation hooks after initialization has completed." ) ;
131
+ Debug . assert ( value !== undefined , "Value must not be 'undefined'" ) ;
132
+ onEmitNode = value ;
133
+ }
115
134
} ;
116
135
117
136
// Ensure the parse tree is clean before applying transformations
118
- dispose ( ) ;
137
+ forEach ( sourceFiles , disposeEmitNodes ) ;
119
138
120
139
performance . mark ( "beforeTransform" ) ;
121
140
122
141
// Chain together and initialize each transformer.
123
142
const transformation = chain ( ...transformers ) ( context ) ;
124
143
144
+ // prevent modification of transformation hooks.
145
+ state = TransformationState . Initialized ;
146
+
125
147
// Transform each source file.
126
148
const transformed = map ( sourceFiles , transformSourceFile ) ;
127
149
128
- // Disable modification of the lexical environment.
129
- lexicalEnvironmentDisabled = true ;
150
+ // prevent modification of the lexical environment.
151
+ state = TransformationState . Completed ;
130
152
131
153
performance . mark ( "afterTransform" ) ;
132
154
performance . measure ( "transformTime" , "beforeTransform" , "afterTransform" ) ;
@@ -155,6 +177,7 @@ namespace ts {
155
177
* Enables expression substitutions in the pretty printer for the provided SyntaxKind.
156
178
*/
157
179
function enableSubstitution ( kind : SyntaxKind ) {
180
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the transformation context after transformation has completed." ) ;
158
181
enabledSyntaxKindFeatures [ kind ] |= SyntaxKindFeatureFlags . Substitution ;
159
182
}
160
183
@@ -174,9 +197,10 @@ namespace ts {
174
197
* @param emitCallback The callback used to emit the node or its substitute.
175
198
*/
176
199
function emitNodeWithSubstitution ( hint : EmitHint , node : Node , emitCallback : ( hint : EmitHint , node : Node ) => void ) {
200
+ Debug . assert ( state < TransformationState . Disposed , "Cannot invoke TransformationResult callbacks after the result is disposed." ) ;
177
201
if ( node ) {
178
202
if ( isSubstitutionEnabled ( node ) ) {
179
- node = context . onSubstituteNode ( hint , node ) || node ;
203
+ node = onSubstituteNode ( hint , node ) || node ;
180
204
}
181
205
emitCallback ( hint , node ) ;
182
206
}
@@ -186,6 +210,7 @@ namespace ts {
186
210
* Enables before/after emit notifications in the pretty printer for the provided SyntaxKind.
187
211
*/
188
212
function enableEmitNotification ( kind : SyntaxKind ) {
213
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the transformation context after transformation has completed." ) ;
189
214
enabledSyntaxKindFeatures [ kind ] |= SyntaxKindFeatureFlags . EmitNotifications ;
190
215
}
191
216
@@ -206,9 +231,10 @@ namespace ts {
206
231
* @param emitCallback The callback used to emit the node.
207
232
*/
208
233
function emitNodeWithNotification ( hint : EmitHint , node : Node , emitCallback : ( hint : EmitHint , node : Node ) => void ) {
234
+ Debug . assert ( state < TransformationState . Disposed , "Cannot invoke TransformationResult callbacks after the result is disposed." ) ;
209
235
if ( node ) {
210
236
if ( isEmitNotificationEnabled ( node ) ) {
211
- context . onEmitNode ( hint , node , emitCallback ) ;
237
+ onEmitNode ( hint , node , emitCallback ) ;
212
238
}
213
239
else {
214
240
emitCallback ( hint , node ) ;
@@ -220,7 +246,8 @@ namespace ts {
220
246
* Records a hoisted variable declaration for the provided name within a lexical environment.
221
247
*/
222
248
function hoistVariableDeclaration ( name : Identifier ) : void {
223
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot modify the lexical environment during the print phase." ) ;
249
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the lexical environment during initialization." ) ;
250
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the lexical environment after transformation has completed." ) ;
224
251
const decl = createVariableDeclaration ( name ) ;
225
252
if ( ! lexicalEnvironmentVariableDeclarations ) {
226
253
lexicalEnvironmentVariableDeclarations = [ decl ] ;
@@ -234,7 +261,8 @@ namespace ts {
234
261
* Records a hoisted function declaration within a lexical environment.
235
262
*/
236
263
function hoistFunctionDeclaration ( func : FunctionDeclaration ) : void {
237
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot modify the lexical environment during the print phase." ) ;
264
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the lexical environment during initialization." ) ;
265
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the lexical environment after transformation has completed." ) ;
238
266
if ( ! lexicalEnvironmentFunctionDeclarations ) {
239
267
lexicalEnvironmentFunctionDeclarations = [ func ] ;
240
268
}
@@ -248,7 +276,8 @@ namespace ts {
248
276
* are pushed onto a stack, and the related storage variables are reset.
249
277
*/
250
278
function startLexicalEnvironment ( ) : void {
251
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot start a lexical environment during the print phase." ) ;
279
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the lexical environment during initialization." ) ;
280
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the lexical environment after transformation has completed." ) ;
252
281
Debug . assert ( ! lexicalEnvironmentSuspended , "Lexical environment is suspended." ) ;
253
282
254
283
// Save the current lexical environment. Rather than resizing the array we adjust the
@@ -264,14 +293,16 @@ namespace ts {
264
293
265
294
/** Suspends the current lexical environment, usually after visiting a parameter list. */
266
295
function suspendLexicalEnvironment ( ) : void {
267
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot suspend a lexical environment during the print phase." ) ;
296
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the lexical environment during initialization." ) ;
297
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the lexical environment after transformation has completed." ) ;
268
298
Debug . assert ( ! lexicalEnvironmentSuspended , "Lexical environment is already suspended." ) ;
269
299
lexicalEnvironmentSuspended = true ;
270
300
}
271
301
272
302
/** Resumes a suspended lexical environment, usually before visiting a function body. */
273
303
function resumeLexicalEnvironment ( ) : void {
274
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot resume a lexical environment during the print phase." ) ;
304
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the lexical environment during initialization." ) ;
305
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the lexical environment after transformation has completed." ) ;
275
306
Debug . assert ( lexicalEnvironmentSuspended , "Lexical environment is not suspended." ) ;
276
307
lexicalEnvironmentSuspended = false ;
277
308
}
@@ -281,7 +312,8 @@ namespace ts {
281
312
* any hoisted declarations added in this environment are returned.
282
313
*/
283
314
function endLexicalEnvironment ( ) : Statement [ ] {
284
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot end a lexical environment during the print phase." ) ;
315
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the lexical environment during initialization." ) ;
316
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the lexical environment after transformation has completed." ) ;
285
317
Debug . assert ( ! lexicalEnvironmentSuspended , "Lexical environment is suspended." ) ;
286
318
287
319
let statements : Statement [ ] ;
@@ -317,22 +349,36 @@ namespace ts {
317
349
}
318
350
319
351
function requestEmitHelper ( helper : EmitHelper ) : void {
320
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot modify the lexical environment during the print phase." ) ;
352
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the transformation context during initialization." ) ;
353
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the transformation context after transformation has completed." ) ;
321
354
Debug . assert ( ! helper . scoped , "Cannot request a scoped emit helper." ) ;
322
355
emitHelpers = append ( emitHelpers , helper ) ;
323
356
}
324
357
325
358
function readEmitHelpers ( ) : EmitHelper [ ] | undefined {
326
- Debug . assert ( ! lexicalEnvironmentDisabled , "Cannot modify the lexical environment during the print phase." ) ;
359
+ Debug . assert ( state > TransformationState . Uninitialized , "Cannot modify the transformation context during initialization." ) ;
360
+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the transformation context after transformation has completed." ) ;
327
361
const helpers = emitHelpers ;
328
362
emitHelpers = undefined ;
329
363
return helpers ;
330
364
}
331
365
332
366
function dispose ( ) {
333
- // Clean up emit nodes on parse tree
334
- for ( const sourceFile of sourceFiles ) {
335
- disposeEmitNodes ( sourceFile ) ;
367
+ if ( state < TransformationState . Disposed ) {
368
+ // Clean up emit nodes on parse tree
369
+ forEach ( sourceFiles , disposeEmitNodes ) ;
370
+
371
+ // Release references to external entries for GC purposes.
372
+ lexicalEnvironmentVariableDeclarations = undefined ;
373
+ lexicalEnvironmentVariableDeclarationsStack = undefined ;
374
+ lexicalEnvironmentFunctionDeclarations = undefined ;
375
+ lexicalEnvironmentFunctionDeclarationsStack = undefined ;
376
+ onSubstituteNode = undefined ;
377
+ onEmitNode = undefined ;
378
+ emitHelpers = undefined ;
379
+
380
+ // Prevent further use of the transformation result.
381
+ state = TransformationState . Disposed ;
336
382
}
337
383
}
338
384
}
0 commit comments