@@ -173,128 +173,128 @@ impl Compiler {
173173 /// Result containing the output file path
174174 #[ tracing:: instrument( skip( self , input) ) ]
175175 pub fn compile_file_to (
176- & self ,
177- file_path : & str ,
178- input : String ,
179- output_path : & Path ,
180- use_define_for_class_fields : bool ,
176+ & self ,
177+ file_path : & str ,
178+ input : String ,
179+ output_path : & Path ,
180+ use_define_for_class_fields : bool ,
181181 ) -> anyhow:: Result < String > {
182- let compile_id = COMPILE_ID . fetch_add ( 1 , Ordering :: SeqCst ) ;
183- let begin = Instant :: now ( ) ;
184-
185- info ! (
186- "[Compile #{compile_id}] START compile_file_to: {} -> {}" ,
187- file_path,
188- output_path. display( )
189- ) ;
190- trace ! (
191- "[Compile #{compile_id}] Input size: {} bytes, use_define_for_class_fields={}" ,
192- input. len( ) ,
193- use_define_for_class_fields
194- ) ;
195-
196- // CRITICAL FIX: Wrap entire compilation in its own scope to ensure
197- // the allocator is dropped before the next file is processed.
198- // This prevents OXC internal state corruption when processing
199- // multiple files sequentially.
200- let codegen_result = {
201- // Parse the TypeScript source
202- info ! ( "[Compile #{compile_id}] Step 1/4: Parsing {}" , file_path) ;
203- let parse_start = Instant :: now ( ) ;
204- let parser_config = self . get_parser_config ( ) ;
205- let mut parse_result = Parser :: parse ( & input, file_path, & parser_config)
206- . map_err ( |errors| anyhow:: anyhow!( "Parse errors: {:?}" , errors) ) ?;
207- info ! (
208- "[Compile #{compile_id}] Step 1/4 complete: Parse in {:?}" ,
209- parse_start. elapsed( )
210- ) ;
211-
212- debug ! (
213- "[Compile #{compile_id}] Memory addresses: allocator={:p}, program={:p}" ,
214- & parse_result. allocator, & parse_result. program
215- ) ;
216- trace ! (
217- "[Compile #{compile_id}] AST body slice: ptr={:p}, len={}" ,
218- parse_result. program. body. as_ptr( ) ,
219- parse_result. program. body. len( )
220- ) ;
221-
222- // Transform the AST - borrow program mutably, don't move it out
223- info ! ( "[Compile #{compile_id}] Step 2/4: Transforming" ) ;
224- let transform_start = Instant :: now ( ) ;
225- let mut transformer_config = self . get_transformer_config ( ) ;
226- transformer_config. use_define_for_class_fields = use_define_for_class_fields;
227-
228- // SAFETY: We borrow program with 'static lifetime from parse_result.
229- // This is safe because:
230- // 1. parse_result stays in scope for the entire inner block
231- // 2. The program and allocator are dropped together when parse_result is
232- // dropped
233- // 3. We never use program after parse_result is dropped
234- let program = unsafe {
235- std:: mem:: transmute :: < & mut oxc_ast:: ast:: Program < ' static > , & mut oxc_ast:: ast:: Program < ' _ > > (
236- & mut parse_result. program ,
237- )
238- } ;
239-
240- let source_type = oxc_span:: SourceType :: from_path ( file_path) . unwrap_or ( oxc_span:: SourceType :: ts ( ) ) ;
241-
242- Transformer :: transform ( & parse_result. allocator , program, file_path, source_type, & transformer_config)
243- . map_err ( |errors| anyhow:: anyhow!( "Transform errors: {:?}" , errors) ) ?;
244- info ! (
245- "[Compile #{compile_id}] Step 2/4 complete: Transform in {:?}" ,
246- transform_start. elapsed( )
247- ) ;
248- trace ! (
249- "[Compile #{compile_id}] Program after transform - body ptr: {:p}" ,
250- program. body. as_ptr( )
251- ) ;
252-
253- // Generate code - parse_result still alive
254- info ! ( "[Compile #{compile_id}] Step 3/4: Codegen" ) ;
255- let codegen_start = Instant :: now ( ) ;
256- let codegen_config = self . get_codegen_config ( ) ;
257- let codegen_result = Codegen :: codegen ( & parse_result. allocator , program, source_type, & codegen_config)
258- . map_err ( |e| anyhow:: anyhow!( "Codegen error: {}" , e) ) ?;
259- info ! (
260- "[Compile #{compile_id}] Step 3/4 complete: Codegen in {:?}, output={}(bytes)" ,
261- codegen_start. elapsed( ) ,
262- codegen_result. code. len( )
263- ) ;
264-
265- // Force drop of parse_result to ensure allocator is freed
266- // before returning from this scope
267- let _ = program;
268- // parse_result will be dropped at end of scope
269- codegen_result
270- } ; // parse_result and allocator dropped here - critical for preventing segfault
271-
272- // Create parent directories if they don't exist
273- if let Some ( parent) = output_path. parent ( ) {
274- std:: fs:: create_dir_all ( parent) ?;
275- }
276-
277- // Write to the specified output path
278- let write_start = Instant :: now ( ) ;
279- std:: fs:: write ( output_path, & codegen_result. code ) ?;
280- trace ! ( "[Compile #{compile_id}] Step 4/4: File write in {:?}" , write_start. elapsed( ) ) ;
281-
282- let elapsed = begin. elapsed ( ) ;
283-
284- {
285- let mut outlook = self . outlook . lock ( ) . unwrap ( ) ;
286- outlook. count += 1 ;
287- outlook. elapsed += elapsed;
288- }
289-
290- info ! (
291- "[Compile #{compile_id}] COMPLETE: {} -> {} in {:?}" ,
292- file_path,
293- output_path. display( ) ,
294- elapsed
295- ) ;
296-
297- Ok ( output_path. to_string_lossy ( ) . to_string ( ) )
182+ let compile_id = COMPILE_ID . fetch_add ( 1 , Ordering :: SeqCst ) ;
183+ let begin = Instant :: now ( ) ;
184+
185+ info ! (
186+ "[Compile #{compile_id}] START compile_file_to: {} -> {}" ,
187+ file_path,
188+ output_path. display( )
189+ ) ;
190+ trace ! (
191+ "[Compile #{compile_id}] Input size: {} bytes, use_define_for_class_fields={}" ,
192+ input. len( ) ,
193+ use_define_for_class_fields
194+ ) ;
195+
196+ // CRITICAL FIX: Wrap entire compilation in its own scope to ensure
197+ // the allocator is dropped before the next file is processed.
198+ // This prevents OXC internal state corruption when processing
199+ // multiple files sequentially.
200+ let codegen_result = {
201+ // Parse the TypeScript source
202+ info ! ( "[Compile #{compile_id}] Step 1/4: Parsing {}" , file_path) ;
203+ let parse_start = Instant :: now ( ) ;
204+ let parser_config = self . get_parser_config ( ) ;
205+ let mut parse_result = Parser :: parse ( & input, file_path, & parser_config)
206+ . map_err ( |errors| anyhow:: anyhow!( "Parse errors: {:?}" , errors) ) ?;
207+ info ! (
208+ "[Compile #{compile_id}] Step 1/4 complete: Parse in {:?}" ,
209+ parse_start. elapsed( )
210+ ) ;
211+
212+ debug ! (
213+ "[Compile #{compile_id}] Memory addresses: allocator={:p}, program={:p}" ,
214+ & parse_result. allocator, & parse_result. program
215+ ) ;
216+ trace ! (
217+ "[Compile #{compile_id}] AST body slice: ptr={:p}, len={}" ,
218+ parse_result. program. body. as_ptr( ) ,
219+ parse_result. program. body. len( )
220+ ) ;
221+
222+ // Transform the AST - borrow program mutably, don't move it out
223+ info ! ( "[Compile #{compile_id}] Step 2/4: Transforming" ) ;
224+ let transform_start = Instant :: now ( ) ;
225+ let mut transformer_config = self . get_transformer_config ( ) ;
226+ transformer_config. use_define_for_class_fields = use_define_for_class_fields;
227+
228+ // SAFETY: We borrow program with 'static lifetime from parse_result.
229+ // This is safe because:
230+ // 1. parse_result stays in scope for the entire inner block
231+ // 2. The program and allocator are dropped together when parse_result is
232+ // dropped
233+ // 3. We never use program after parse_result is dropped
234+ let program = unsafe {
235+ std:: mem:: transmute :: < & mut oxc_ast:: ast:: Program < ' static > , & mut oxc_ast:: ast:: Program < ' _ > > (
236+ & mut parse_result. program ,
237+ )
238+ } ;
239+
240+ let source_type = oxc_span:: SourceType :: from_path ( file_path) . unwrap_or ( oxc_span:: SourceType :: ts ( ) ) ;
241+
242+ Transformer :: transform ( & parse_result. allocator , program, file_path, source_type, & transformer_config)
243+ . map_err ( |errors| anyhow:: anyhow!( "Transform errors: {:?}" , errors) ) ?;
244+ info ! (
245+ "[Compile #{compile_id}] Step 2/4 complete: Transform in {:?}" ,
246+ transform_start. elapsed( )
247+ ) ;
248+ trace ! (
249+ "[Compile #{compile_id}] Program after transform - body ptr: {:p}" ,
250+ program. body. as_ptr( )
251+ ) ;
252+
253+ // Generate code - parse_result still alive
254+ info ! ( "[Compile #{compile_id}] Step 3/4: Codegen" ) ;
255+ let codegen_start = Instant :: now ( ) ;
256+ let codegen_config = self . get_codegen_config ( ) ;
257+ let codegen_result = Codegen :: codegen ( & parse_result. allocator , program, source_type, & codegen_config)
258+ . map_err ( |e| anyhow:: anyhow!( "Codegen error: {}" , e) ) ?;
259+ info ! (
260+ "[Compile #{compile_id}] Step 3/4 complete: Codegen in {:?}, output={}(bytes)" ,
261+ codegen_start. elapsed( ) ,
262+ codegen_result. code. len( )
263+ ) ;
264+
265+ // Force drop of parse_result to ensure allocator is freed
266+ // before returning from this scope
267+ let _ = program;
268+ // parse_result will be dropped at end of scope
269+ codegen_result
270+ } ; // parse_result and allocator dropped here - critical for preventing segfault
271+
272+ // Create parent directories if they don't exist
273+ if let Some ( parent) = output_path. parent ( ) {
274+ std:: fs:: create_dir_all ( parent) ?;
275+ }
276+
277+ // Write to the specified output path
278+ let write_start = Instant :: now ( ) ;
279+ std:: fs:: write ( output_path, & codegen_result. code ) ?;
280+ trace ! ( "[Compile #{compile_id}] Step 4/4: File write in {:?}" , write_start. elapsed( ) ) ;
281+
282+ let elapsed = begin. elapsed ( ) ;
283+
284+ {
285+ let mut outlook = self . outlook . lock ( ) . unwrap ( ) ;
286+ outlook. count += 1 ;
287+ outlook. elapsed += elapsed;
288+ }
289+
290+ info ! (
291+ "[Compile #{compile_id}] COMPLETE: {} -> {} in {:?}" ,
292+ file_path,
293+ output_path. display( ) ,
294+ elapsed
295+ ) ;
296+
297+ Ok ( output_path. to_string_lossy ( ) . to_string ( ) )
298298 }
299299
300300 /// Get parser configuration from compiler config
0 commit comments