@@ -24,6 +24,13 @@ namespace ts {
2424 }
2525 }
2626
27+ const enum TransformationState {
28+ Uninitialized ,
29+ Initialized ,
30+ Completed ,
31+ Disposed
32+ }
33+
2734 const enum SyntaxKindFeatureFlags {
2835 Substitution = 1 << 0 ,
2936 EmitNotifications = 1 << 1 ,
@@ -83,14 +90,16 @@ namespace ts {
8390 */
8491 export function transformFiles ( resolver : EmitResolver , host : EmitHost , sourceFiles : SourceFile [ ] , transformers : Transformer [ ] ) : TransformationResult {
8592 const enabledSyntaxKindFeatures = new Array < SyntaxKindFeatureFlags > ( SyntaxKind . Count ) ;
86- let lexicalEnvironmentDisabled = false ;
8793 let lexicalEnvironmentVariableDeclarations : VariableDeclaration [ ] ;
8894 let lexicalEnvironmentFunctionDeclarations : FunctionDeclaration [ ] ;
8995 let lexicalEnvironmentVariableDeclarationsStack : VariableDeclaration [ ] [ ] = [ ] ;
9096 let lexicalEnvironmentFunctionDeclarationsStack : FunctionDeclaration [ ] [ ] = [ ] ;
9197 let lexicalEnvironmentStackOffset = 0 ;
9298 let lexicalEnvironmentSuspended = false ;
9399 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 ;
94103
95104 // The transformation context is provided to each transformer as part of transformer
96105 // initialization.
@@ -106,27 +115,40 @@ namespace ts {
106115 hoistFunctionDeclaration,
107116 requestEmitHelper,
108117 readEmitHelpers,
109- onSubstituteNode : ( _ , node ) => node ,
110118 enableSubstitution,
111- isSubstitutionEnabled,
112- onEmitNode : ( hint , node , callback ) => callback ( hint , node ) ,
113119 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+ }
115134 } ;
116135
117136 // Ensure the parse tree is clean before applying transformations
118- dispose ( ) ;
137+ forEach ( sourceFiles , disposeEmitNodes ) ;
119138
120139 performance . mark ( "beforeTransform" ) ;
121140
122141 // Chain together and initialize each transformer.
123142 const transformation = chain ( ...transformers ) ( context ) ;
124143
144+ // prevent modification of transformation hooks.
145+ state = TransformationState . Initialized ;
146+
125147 // Transform each source file.
126148 const transformed = map ( sourceFiles , transformSourceFile ) ;
127149
128- // Disable modification of the lexical environment.
129- lexicalEnvironmentDisabled = true ;
150+ // prevent modification of the lexical environment.
151+ state = TransformationState . Completed ;
130152
131153 performance . mark ( "afterTransform" ) ;
132154 performance . measure ( "transformTime" , "beforeTransform" , "afterTransform" ) ;
@@ -155,6 +177,7 @@ namespace ts {
155177 * Enables expression substitutions in the pretty printer for the provided SyntaxKind.
156178 */
157179 function enableSubstitution ( kind : SyntaxKind ) {
180+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the transformation context after transformation has completed." ) ;
158181 enabledSyntaxKindFeatures [ kind ] |= SyntaxKindFeatureFlags . Substitution ;
159182 }
160183
@@ -174,9 +197,10 @@ namespace ts {
174197 * @param emitCallback The callback used to emit the node or its substitute.
175198 */
176199 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." ) ;
177201 if ( node ) {
178202 if ( isSubstitutionEnabled ( node ) ) {
179- node = context . onSubstituteNode ( hint , node ) || node ;
203+ node = onSubstituteNode ( hint , node ) || node ;
180204 }
181205 emitCallback ( hint , node ) ;
182206 }
@@ -186,6 +210,7 @@ namespace ts {
186210 * Enables before/after emit notifications in the pretty printer for the provided SyntaxKind.
187211 */
188212 function enableEmitNotification ( kind : SyntaxKind ) {
213+ Debug . assert ( state < TransformationState . Completed , "Cannot modify the transformation context after transformation has completed." ) ;
189214 enabledSyntaxKindFeatures [ kind ] |= SyntaxKindFeatureFlags . EmitNotifications ;
190215 }
191216
@@ -206,9 +231,10 @@ namespace ts {
206231 * @param emitCallback The callback used to emit the node.
207232 */
208233 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." ) ;
209235 if ( node ) {
210236 if ( isEmitNotificationEnabled ( node ) ) {
211- context . onEmitNode ( hint , node , emitCallback ) ;
237+ onEmitNode ( hint , node , emitCallback ) ;
212238 }
213239 else {
214240 emitCallback ( hint , node ) ;
@@ -220,7 +246,8 @@ namespace ts {
220246 * Records a hoisted variable declaration for the provided name within a lexical environment.
221247 */
222248 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." ) ;
224251 const decl = createVariableDeclaration ( name ) ;
225252 if ( ! lexicalEnvironmentVariableDeclarations ) {
226253 lexicalEnvironmentVariableDeclarations = [ decl ] ;
@@ -234,7 +261,8 @@ namespace ts {
234261 * Records a hoisted function declaration within a lexical environment.
235262 */
236263 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." ) ;
238266 if ( ! lexicalEnvironmentFunctionDeclarations ) {
239267 lexicalEnvironmentFunctionDeclarations = [ func ] ;
240268 }
@@ -248,7 +276,8 @@ namespace ts {
248276 * are pushed onto a stack, and the related storage variables are reset.
249277 */
250278 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." ) ;
252281 Debug . assert ( ! lexicalEnvironmentSuspended , "Lexical environment is suspended." ) ;
253282
254283 // Save the current lexical environment. Rather than resizing the array we adjust the
@@ -264,14 +293,16 @@ namespace ts {
264293
265294 /** Suspends the current lexical environment, usually after visiting a parameter list. */
266295 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." ) ;
268298 Debug . assert ( ! lexicalEnvironmentSuspended , "Lexical environment is already suspended." ) ;
269299 lexicalEnvironmentSuspended = true ;
270300 }
271301
272302 /** Resumes a suspended lexical environment, usually before visiting a function body. */
273303 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." ) ;
275306 Debug . assert ( lexicalEnvironmentSuspended , "Lexical environment is not suspended." ) ;
276307 lexicalEnvironmentSuspended = false ;
277308 }
@@ -281,7 +312,8 @@ namespace ts {
281312 * any hoisted declarations added in this environment are returned.
282313 */
283314 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." ) ;
285317 Debug . assert ( ! lexicalEnvironmentSuspended , "Lexical environment is suspended." ) ;
286318
287319 let statements : Statement [ ] ;
@@ -317,22 +349,36 @@ namespace ts {
317349 }
318350
319351 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." ) ;
321354 Debug . assert ( ! helper . scoped , "Cannot request a scoped emit helper." ) ;
322355 emitHelpers = append ( emitHelpers , helper ) ;
323356 }
324357
325358 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." ) ;
327361 const helpers = emitHelpers ;
328362 emitHelpers = undefined ;
329363 return helpers ;
330364 }
331365
332366 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 ;
336382 }
337383 }
338384 }
0 commit comments