1// Copyright (c) Meta Platforms, Inc. and affiliates.2//3// This source code is licensed under the MIT license found in the4// LICENSE file in the root directory of this source tree.56//! Compilation pipeline for a single function.7//!8//! Analogous to TS `Pipeline.ts` (`compileFn` → `run` → `runWithEnvironment`).9//! Currently runs BuildHIR (lowering) and PruneMaybeThrows.1011use indexmap::IndexMap;12use react_compiler_ast::scope::ScopeInfo;13use react_compiler_diagnostics::CompilerError;14use react_compiler_hir::ReactFunctionType;15use react_compiler_hir::environment::Environment;16use react_compiler_hir::environment::OutputMode;17use react_compiler_hir::environment_config::EnvironmentConfig;18use react_compiler_lowering::FunctionNode;19use rustc_hash::{FxBuildHasher, FxHashMap};2021use super::compile_result::CodegenFunction;22use super::compile_result::CompilerErrorDetailInfo;23use super::compile_result::CompilerErrorItemInfo;24use super::compile_result::DebugLogEntry;25use super::compile_result::LoggerPosition;26use super::compile_result::LoggerSourceLocation;27use super::compile_result::OutlinedFunction;28use super::imports::ProgramContext;29use super::plugin_options::CompilerOutputMode;30use crate::debug_print;3132/// Run the compilation pipeline on a single function.33///34/// Currently: creates an Environment, runs BuildHIR (lowering), and produces35/// debug output via the context. Returns a CodegenFunction with zeroed memo36/// stats on success (codegen is not yet implemented).37pub fn compile_fn(38 func: &FunctionNode<'_>,39 fn_name: Option<&str>,40 scope_info: &ScopeInfo,41 fn_type: ReactFunctionType,42 mode: CompilerOutputMode,43 env_config: &EnvironmentConfig,44 context: &mut ProgramContext,45) -> Result<CodegenFunction, CompilerError> {46 let mut env = Environment::with_config(env_config.clone());47 env.fn_type = fn_type;48 env.output_mode = match mode {49 CompilerOutputMode::Ssr => OutputMode::Ssr,50 CompilerOutputMode::Client => OutputMode::Client,51 CompilerOutputMode::Lint => OutputMode::Lint,52 };53 env.code = context.code.clone();54 env.filename = context.filename.clone();55 env.instrument_fn_name = context.instrument_fn_name.clone();56 env.instrument_gating_name = context.instrument_gating_name.clone();57 env.hook_guard_name = context.hook_guard_name.clone();58 env.seed_uid_known_names(&context.known_referenced_names());5960 env.reference_node_ids = scope_info.ref_node_id_to_binding.keys().copied().collect();6162 context.timing.start("lower");63 let mut hir = react_compiler_lowering::lower(func, fn_name, scope_info, &mut env)?;64 context.timing.stop();6566 // Copy renames from lowering to context (keep on env for codegen to apply to type annotations)67 if !env.renames.is_empty() {68 context.renames.extend(env.renames.iter().cloned());69 }7071 // Check for Invariant errors after lowering, before logging HIR.72 // In TS, Invariant errors throw from recordError(), aborting lower() before73 // the HIR entry is logged. The thrown error contains ONLY the Invariant error,74 // not other recorded (non-Invariant) errors.75 if env.has_invariant_errors() {76 return Err(env.take_invariant_errors());77 }7879 if context.debug_enabled {80 context.timing.start("debug_print:HIR");81 let debug_hir = debug_print::debug_hir(&hir, &env);82 context.log_debug(DebugLogEntry::new("HIR", debug_hir));83 context.timing.stop();84 }8586 context.timing.start("PruneMaybeThrows");87 react_compiler_optimization::prune_maybe_throws(&mut hir, &mut env.functions)?;88 context.timing.stop();8990 if context.debug_enabled {91 context.timing.start("debug_print:PruneMaybeThrows");92 let debug_prune = debug_print::debug_hir(&hir, &env);93 context.log_debug(DebugLogEntry::new("PruneMaybeThrows", debug_prune));94 context.timing.stop();95 }9697 context.timing.start("ValidateContextVariableLValues");98 react_compiler_validation::validate_context_variable_lvalues(&hir, &mut env)?;99 if context.debug_enabled {100 context.log_debug(DebugLogEntry::new(101 "ValidateContextVariableLValues",102 "ok".to_string(),103 ));104 }105 context.timing.stop();106107 context.timing.start("ValidateUseMemo");108 let void_memo_errors = react_compiler_validation::validate_use_memo(&hir, &mut env);109 log_errors_as_events(&void_memo_errors, context);110 if context.debug_enabled {111 context.log_debug(DebugLogEntry::new("ValidateUseMemo", "ok".to_string()));112 }113 context.timing.stop();114115 context.timing.start("DropManualMemoization");116 react_compiler_optimization::drop_manual_memoization(&mut hir, &mut env)?;117 context.timing.stop();118119 if context.debug_enabled {120 context.timing.start("debug_print:DropManualMemoization");121 let debug_drop_memo = debug_print::debug_hir(&hir, &env);122 context.log_debug(DebugLogEntry::new("DropManualMemoization", debug_drop_memo));123 context.timing.stop();124 }125126 context127 .timing128 .start("InlineImmediatelyInvokedFunctionExpressions");129 react_compiler_optimization::inline_immediately_invoked_function_expressions(130 &mut hir, &mut env,131 );132 context.timing.stop();133134 if context.debug_enabled {135 context136 .timing137 .start("debug_print:InlineImmediatelyInvokedFunctionExpressions");138 let debug_inline_iifes = debug_print::debug_hir(&hir, &env);139 context.log_debug(DebugLogEntry::new(140 "InlineImmediatelyInvokedFunctionExpressions",141 debug_inline_iifes,142 ));143 context.timing.stop();144 }145146 context.timing.start("MergeConsecutiveBlocks");147 react_compiler_optimization::merge_consecutive_blocks::merge_consecutive_blocks(148 &mut hir,149 &mut env.functions,150 );151 context.timing.stop();152153 if context.debug_enabled {154 context.timing.start("debug_print:MergeConsecutiveBlocks");155 let debug_merge = debug_print::debug_hir(&hir, &env);156 context.log_debug(DebugLogEntry::new("MergeConsecutiveBlocks", debug_merge));157 context.timing.stop();158 }159160 // TODO: port assertConsistentIdentifiers161 if context.debug_enabled {162 context.log_debug(DebugLogEntry::new(163 "AssertConsistentIdentifiers",164 "ok".to_string(),165 ));166 }167 // TODO: port assertTerminalSuccessorsExist168 if context.debug_enabled {169 context.log_debug(DebugLogEntry::new(170 "AssertTerminalSuccessorsExist",171 "ok".to_string(),172 ));173 }174175 context.timing.start("EnterSSA");176 react_compiler_ssa::enter_ssa(&mut hir, &mut env).map_err(|diag| {177 let loc = diag.primary_location().cloned();178 let mut err = CompilerError::new();179 err.push_error_detail(react_compiler_diagnostics::CompilerErrorDetail {180 category: diag.category,181 reason: diag.reason,182 description: diag.description,183 loc,184 suggestions: diag.suggestions,185 });186 err187 })?;188 context.timing.stop();189190 if context.debug_enabled {191 context.timing.start("debug_print:SSA");192 let debug_ssa = debug_print::debug_hir(&hir, &env);193 context.log_debug(DebugLogEntry::new("SSA", debug_ssa));194 context.timing.stop();195 }196197 context.timing.start("EliminateRedundantPhi");198 react_compiler_ssa::eliminate_redundant_phi(&mut hir, &mut env);199 context.timing.stop();200201 if context.debug_enabled {202 context.timing.start("debug_print:EliminateRedundantPhi");203 let debug_eliminate_phi = debug_print::debug_hir(&hir, &env);204 context.log_debug(DebugLogEntry::new(205 "EliminateRedundantPhi",206 debug_eliminate_phi,207 ));208 context.timing.stop();209 }210211 // TODO: port assertConsistentIdentifiers212 if context.debug_enabled {213 context.log_debug(DebugLogEntry::new(214 "AssertConsistentIdentifiers",215 "ok".to_string(),216 ));217 }218219 context.timing.start("ConstantPropagation");220 react_compiler_optimization::constant_propagation(&mut hir, &mut env);221 context.timing.stop();222223 if context.debug_enabled {224 context.timing.start("debug_print:ConstantPropagation");225 let debug_const_prop = debug_print::debug_hir(&hir, &env);226 context.log_debug(DebugLogEntry::new("ConstantPropagation", debug_const_prop));227 context.timing.stop();228 }229230 context.timing.start("InferTypes");231 react_compiler_typeinference::infer_types(&mut hir, &mut env)?;232 context.timing.stop();233234 if context.debug_enabled {235 context.timing.start("debug_print:InferTypes");236 let debug_infer_types = debug_print::debug_hir(&hir, &env);237 context.log_debug(DebugLogEntry::new("InferTypes", debug_infer_types));238 context.timing.stop();239 }240241 if env.enable_validations() {242 if env.config.validate_hooks_usage {243 context.timing.start("ValidateHooksUsage");244 react_compiler_validation::validate_hooks_usage(&hir, &mut env)?;245 if context.debug_enabled {246 context.log_debug(DebugLogEntry::new("ValidateHooksUsage", "ok".to_string()));247 }248 context.timing.stop();249 }250251 if env.config.validate_no_capitalized_calls.is_some() {252 context.timing.start("ValidateNoCapitalizedCalls");253 react_compiler_validation::validate_no_capitalized_calls(&hir, &mut env)?;254 if context.debug_enabled {255 context.log_debug(DebugLogEntry::new(256 "ValidateNoCapitalizedCalls",257 "ok".to_string(),258 ));259 }260 context.timing.stop();261 }262 }263264 context.timing.start("OptimizePropsMethodCalls");265 react_compiler_optimization::optimize_props_method_calls(&mut hir, &env);266 context.timing.stop();267268 if context.debug_enabled {269 context.timing.start("debug_print:OptimizePropsMethodCalls");270 let debug_optimize_props = debug_print::debug_hir(&hir, &env);271 context.log_debug(DebugLogEntry::new(272 "OptimizePropsMethodCalls",273 debug_optimize_props,274 ));275 context.timing.stop();276 }277278 context.timing.start("AnalyseFunctions");279 let mut inner_logs: Vec<String> = Vec::new();280 let debug_inner = context.debug_enabled;281 let analyse_result = react_compiler_inference::analyse_functions(282 &mut hir,283 &mut env,284 &mut |inner_func, inner_env| {285 if debug_inner {286 inner_logs.push(debug_print::debug_hir(inner_func, inner_env));287 }288 },289 );290 context.timing.stop();291292 // Always flush inner logs before propagating errors293 if context.debug_enabled {294 for inner_log in inner_logs {295 context.log_debug(DebugLogEntry::new("AnalyseFunction (inner)", inner_log));296 }297 }298299 analyse_result?;300301 if env.has_invariant_errors() {302 return Err(env.take_invariant_errors());303 }304305 if context.debug_enabled {306 context.timing.start("debug_print:AnalyseFunctions");307 let debug_analyse_functions = debug_print::debug_hir(&hir, &env);308 context.log_debug(DebugLogEntry::new(309 "AnalyseFunctions",310 debug_analyse_functions,311 ));312 context.timing.stop();313 }314315 context.timing.start("InferMutationAliasingEffects");316 react_compiler_inference::infer_mutation_aliasing_effects(&mut hir, &mut env, false)?;317 context.timing.stop();318319 if context.debug_enabled {320 context321 .timing322 .start("debug_print:InferMutationAliasingEffects");323 let debug_infer_effects = debug_print::debug_hir(&hir, &env);324 context.log_debug(DebugLogEntry::new(325 "InferMutationAliasingEffects",326 debug_infer_effects,327 ));328 context.timing.stop();329 }330331 if env.output_mode == OutputMode::Ssr {332 context.timing.start("OptimizeForSSR");333 react_compiler_optimization::optimize_for_ssr(&mut hir, &env);334 context.timing.stop();335336 if context.debug_enabled {337 context.timing.start("debug_print:OptimizeForSSR");338 let debug_ssr = debug_print::debug_hir(&hir, &env);339 context.log_debug(DebugLogEntry::new("OptimizeForSSR", debug_ssr));340 context.timing.stop();341 }342 }343344 context.timing.start("DeadCodeElimination");345 react_compiler_optimization::dead_code_elimination(&mut hir, &env);346 context.timing.stop();347348 if context.debug_enabled {349 context.timing.start("debug_print:DeadCodeElimination");350 let debug_dce = debug_print::debug_hir(&hir, &env);351 context.log_debug(DebugLogEntry::new("DeadCodeElimination", debug_dce));352 context.timing.stop();353 }354355 context.timing.start("PruneMaybeThrows2");356 react_compiler_optimization::prune_maybe_throws(&mut hir, &mut env.functions)?;357 context.timing.stop();358359 if context.debug_enabled {360 context.timing.start("debug_print:PruneMaybeThrows2");361 let debug_prune2 = debug_print::debug_hir(&hir, &env);362 context.log_debug(DebugLogEntry::new("PruneMaybeThrows", debug_prune2));363 context.timing.stop();364 }365366 context.timing.start("InferMutationAliasingRanges");367 react_compiler_inference::infer_mutation_aliasing_ranges(&mut hir, &mut env, false)?;368 context.timing.stop();369370 if context.debug_enabled {371 context372 .timing373 .start("debug_print:InferMutationAliasingRanges");374 let debug_infer_ranges = debug_print::debug_hir(&hir, &env);375 context.log_debug(DebugLogEntry::new(376 "InferMutationAliasingRanges",377 debug_infer_ranges,378 ));379 context.timing.stop();380 }381382 if env.enable_validations() {383 context384 .timing385 .start("ValidateLocalsNotReassignedAfterRender");386 react_compiler_validation::validate_locals_not_reassigned_after_render(&hir, &mut env);387 if context.debug_enabled {388 context.log_debug(DebugLogEntry::new(389 "ValidateLocalsNotReassignedAfterRender",390 "ok".to_string(),391 ));392 }393 context.timing.stop();394395 if env.config.validate_ref_access_during_render {396 context.timing.start("ValidateNoRefAccessInRender");397 react_compiler_validation::validate_no_ref_access_in_render(&hir, &mut env);398 if context.debug_enabled {399 context.log_debug(DebugLogEntry::new(400 "ValidateNoRefAccessInRender",401 "ok".to_string(),402 ));403 }404 context.timing.stop();405 }406407 if env.config.validate_no_set_state_in_render {408 context.timing.start("ValidateNoSetStateInRender");409 react_compiler_validation::validate_no_set_state_in_render(&hir, &mut env)?;410 if context.debug_enabled {411 context.log_debug(DebugLogEntry::new(412 "ValidateNoSetStateInRender",413 "ok".to_string(),414 ));415 }416 context.timing.stop();417 }418419 if env.config.validate_no_derived_computations_in_effects_exp420 && env.output_mode == OutputMode::Lint421 {422 context423 .timing424 .start("ValidateNoDerivedComputationsInEffects");425 let errors =426 react_compiler_validation::validate_no_derived_computations_in_effects_exp(427 &hir, &env,428 )?;429 log_errors_as_events(&errors, context);430 if context.debug_enabled {431 context.log_debug(DebugLogEntry::new(432 "ValidateNoDerivedComputationsInEffects",433 "ok".to_string(),434 ));435 }436 context.timing.stop();437 } else if env.config.validate_no_derived_computations_in_effects {438 context439 .timing440 .start("ValidateNoDerivedComputationsInEffects");441 react_compiler_validation::validate_no_derived_computations_in_effects(&hir, &mut env)?;442 if context.debug_enabled {443 context.log_debug(DebugLogEntry::new(444 "ValidateNoDerivedComputationsInEffects",445 "ok".to_string(),446 ));447 }448 context.timing.stop();449 }450451 if env.config.validate_no_set_state_in_effects && env.output_mode == OutputMode::Lint {452 context.timing.start("ValidateNoSetStateInEffects");453 let errors = react_compiler_validation::validate_no_set_state_in_effects(&hir, &env)?;454 log_errors_as_events(&errors, context);455 if context.debug_enabled {456 context.log_debug(DebugLogEntry::new(457 "ValidateNoSetStateInEffects",458 "ok".to_string(),459 ));460 }461 context.timing.stop();462 }463464 if env.config.validate_no_jsx_in_try_statements && env.output_mode == OutputMode::Lint {465 context.timing.start("ValidateNoJSXInTryStatement");466 let errors = react_compiler_validation::validate_no_jsx_in_try_statement(&hir);467 log_errors_as_events(&errors, context);468 if context.debug_enabled {469 context.log_debug(DebugLogEntry::new(470 "ValidateNoJSXInTryStatement",471 "ok".to_string(),472 ));473 }474 context.timing.stop();475 }476477 context478 .timing479 .start("ValidateNoFreezingKnownMutableFunctions");480 react_compiler_validation::validate_no_freezing_known_mutable_functions(&hir, &mut env);481 if context.debug_enabled {482 context.log_debug(DebugLogEntry::new(483 "ValidateNoFreezingKnownMutableFunctions",484 "ok".to_string(),485 ));486 }487 context.timing.stop();488 }489490 context.timing.start("InferReactivePlaces");491 react_compiler_inference::infer_reactive_places(&mut hir, &mut env)?;492 context.timing.stop();493494 if context.debug_enabled {495 context.timing.start("debug_print:InferReactivePlaces");496 let debug_reactive_places = debug_print::debug_hir(&hir, &env);497 context.log_debug(DebugLogEntry::new(498 "InferReactivePlaces",499 debug_reactive_places,500 ));501 context.timing.stop();502 }503504 if env.enable_validations() {505 context.timing.start("ValidateExhaustiveDependencies");506 react_compiler_validation::validate_exhaustive_dependencies(&mut hir, &mut env)?;507 if context.debug_enabled {508 context.log_debug(DebugLogEntry::new(509 "ValidateExhaustiveDependencies",510 "ok".to_string(),511 ));512 }513 context.timing.stop();514 }515516 context517 .timing518 .start("RewriteInstructionKindsBasedOnReassignment");519 react_compiler_ssa::rewrite_instruction_kinds_based_on_reassignment(&mut hir, &env)?;520 context.timing.stop();521522 if context.debug_enabled {523 context524 .timing525 .start("debug_print:RewriteInstructionKindsBasedOnReassignment");526 let debug_rewrite = debug_print::debug_hir(&hir, &env);527 context.log_debug(DebugLogEntry::new(528 "RewriteInstructionKindsBasedOnReassignment",529 debug_rewrite,530 ));531 context.timing.stop();532 }533534 if env.enable_validations()535 && env.config.validate_static_components536 && env.output_mode == OutputMode::Lint537 {538 context.timing.start("ValidateStaticComponents");539 let errors = react_compiler_validation::validate_static_components(&hir);540 log_errors_as_events(&errors, context);541 if context.debug_enabled {542 context.log_debug(DebugLogEntry::new(543 "ValidateStaticComponents",544 "ok".to_string(),545 ));546 }547 context.timing.stop();548 }549550 if env.enable_memoization() {551 context.timing.start("InferReactiveScopeVariables");552 react_compiler_inference::infer_reactive_scope_variables(&mut hir, &mut env)?;553 context.timing.stop();554555 if context.debug_enabled {556 context557 .timing558 .start("debug_print:InferReactiveScopeVariables");559 let debug_infer_scopes = debug_print::debug_hir(&hir, &env);560 context.log_debug(DebugLogEntry::new(561 "InferReactiveScopeVariables",562 debug_infer_scopes,563 ));564 context.timing.stop();565 }566 }567568 context569 .timing570 .start("MemoizeFbtAndMacroOperandsInSameScope");571 let fbt_operands =572 react_compiler_inference::memoize_fbt_and_macro_operands_in_same_scope(&hir, &mut env);573 context.timing.stop();574575 if context.debug_enabled {576 context577 .timing578 .start("debug_print:MemoizeFbtAndMacroOperandsInSameScope");579 let debug_fbt = debug_print::debug_hir(&hir, &env);580 context.log_debug(DebugLogEntry::new(581 "MemoizeFbtAndMacroOperandsInSameScope",582 debug_fbt,583 ));584 context.timing.stop();585 }586587 if env.config.enable_jsx_outlining {588 context.timing.start("OutlineJsx");589 react_compiler_optimization::outline_jsx(&mut hir, &mut env);590 context.timing.stop();591 }592593 if env.config.enable_name_anonymous_functions {594 context.timing.start("NameAnonymousFunctions");595 react_compiler_optimization::name_anonymous_functions(&mut hir, &mut env);596 context.timing.stop();597598 if context.debug_enabled {599 context.timing.start("debug_print:NameAnonymousFunctions");600 let debug_name_anon = debug_print::debug_hir(&hir, &env);601 context.log_debug(DebugLogEntry::new(602 "NameAnonymousFunctions",603 debug_name_anon,604 ));605 context.timing.stop();606 }607 }608609 if env.config.enable_function_outlining {610 context.timing.start("OutlineFunctions");611 react_compiler_optimization::outline_functions(&mut hir, &mut env, &fbt_operands);612 context.timing.stop();613614 if context.debug_enabled {615 context.timing.start("debug_print:OutlineFunctions");616 let debug_outline = debug_print::debug_hir(&hir, &env);617 context.log_debug(DebugLogEntry::new("OutlineFunctions", debug_outline));618 context.timing.stop();619 }620 }621622 context.timing.start("AlignMethodCallScopes");623 react_compiler_inference::align_method_call_scopes(&mut hir, &mut env);624 context.timing.stop();625626 if context.debug_enabled {627 context.timing.start("debug_print:AlignMethodCallScopes");628 let debug_align = debug_print::debug_hir(&hir, &env);629 context.log_debug(DebugLogEntry::new("AlignMethodCallScopes", debug_align));630 context.timing.stop();631 }632633 context.timing.start("AlignObjectMethodScopes");634 react_compiler_inference::align_object_method_scopes(&mut hir, &mut env);635 context.timing.stop();636637 if context.debug_enabled {638 context.timing.start("debug_print:AlignObjectMethodScopes");639 let debug_align_obj = debug_print::debug_hir(&hir, &env);640 context.log_debug(DebugLogEntry::new(641 "AlignObjectMethodScopes",642 debug_align_obj,643 ));644 context.timing.stop();645 }646647 context.timing.start("PruneUnusedLabelsHIR");648 react_compiler_optimization::prune_unused_labels_hir(&mut hir);649 context.timing.stop();650651 if context.debug_enabled {652 context.timing.start("debug_print:PruneUnusedLabelsHIR");653 let debug_prune_labels = debug_print::debug_hir(&hir, &env);654 context.log_debug(DebugLogEntry::new(655 "PruneUnusedLabelsHIR",656 debug_prune_labels,657 ));658 context.timing.stop();659 }660661 context.timing.start("AlignReactiveScopesToBlockScopesHIR");662 react_compiler_inference::align_reactive_scopes_to_block_scopes_hir(&mut hir, &mut env);663 context.timing.stop();664665 if context.debug_enabled {666 context667 .timing668 .start("debug_print:AlignReactiveScopesToBlockScopesHIR");669 let debug_align_block_scopes = debug_print::debug_hir(&hir, &env);670 context.log_debug(DebugLogEntry::new(671 "AlignReactiveScopesToBlockScopesHIR",672 debug_align_block_scopes,673 ));674 context.timing.stop();675 }676677 context.timing.start("MergeOverlappingReactiveScopesHIR");678 react_compiler_inference::merge_overlapping_reactive_scopes_hir(&mut hir, &mut env);679 context.timing.stop();680681 if context.debug_enabled {682 context683 .timing684 .start("debug_print:MergeOverlappingReactiveScopesHIR");685 let debug_merge_overlapping = debug_print::debug_hir(&hir, &env);686 context.log_debug(DebugLogEntry::new(687 "MergeOverlappingReactiveScopesHIR",688 debug_merge_overlapping,689 ));690 context.timing.stop();691 }692693 // TODO: port assertValidBlockNesting694 if context.debug_enabled {695 context.log_debug(DebugLogEntry::new(696 "AssertValidBlockNesting",697 "ok".to_string(),698 ));699 }700701 context.timing.start("BuildReactiveScopeTerminalsHIR");702 react_compiler_inference::build_reactive_scope_terminals_hir(&mut hir, &mut env);703 context.timing.stop();704705 if context.debug_enabled {706 context707 .timing708 .start("debug_print:BuildReactiveScopeTerminalsHIR");709 let debug_build_scope_terminals = debug_print::debug_hir(&hir, &env);710 context.log_debug(DebugLogEntry::new(711 "BuildReactiveScopeTerminalsHIR",712 debug_build_scope_terminals,713 ));714 context.timing.stop();715 }716717 // TODO: port assertValidBlockNesting718 if context.debug_enabled {719 context.log_debug(DebugLogEntry::new(720 "AssertValidBlockNesting",721 "ok".to_string(),722 ));723 }724725 context.timing.start("FlattenReactiveLoopsHIR");726 react_compiler_inference::flatten_reactive_loops_hir(&mut hir);727 context.timing.stop();728729 if context.debug_enabled {730 context.timing.start("debug_print:FlattenReactiveLoopsHIR");731 let debug_flatten_loops = debug_print::debug_hir(&hir, &env);732 context.log_debug(DebugLogEntry::new(733 "FlattenReactiveLoopsHIR",734 debug_flatten_loops,735 ));736 context.timing.stop();737 }738739 context.timing.start("FlattenScopesWithHooksOrUseHIR");740 react_compiler_inference::flatten_scopes_with_hooks_or_use_hir(&mut hir, &env)?;741 context.timing.stop();742743 if context.debug_enabled {744 context745 .timing746 .start("debug_print:FlattenScopesWithHooksOrUseHIR");747 let debug_flatten_hooks = debug_print::debug_hir(&hir, &env);748 context.log_debug(DebugLogEntry::new(749 "FlattenScopesWithHooksOrUseHIR",750 debug_flatten_hooks,751 ));752 context.timing.stop();753 }754755 // TODO: port assertTerminalSuccessorsExist756 if context.debug_enabled {757 context.log_debug(DebugLogEntry::new(758 "AssertTerminalSuccessorsExist",759 "ok".to_string(),760 ));761 }762 // TODO: port assertTerminalPredsExist763 if context.debug_enabled {764 context.log_debug(DebugLogEntry::new(765 "AssertTerminalPredsExist",766 "ok".to_string(),767 ));768 }769770 context.timing.start("PropagateScopeDependenciesHIR");771 react_compiler_inference::propagate_scope_dependencies_hir(&mut hir, &mut env);772 context.timing.stop();773774 if context.debug_enabled {775 context776 .timing777 .start("debug_print:PropagateScopeDependenciesHIR");778 let debug_propagate_deps = debug_print::debug_hir(&hir, &env);779 context.log_debug(DebugLogEntry::new(780 "PropagateScopeDependenciesHIR",781 debug_propagate_deps,782 ));783 context.timing.stop();784 }785786 context.timing.start("BuildReactiveFunction");787 let mut reactive_fn = react_compiler_reactive_scopes::build_reactive_function(&hir, &env)?;788 context.timing.stop();789790 let hir_formatter = |fmt: &mut react_compiler_hir::print::PrintFormatter,791 func: &react_compiler_hir::HirFunction| {792 debug_print::format_hir_function_into(fmt, func);793 };794795 if context.debug_enabled {796 context.timing.start("debug_print:BuildReactiveFunction");797 let debug_reactive = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(798 &reactive_fn, &env, Some(&hir_formatter),799 );800 context.log_debug(DebugLogEntry::new("BuildReactiveFunction", debug_reactive));801 context.timing.stop();802 }803804 context.timing.start("AssertWellFormedBreakTargets");805 react_compiler_reactive_scopes::assert_well_formed_break_targets(&reactive_fn, &env);806 if context.debug_enabled {807 context.log_debug(DebugLogEntry::new(808 "AssertWellFormedBreakTargets",809 "ok".to_string(),810 ));811 }812 context.timing.stop();813814 context.timing.start("PruneUnusedLabels");815 react_compiler_reactive_scopes::prune_unused_labels(&mut reactive_fn, &env)?;816 context.timing.stop();817818 if context.debug_enabled {819 context.timing.start("debug_print:PruneUnusedLabels");820 let debug_prune_labels_reactive = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(821 &reactive_fn, &env, Some(&hir_formatter),822 );823 context.log_debug(DebugLogEntry::new(824 "PruneUnusedLabels",825 debug_prune_labels_reactive,826 ));827 context.timing.stop();828 }829830 context.timing.start("AssertScopeInstructionsWithinScopes");831 react_compiler_reactive_scopes::assert_scope_instructions_within_scopes(&reactive_fn, &env)?;832 if context.debug_enabled {833 context.log_debug(DebugLogEntry::new(834 "AssertScopeInstructionsWithinScopes",835 "ok".to_string(),836 ));837 }838 context.timing.stop();839840 context.timing.start("PruneNonEscapingScopes");841 react_compiler_reactive_scopes::prune_non_escaping_scopes(&mut reactive_fn, &mut env)?;842 context.timing.stop();843844 if context.debug_enabled {845 context.timing.start("debug_print:PruneNonEscapingScopes");846 let debug = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(847 &reactive_fn, &env, Some(&hir_formatter),848 );849 context.log_debug(DebugLogEntry::new("PruneNonEscapingScopes", debug));850 context.timing.stop();851 }852853 context.timing.start("PruneNonReactiveDependencies");854 react_compiler_reactive_scopes::prune_non_reactive_dependencies(&mut reactive_fn, &mut env);855 context.timing.stop();856857 if context.debug_enabled {858 context859 .timing860 .start("debug_print:PruneNonReactiveDependencies");861 let debug_prune_non_reactive = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(862 &reactive_fn, &env, Some(&hir_formatter),863 );864 context.log_debug(DebugLogEntry::new(865 "PruneNonReactiveDependencies",866 debug_prune_non_reactive,867 ));868 context.timing.stop();869 }870871 context.timing.start("PruneUnusedScopes");872 react_compiler_reactive_scopes::prune_unused_scopes(&mut reactive_fn, &env)?;873 context.timing.stop();874875 if context.debug_enabled {876 context.timing.start("debug_print:PruneUnusedScopes");877 let debug_prune_unused_scopes = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(878 &reactive_fn, &env, Some(&hir_formatter),879 );880 context.log_debug(DebugLogEntry::new(881 "PruneUnusedScopes",882 debug_prune_unused_scopes,883 ));884 context.timing.stop();885 }886887 context888 .timing889 .start("MergeReactiveScopesThatInvalidateTogether");890 react_compiler_reactive_scopes::merge_reactive_scopes_that_invalidate_together(891 &mut reactive_fn,892 &mut env,893 )?;894 context.timing.stop();895896 if context.debug_enabled {897 context898 .timing899 .start("debug_print:MergeReactiveScopesThatInvalidateTogether");900 let debug = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(901 &reactive_fn, &env, Some(&hir_formatter),902 );903 context.log_debug(DebugLogEntry::new(904 "MergeReactiveScopesThatInvalidateTogether",905 debug,906 ));907 context.timing.stop();908 }909910 context.timing.start("PruneAlwaysInvalidatingScopes");911 react_compiler_reactive_scopes::prune_always_invalidating_scopes(&mut reactive_fn, &env)?;912 context.timing.stop();913914 if context.debug_enabled {915 context916 .timing917 .start("debug_print:PruneAlwaysInvalidatingScopes");918 let debug_prune_always_inv = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(919 &reactive_fn, &env, Some(&hir_formatter),920 );921 context.log_debug(DebugLogEntry::new(922 "PruneAlwaysInvalidatingScopes",923 debug_prune_always_inv,924 ));925 context.timing.stop();926 }927928 context.timing.start("PropagateEarlyReturns");929 react_compiler_reactive_scopes::propagate_early_returns(&mut reactive_fn, &mut env);930 context.timing.stop();931932 if context.debug_enabled {933 context.timing.start("debug_print:PropagateEarlyReturns");934 let debug = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(935 &reactive_fn, &env, Some(&hir_formatter),936 );937 context.log_debug(DebugLogEntry::new("PropagateEarlyReturns", debug));938 context.timing.stop();939 }940941 context.timing.start("PruneUnusedLValues");942 react_compiler_reactive_scopes::prune_unused_lvalues(&mut reactive_fn, &env);943 context.timing.stop();944945 if context.debug_enabled {946 context.timing.start("debug_print:PruneUnusedLValues");947 let debug_prune_lvalues = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(948 &reactive_fn, &env, Some(&hir_formatter),949 );950 context.log_debug(DebugLogEntry::new(951 "PruneUnusedLValues",952 debug_prune_lvalues,953 ));954 context.timing.stop();955 }956957 context.timing.start("PromoteUsedTemporaries");958 react_compiler_reactive_scopes::promote_used_temporaries(&mut reactive_fn, &mut env);959 context.timing.stop();960961 if context.debug_enabled {962 context.timing.start("debug_print:PromoteUsedTemporaries");963 let debug = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(964 &reactive_fn, &env, Some(&hir_formatter),965 );966 context.log_debug(DebugLogEntry::new("PromoteUsedTemporaries", debug));967 context.timing.stop();968 }969970 context971 .timing972 .start("ExtractScopeDeclarationsFromDestructuring");973 react_compiler_reactive_scopes::extract_scope_declarations_from_destructuring(974 &mut reactive_fn,975 &mut env,976 )?;977 context.timing.stop();978979 if context.debug_enabled {980 context981 .timing982 .start("debug_print:ExtractScopeDeclarationsFromDestructuring");983 let debug = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(984 &reactive_fn, &env, Some(&hir_formatter),985 );986 context.log_debug(DebugLogEntry::new(987 "ExtractScopeDeclarationsFromDestructuring",988 debug,989 ));990 context.timing.stop();991 }992993 context.timing.start("StabilizeBlockIds");994 react_compiler_reactive_scopes::stabilize_block_ids(&mut reactive_fn, &mut env);995 context.timing.stop();996997 if context.debug_enabled {998 context.timing.start("debug_print:StabilizeBlockIds");999 let debug_stabilize = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(1000 &reactive_fn, &env, Some(&hir_formatter),1001 );1002 context.log_debug(DebugLogEntry::new("StabilizeBlockIds", debug_stabilize));1003 context.timing.stop();1004 }10051006 context.timing.start("RenameVariables");1007 let unique_identifiers =1008 react_compiler_reactive_scopes::rename_variables(&mut reactive_fn, &mut env);1009 context.timing.stop();10101011 for name in &unique_identifiers {1012 context.add_new_reference(name.clone());1013 }10141015 if context.debug_enabled {1016 context.timing.start("debug_print:RenameVariables");1017 let debug = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(1018 &reactive_fn, &env, Some(&hir_formatter),1019 );1020 context.log_debug(DebugLogEntry::new("RenameVariables", debug));1021 context.timing.stop();1022 }10231024 context.timing.start("PruneHoistedContexts");1025 react_compiler_reactive_scopes::prune_hoisted_contexts(&mut reactive_fn, &mut env)?;1026 context.timing.stop();10271028 if context.debug_enabled {1029 context.timing.start("debug_print:PruneHoistedContexts");1030 let debug = react_compiler_reactive_scopes::print_reactive_function::debug_reactive_function_with_formatter(1031 &reactive_fn, &env, Some(&hir_formatter),1032 );1033 context.log_debug(DebugLogEntry::new("PruneHoistedContexts", debug));1034 context.timing.stop();1035 }10361037 if env.config.enable_preserve_existing_memoization_guarantees1038 || env.config.validate_preserve_existing_memoization_guarantees1039 {1040 context.timing.start("ValidatePreservedManualMemoization");1041 react_compiler_validation::validate_preserved_manual_memoization(&reactive_fn, &mut env);1042 if context.debug_enabled {1043 context.log_debug(DebugLogEntry::new(1044 "ValidatePreservedManualMemoization",1045 "ok".to_string(),1046 ));1047 }1048 context.timing.stop();1049 }10501051 context.timing.start("codegen");1052 let codegen_result = react_compiler_reactive_scopes::codegen_function(1053 &reactive_fn,1054 &mut env,1055 unique_identifiers,1056 fbt_operands,1057 )?;1058 context.timing.stop();10591060 // NOTE: we intentionally do NOT register the memo cache import here.1061 // The import is registered in apply_compiled_functions() only for functions1062 // that are actually applied to the output. Registering it here would cause1063 // a spurious `import { c as _c }` when a function compiles with memo slots1064 // but is later discarded (e.g., due to "use no memo" opt-out or errors),1065 // while other functions in the same file compile to 0 memo slots.10661067 if env.config.validate_source_locations {1068 super::validate_source_locations::validate_source_locations(1069 func,1070 &codegen_result,1071 &mut env,1072 );1073 }10741075 // Simulate unexpected exception for testing (matches TS Pipeline.ts)1076 if env.config.throw_unknown_exception_testonly {1077 let mut err = CompilerError::new();1078 err.push_error_detail(react_compiler_diagnostics::CompilerErrorDetail {1079 category: react_compiler_diagnostics::ErrorCategory::Invariant,1080 reason: "unexpected error".to_string(),1081 description: None,1082 loc: None,1083 suggestions: None,1084 });1085 return Err(err);1086 }10871088 // Check for accumulated errors at the end of the pipeline1089 // (matches TS Pipeline.ts: env.hasErrors() → Err at the end)1090 if env.has_errors() {1091 // Merge UIDs even on error: in TS, Babel's scope.generateUid() permanently1092 // registers names in the scope's `uids` map regardless of whether the function1093 // compilation succeeds or fails. Without this merge, failed compilations would1094 // "leak" _temp names that subsequent successful compilations wouldn't see,1095 // causing numbering mismatches vs TS.1096 if let Some(uid_names) = env.take_uid_known_names() {1097 context.merge_uid_known_names(&uid_names);1098 }1099 return Err(env.take_errors());1100 }11011102 // Re-compile outlined functions through the full pipeline.1103 // This mirrors TS behavior where outlined functions from JSX outlining1104 // are pushed back onto the compilation queue and compiled as components.1105 let mut compiled_outlined: Vec<OutlinedFunction> = Vec::new();1106 for o in codegen_result.outlined {1107 let outlined_codegen = CodegenFunction {1108 loc: o.func.loc,1109 id: o.func.id,1110 name_hint: o.func.name_hint,1111 params: o.func.params,1112 body: o.func.body,1113 generator: o.func.generator,1114 is_async: o.func.is_async,1115 memo_slots_used: o.func.memo_slots_used,1116 memo_blocks: o.func.memo_blocks,1117 memo_values: o.func.memo_values,1118 pruned_memo_blocks: o.func.pruned_memo_blocks,1119 pruned_memo_values: o.func.pruned_memo_values,1120 outlined: Vec::new(),1121 };1122 if let Some(fn_type) = o.fn_type {1123 let fn_name = outlined_codegen.id.as_ref().map(|id| id.name.clone());1124 match compile_outlined_fn(1125 outlined_codegen,1126 fn_name.as_deref(),1127 fn_type,1128 mode,1129 env_config,1130 context,1131 ) {1132 Ok(compiled) => {1133 compiled_outlined.push(OutlinedFunction {1134 func: compiled,1135 fn_type: Some(fn_type),1136 });1137 }1138 Err(_err) => {1139 // If re-compilation fails, skip the outlined function1140 }1141 }1142 } else {1143 compiled_outlined.push(OutlinedFunction {1144 func: outlined_codegen,1145 fn_type: o.fn_type,1146 });1147 }1148 }11491150 if let Some(uid_names) = env.take_uid_known_names() {1151 context.merge_uid_known_names(&uid_names);1152 }11531154 Ok(CodegenFunction {1155 loc: codegen_result.loc,1156 id: codegen_result.id,1157 name_hint: codegen_result.name_hint,1158 params: codegen_result.params,1159 body: codegen_result.body,1160 generator: codegen_result.generator,1161 is_async: codegen_result.is_async,1162 memo_slots_used: codegen_result.memo_slots_used,1163 memo_blocks: codegen_result.memo_blocks,1164 memo_values: codegen_result.memo_values,1165 pruned_memo_blocks: codegen_result.pruned_memo_blocks,1166 pruned_memo_values: codegen_result.pruned_memo_values,1167 outlined: compiled_outlined,1168 })1169}11701171/// Compile an outlined function's codegen AST through the full pipeline.1172///1173/// Creates a fresh Environment, builds a synthetic ScopeInfo with unique fake1174/// positions for identifier resolution, lowers from AST to HIR, then runs1175/// the full compilation pipeline. This mirrors the TS behavior where outlined1176/// functions are inserted into the program AST and re-compiled from scratch.1177pub fn compile_outlined_fn(1178 mut codegen_fn: CodegenFunction,1179 fn_name: Option<&str>,1180 fn_type: ReactFunctionType,1181 mode: CompilerOutputMode,1182 env_config: &EnvironmentConfig,1183 context: &mut ProgramContext,1184) -> Result<CodegenFunction, CompilerError> {1185 let mut env = Environment::with_config(env_config.clone());1186 env.fn_type = fn_type;1187 env.output_mode = match mode {1188 CompilerOutputMode::Ssr => OutputMode::Ssr,1189 CompilerOutputMode::Client => OutputMode::Client,1190 CompilerOutputMode::Lint => OutputMode::Lint,1191 };11921193 // Build a FunctionDeclaration from the codegen output1194 let mut outlined_decl = react_compiler_ast::statements::FunctionDeclaration {1195 base: react_compiler_ast::common::BaseNode::typed("FunctionDeclaration"),1196 id: codegen_fn.id.take(),1197 params: std::mem::take(&mut codegen_fn.params),1198 body: std::mem::replace(1199 &mut codegen_fn.body,1200 react_compiler_ast::statements::BlockStatement {1201 base: react_compiler_ast::common::BaseNode::typed("BlockStatement"),1202 body: Vec::new(),1203 directives: Vec::new(),1204 },1205 ),1206 generator: codegen_fn.generator,1207 is_async: codegen_fn.is_async,1208 declare: None,1209 return_type: None,1210 type_parameters: None,1211 predicate: None,1212 component_declaration: false,1213 hook_declaration: false,1214 };12151216 // Build scope info by assigning fake positions to all identifiers1217 let scope_info = build_outlined_scope_info(&mut outlined_decl);12181219 let func_node = react_compiler_lowering::FunctionNode::FunctionDeclaration(&outlined_decl);1220 let mut hir = react_compiler_lowering::lower(&func_node, fn_name, &scope_info, &mut env)?;12211222 if env.has_invariant_errors() {1223 return Err(env.take_invariant_errors());1224 }12251226 run_pipeline_passes(&mut hir, &mut env, context)1227}12281229/// Build a ScopeInfo for an outlined function declaration by assigning unique1230/// fake positions to all Identifier nodes and building the binding/reference maps.1231fn build_outlined_scope_info(1232 func: &mut react_compiler_ast::statements::FunctionDeclaration,1233) -> react_compiler_ast::scope::ScopeInfo {1234 use react_compiler_ast::scope::*;12351236 let mut pos: u32 = 1; // reserve 0 for the function itself1237 func.base.start = Some(0);12381239 let mut fn_bindings: FxHashMap<String, BindingId> = FxHashMap::default();1240 let mut bindings_list: Vec<BindingData> = Vec::new();1241 let mut ref_to_binding: IndexMap<u32, BindingId, FxBuildHasher> = IndexMap::default();12421243 // Helper to add a binding1244 let _add_binding =1245 |name: &str,1246 kind: BindingKind,1247 p: u32,1248 fn_bindings: &mut FxHashMap<String, BindingId>,1249 bindings_list: &mut Vec<BindingData>,1250 ref_to_binding: &mut IndexMap<u32, BindingId, FxBuildHasher>| {1251 if fn_bindings.contains_key(name) {1252 // Already exists, just add reference1253 let bid = fn_bindings[name];1254 ref_to_binding.insert(p, bid);1255 return;1256 }1257 let binding_id = BindingId(bindings_list.len() as u32);1258 fn_bindings.insert(name.to_string(), binding_id);1259 bindings_list.push(BindingData {1260 id: binding_id,1261 name: name.to_string(),1262 kind,1263 scope: ScopeId(1),1264 declaration_type: "VariableDeclarator".to_string(),1265 declaration_start: Some(p),1266 declaration_node_id: None,1267 import: None,1268 });1269 ref_to_binding.insert(p, binding_id);1270 };12711272 // Process params - add as Param bindings1273 for param in &mut func.params {1274 outlined_assign_pattern_positions(1275 param,1276 &mut pos,1277 BindingKind::Param,1278 &mut fn_bindings,1279 &mut bindings_list,1280 &mut ref_to_binding,1281 );1282 }12831284 // Process body - walk all statements to assign positions and collect variable declarations1285 for stmt in &mut func.body.body {1286 outlined_assign_stmt_positions(1287 stmt,1288 &mut pos,1289 &mut fn_bindings,1290 &mut bindings_list,1291 &mut ref_to_binding,1292 );1293 }12941295 let program_scope = ScopeData {1296 id: ScopeId(0),1297 parent: None,1298 kind: ScopeKind::Program,1299 bindings: FxHashMap::default(),1300 };1301 let fn_scope = ScopeData {1302 id: ScopeId(1),1303 parent: Some(ScopeId(0)),1304 kind: ScopeKind::Function,1305 bindings: fn_bindings,1306 };13071308 let mut node_to_scope: FxHashMap<u32, ScopeId> = FxHashMap::default();1309 node_to_scope.insert(0, ScopeId(1));13101311 // Mirror position maps into node-ID maps for outlined functions1312 let mut node_id_to_scope: FxHashMap<u32, ScopeId> = FxHashMap::default();1313 node_id_to_scope.insert(0, ScopeId(1));1314 let ref_node_id_to_binding: IndexMap<u32, BindingId, FxBuildHasher> =1315 ref_to_binding.iter().map(|(&k, &v)| (k, v)).collect();13161317 ScopeInfo {1318 scopes: vec![program_scope, fn_scope],1319 bindings: bindings_list,1320 node_to_scope,1321 node_to_scope_end: FxHashMap::default(),1322 reference_to_binding: IndexMap::default(),1323 ref_node_id_to_binding,1324 node_id_to_scope,1325 program_scope: ScopeId(0),1326 }1327}13281329/// Assign positions to identifiers in a pattern and register as bindings.1330fn outlined_assign_pattern_positions(1331 pattern: &mut react_compiler_ast::patterns::PatternLike,1332 pos: &mut u32,1333 kind: react_compiler_ast::scope::BindingKind,1334 fn_bindings: &mut rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,1335 bindings_list: &mut Vec<react_compiler_ast::scope::BindingData>,1336 ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,1337) {1338 use react_compiler_ast::patterns::PatternLike;1339 use react_compiler_ast::scope::*;13401341 match pattern {1342 PatternLike::Identifier(id) => {1343 let p = *pos;1344 *pos += 1;1345 id.base.start = Some(p);1346 id.base.node_id = Some(p);1347 // Add as a binding1348 if !fn_bindings.contains_key(&id.name) {1349 let binding_id = BindingId(bindings_list.len() as u32);1350 fn_bindings.insert(id.name.clone(), binding_id);1351 bindings_list.push(BindingData {1352 id: binding_id,1353 name: id.name.clone(),1354 kind: kind.clone(),1355 scope: ScopeId(1),1356 declaration_type: "VariableDeclarator".to_string(),1357 declaration_start: Some(p),1358 declaration_node_id: Some(p),1359 import: None,1360 });1361 ref_to_binding.insert(p, binding_id);1362 } else {1363 let bid = fn_bindings[&id.name];1364 ref_to_binding.insert(p, bid);1365 }1366 }1367 PatternLike::ObjectPattern(obj) => {1368 for prop in &mut obj.properties {1369 match prop {1370 react_compiler_ast::patterns::ObjectPatternProperty::ObjectProperty(1371 p_inner,1372 ) => {1373 outlined_assign_pattern_positions(1374 &mut p_inner.value,1375 pos,1376 kind.clone(),1377 fn_bindings,1378 bindings_list,1379 ref_to_binding,1380 );1381 }1382 react_compiler_ast::patterns::ObjectPatternProperty::RestElement(r) => {1383 outlined_assign_pattern_positions(1384 &mut r.argument,1385 pos,1386 kind.clone(),1387 fn_bindings,1388 bindings_list,1389 ref_to_binding,1390 );1391 }1392 }1393 }1394 }1395 PatternLike::ArrayPattern(arr) => {1396 for elem in arr.elements.iter_mut().flatten() {1397 outlined_assign_pattern_positions(1398 elem,1399 pos,1400 kind.clone(),1401 fn_bindings,1402 bindings_list,1403 ref_to_binding,1404 );1405 }1406 }1407 PatternLike::AssignmentPattern(assign) => {1408 outlined_assign_pattern_positions(1409 &mut assign.left,1410 pos,1411 kind.clone(),1412 fn_bindings,1413 bindings_list,1414 ref_to_binding,1415 );1416 }1417 PatternLike::RestElement(rest) => {1418 outlined_assign_pattern_positions(1419 &mut rest.argument,1420 pos,1421 kind.clone(),1422 fn_bindings,1423 bindings_list,1424 ref_to_binding,1425 );1426 }1427 _ => {}1428 }1429}14301431/// Assign positions to identifiers in a statement body.1432fn outlined_assign_stmt_positions(1433 stmt: &mut react_compiler_ast::statements::Statement,1434 pos: &mut u32,1435 fn_bindings: &mut rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,1436 bindings_list: &mut Vec<react_compiler_ast::scope::BindingData>,1437 ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,1438) {1439 use react_compiler_ast::statements::Statement;14401441 match stmt {1442 Statement::VariableDeclaration(decl) => {1443 for declarator in &mut decl.declarations {1444 // Process init first (references)1445 if let Some(init) = &mut declarator.init {1446 outlined_assign_expr_positions(init, pos, fn_bindings, ref_to_binding);1447 }1448 // Process pattern (declarations)1449 outlined_assign_pattern_positions(1450 &mut declarator.id,1451 pos,1452 react_compiler_ast::scope::BindingKind::Let,1453 fn_bindings,1454 bindings_list,1455 ref_to_binding,1456 );1457 }1458 }1459 Statement::ReturnStatement(ret) => {1460 if let Some(arg) = &mut ret.argument {1461 outlined_assign_expr_positions(arg, pos, fn_bindings, ref_to_binding);1462 }1463 }1464 Statement::ExpressionStatement(expr_stmt) => {1465 outlined_assign_expr_positions(1466 &mut expr_stmt.expression,1467 pos,1468 fn_bindings,1469 ref_to_binding,1470 );1471 }1472 _ => {}1473 }1474}14751476/// Assign positions to identifiers in an expression.1477fn outlined_assign_expr_positions(1478 expr: &mut react_compiler_ast::expressions::Expression,1479 pos: &mut u32,1480 fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,1481 ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,1482) {1483 use react_compiler_ast::expressions::*;14841485 match expr {1486 Expression::Identifier(id) => {1487 let p = *pos;1488 *pos += 1;1489 id.base.start = Some(p);1490 id.base.node_id = Some(p);1491 if let Some(&bid) = fn_bindings.get(&id.name) {1492 ref_to_binding.insert(p, bid);1493 }1494 }1495 Expression::JSXElement(jsx) => {1496 // Opening tag1497 outlined_assign_jsx_name_positions(1498 &mut jsx.opening_element.name,1499 pos,1500 fn_bindings,1501 ref_to_binding,1502 );1503 for attr in &mut jsx.opening_element.attributes {1504 match attr {1505 react_compiler_ast::jsx::JSXAttributeItem::JSXAttribute(a) => {1506 if let Some(val) = &mut a.value {1507 outlined_assign_jsx_val_positions(1508 val,1509 pos,1510 fn_bindings,1511 ref_to_binding,1512 );1513 }1514 }1515 react_compiler_ast::jsx::JSXAttributeItem::JSXSpreadAttribute(s) => {1516 outlined_assign_expr_positions(1517 &mut s.argument,1518 pos,1519 fn_bindings,1520 ref_to_binding,1521 );1522 }1523 }1524 }1525 for child in &mut jsx.children {1526 outlined_assign_jsx_child_positions(child, pos, fn_bindings, ref_to_binding);1527 }1528 }1529 Expression::JSXFragment(frag) => {1530 for child in &mut frag.children {1531 outlined_assign_jsx_child_positions(child, pos, fn_bindings, ref_to_binding);1532 }1533 }1534 _ => {}1535 }1536}15371538fn outlined_assign_jsx_name_positions(1539 name: &mut react_compiler_ast::jsx::JSXElementName,1540 pos: &mut u32,1541 fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,1542 ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,1543) {1544 match name {1545 react_compiler_ast::jsx::JSXElementName::JSXIdentifier(id) => {1546 let p = *pos;1547 *pos += 1;1548 id.base.start = Some(p);1549 id.base.node_id = Some(p);1550 if let Some(&bid) = fn_bindings.get(&id.name) {1551 ref_to_binding.insert(p, bid);1552 }1553 }1554 react_compiler_ast::jsx::JSXElementName::JSXMemberExpression(m) => {1555 outlined_assign_jsx_member_positions(m, pos, fn_bindings, ref_to_binding);1556 }1557 _ => {}1558 }1559}15601561fn outlined_assign_jsx_member_positions(1562 member: &mut react_compiler_ast::jsx::JSXMemberExpression,1563 pos: &mut u32,1564 fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,1565 ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,1566) {1567 match &mut *member.object {1568 react_compiler_ast::jsx::JSXMemberExprObject::JSXIdentifier(id) => {1569 let p = *pos;1570 *pos += 1;1571 id.base.start = Some(p);1572 id.base.node_id = Some(p);1573 if let Some(&bid) = fn_bindings.get(&id.name) {1574 ref_to_binding.insert(p, bid);1575 }1576 }1577 react_compiler_ast::jsx::JSXMemberExprObject::JSXMemberExpression(inner) => {1578 outlined_assign_jsx_member_positions(inner, pos, fn_bindings, ref_to_binding);1579 }1580 }1581}15821583fn outlined_assign_jsx_val_positions(1584 val: &mut react_compiler_ast::jsx::JSXAttributeValue,1585 pos: &mut u32,1586 fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,1587 ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,1588) {1589 match val {1590 react_compiler_ast::jsx::JSXAttributeValue::JSXExpressionContainer(c) => {1591 if let react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(e) =1592 &mut c.expression1593 {1594 outlined_assign_expr_positions(e, pos, fn_bindings, ref_to_binding);1595 }1596 }1597 react_compiler_ast::jsx::JSXAttributeValue::JSXElement(el) => {1598 let mut expr = react_compiler_ast::expressions::Expression::JSXElement(el.clone());1599 outlined_assign_expr_positions(&mut expr, pos, fn_bindings, ref_to_binding);1600 if let react_compiler_ast::expressions::Expression::JSXElement(new_el) = expr {1601 **el = *new_el;1602 }1603 }1604 _ => {}1605 }1606}16071608fn outlined_assign_jsx_child_positions(1609 child: &mut react_compiler_ast::jsx::JSXChild,1610 pos: &mut u32,1611 fn_bindings: &rustc_hash::FxHashMap<String, react_compiler_ast::scope::BindingId>,1612 ref_to_binding: &mut IndexMap<u32, react_compiler_ast::scope::BindingId, FxBuildHasher>,1613) {1614 match child {1615 react_compiler_ast::jsx::JSXChild::JSXExpressionContainer(c) => {1616 if let react_compiler_ast::jsx::JSXExpressionContainerExpr::Expression(e) =1617 &mut c.expression1618 {1619 outlined_assign_expr_positions(e, pos, fn_bindings, ref_to_binding);1620 }1621 }1622 react_compiler_ast::jsx::JSXChild::JSXElement(el) => {1623 let mut expr =1624 react_compiler_ast::expressions::Expression::JSXElement(Box::new(*el.clone()));1625 outlined_assign_expr_positions(&mut expr, pos, fn_bindings, ref_to_binding);1626 if let react_compiler_ast::expressions::Expression::JSXElement(new_el) = expr {1627 **el = *new_el;1628 }1629 }1630 react_compiler_ast::jsx::JSXChild::JSXFragment(frag) => {1631 for inner in &mut frag.children {1632 outlined_assign_jsx_child_positions(inner, pos, fn_bindings, ref_to_binding);1633 }1634 }1635 _ => {}1636 }1637}1638// end of outlined function helpers16391640/// Run the compilation pipeline passes on an HIR function (everything after lowering).1641///1642/// This is extracted from `compile_fn` to allow reuse for outlined functions.1643/// Returns the compiled CodegenFunction on success.1644fn run_pipeline_passes(1645 hir: &mut react_compiler_hir::HirFunction,1646 env: &mut Environment,1647 context: &mut ProgramContext,1648) -> Result<CodegenFunction, CompilerError> {1649 react_compiler_optimization::prune_maybe_throws(hir, &mut env.functions)?;16501651 react_compiler_optimization::drop_manual_memoization(hir, env)?;16521653 react_compiler_optimization::inline_immediately_invoked_function_expressions(hir, env);16541655 react_compiler_optimization::merge_consecutive_blocks::merge_consecutive_blocks(1656 hir,1657 &mut env.functions,1658 );16591660 react_compiler_ssa::enter_ssa(hir, env).map_err(|diag| {1661 let loc = diag.primary_location().cloned();1662 let mut err = CompilerError::new();1663 err.push_error_detail(react_compiler_diagnostics::CompilerErrorDetail {1664 category: diag.category,1665 reason: diag.reason,1666 description: diag.description,1667 loc,1668 suggestions: diag.suggestions,1669 });1670 err1671 })?;16721673 react_compiler_ssa::eliminate_redundant_phi(hir, env);16741675 react_compiler_optimization::constant_propagation(hir, env);16761677 react_compiler_typeinference::infer_types(hir, env)?;16781679 if env.enable_validations() {1680 if env.config.validate_hooks_usage {1681 react_compiler_validation::validate_hooks_usage(hir, env)?;1682 }1683 }16841685 react_compiler_optimization::optimize_props_method_calls(hir, env);16861687 react_compiler_inference::analyse_functions(hir, env, &mut |_inner_func, _inner_env| {})?;16881689 if env.has_invariant_errors() {1690 return Err(env.take_invariant_errors());1691 }16921693 react_compiler_inference::infer_mutation_aliasing_effects(hir, env, false)?;16941695 if env.output_mode == OutputMode::Ssr {1696 react_compiler_optimization::optimize_for_ssr(hir, env);1697 }16981699 react_compiler_optimization::dead_code_elimination(hir, env);17001701 react_compiler_optimization::prune_maybe_throws(hir, &mut env.functions)?;17021703 react_compiler_inference::infer_mutation_aliasing_ranges(hir, env, false)?;17041705 if env.enable_validations() {1706 react_compiler_validation::validate_locals_not_reassigned_after_render(hir, env);17071708 if env.config.validate_ref_access_during_render {1709 react_compiler_validation::validate_no_ref_access_in_render(hir, env);1710 }17111712 if env.config.validate_no_set_state_in_render {1713 react_compiler_validation::validate_no_set_state_in_render(hir, env)?;1714 }17151716 react_compiler_validation::validate_no_freezing_known_mutable_functions(hir, env);1717 }17181719 react_compiler_inference::infer_reactive_places(hir, env)?;17201721 if env.enable_validations() {1722 react_compiler_validation::validate_exhaustive_dependencies(hir, env)?;1723 }17241725 react_compiler_ssa::rewrite_instruction_kinds_based_on_reassignment(hir, env)?;17261727 if env.enable_memoization() {1728 react_compiler_inference::infer_reactive_scope_variables(hir, env)?;1729 }17301731 let fbt_operands =1732 react_compiler_inference::memoize_fbt_and_macro_operands_in_same_scope(hir, env);17331734 // Don't run outline_jsx on outlined functions (they're already outlined)17351736 if env.config.enable_name_anonymous_functions {1737 react_compiler_optimization::name_anonymous_functions(hir, env);1738 }17391740 if env.config.enable_function_outlining {1741 react_compiler_optimization::outline_functions(hir, env, &fbt_operands);1742 }17431744 react_compiler_inference::align_method_call_scopes(hir, env);1745 react_compiler_inference::align_object_method_scopes(hir, env);17461747 react_compiler_optimization::prune_unused_labels_hir(hir);17481749 react_compiler_inference::align_reactive_scopes_to_block_scopes_hir(hir, env);1750 react_compiler_inference::merge_overlapping_reactive_scopes_hir(hir, env);17511752 react_compiler_inference::build_reactive_scope_terminals_hir(hir, env);1753 react_compiler_inference::flatten_reactive_loops_hir(hir);1754 react_compiler_inference::flatten_scopes_with_hooks_or_use_hir(hir, env)?;1755 react_compiler_inference::propagate_scope_dependencies_hir(hir, env);1756 let mut reactive_fn = react_compiler_reactive_scopes::build_reactive_function(hir, env)?;17571758 react_compiler_reactive_scopes::assert_well_formed_break_targets(&reactive_fn, env);17591760 react_compiler_reactive_scopes::prune_unused_labels(&mut reactive_fn, env)?;17611762 react_compiler_reactive_scopes::assert_scope_instructions_within_scopes(&reactive_fn, env)?;17631764 react_compiler_reactive_scopes::prune_non_escaping_scopes(&mut reactive_fn, env)?;1765 react_compiler_reactive_scopes::prune_non_reactive_dependencies(&mut reactive_fn, env);1766 react_compiler_reactive_scopes::prune_unused_scopes(&mut reactive_fn, env)?;1767 react_compiler_reactive_scopes::merge_reactive_scopes_that_invalidate_together(1768 &mut reactive_fn,1769 env,1770 )?;1771 react_compiler_reactive_scopes::prune_always_invalidating_scopes(&mut reactive_fn, env)?;1772 react_compiler_reactive_scopes::propagate_early_returns(&mut reactive_fn, env);1773 react_compiler_reactive_scopes::prune_unused_lvalues(&mut reactive_fn, env);1774 react_compiler_reactive_scopes::promote_used_temporaries(&mut reactive_fn, env);1775 react_compiler_reactive_scopes::extract_scope_declarations_from_destructuring(1776 &mut reactive_fn,1777 env,1778 )?;1779 react_compiler_reactive_scopes::stabilize_block_ids(&mut reactive_fn, env);17801781 let unique_identifiers =1782 react_compiler_reactive_scopes::rename_variables(&mut reactive_fn, env);1783 for name in &unique_identifiers {1784 context.add_new_reference(name.clone());1785 }17861787 react_compiler_reactive_scopes::prune_hoisted_contexts(&mut reactive_fn, env)?;17881789 if env.config.enable_preserve_existing_memoization_guarantees1790 || env.config.validate_preserve_existing_memoization_guarantees1791 {1792 react_compiler_validation::validate_preserved_manual_memoization(&reactive_fn, env);1793 }17941795 let codegen_result = react_compiler_reactive_scopes::codegen_function(1796 &reactive_fn,1797 env,1798 unique_identifiers,1799 fbt_operands,1800 )?;18011802 Ok(CodegenFunction {1803 loc: codegen_result.loc,1804 id: codegen_result.id,1805 name_hint: codegen_result.name_hint,1806 params: codegen_result.params,1807 body: codegen_result.body,1808 generator: codegen_result.generator,1809 is_async: codegen_result.is_async,1810 memo_slots_used: codegen_result.memo_slots_used,1811 memo_blocks: codegen_result.memo_blocks,1812 memo_values: codegen_result.memo_values,1813 pruned_memo_blocks: codegen_result.pruned_memo_blocks,1814 pruned_memo_values: codegen_result.pruned_memo_values,1815 outlined: codegen_result1816 .outlined1817 .into_iter()1818 .map(|o| OutlinedFunction {1819 func: CodegenFunction {1820 loc: o.func.loc,1821 id: o.func.id,1822 name_hint: o.func.name_hint,1823 params: o.func.params,1824 body: o.func.body,1825 generator: o.func.generator,1826 is_async: o.func.is_async,1827 memo_slots_used: o.func.memo_slots_used,1828 memo_blocks: o.func.memo_blocks,1829 memo_values: o.func.memo_values,1830 pruned_memo_blocks: o.func.pruned_memo_blocks,1831 pruned_memo_values: o.func.pruned_memo_values,1832 outlined: Vec::new(),1833 },1834 fn_type: o.fn_type,1835 })1836 .collect(),1837 })1838}18391840/// Log CompilerError diagnostics as CompileError events, matching TS `env.logErrors()` behavior.1841/// These are logged for telemetry/lint output but not accumulated as compile errors.1842fn log_errors_as_events(errors: &CompilerError, context: &mut ProgramContext) {1843 // Use the source_filename from the AST (set by parser's sourceFilename option).1844 // This is stored on the Environment during lowering.1845 let source_filename = context.source_filename();1846 for detail in &errors.details {1847 let detail_info = match detail {1848 react_compiler_diagnostics::CompilerErrorOrDiagnostic::Diagnostic(d) => {1849 let items: Option<Vec<CompilerErrorItemInfo>> = {1850 let v: Vec<CompilerErrorItemInfo> = d1851 .details1852 .iter()1853 .map(|item| match item {1854 react_compiler_diagnostics::CompilerDiagnosticDetail::Error {1855 loc,1856 message,1857 identifier_name,1858 } => CompilerErrorItemInfo {1859 kind: "error".to_string(),1860 loc: loc.as_ref().map(|l| LoggerSourceLocation {1861 start: LoggerPosition {1862 line: l.start.line,1863 column: l.start.column,1864 index: l.start.index,1865 },1866 end: LoggerPosition {1867 line: l.end.line,1868 column: l.end.column,1869 index: l.end.index,1870 },1871 filename: source_filename.clone(),1872 identifier_name: identifier_name.clone(),1873 }),1874 message: message.clone(),1875 },1876 react_compiler_diagnostics::CompilerDiagnosticDetail::Hint {1877 message,1878 } => CompilerErrorItemInfo {1879 kind: "hint".to_string(),1880 loc: None,1881 message: Some(message.clone()),1882 },1883 })1884 .collect();1885 if v.is_empty() { None } else { Some(v) }1886 };1887 CompilerErrorDetailInfo {1888 category: format!("{:?}", d.category),1889 reason: d.reason.clone(),1890 description: d.description.clone(),1891 severity: format!("{:?}", d.logged_severity()),1892 suggestions: None,1893 details: items,1894 loc: None,1895 }1896 }1897 react_compiler_diagnostics::CompilerErrorOrDiagnostic::ErrorDetail(d) => {1898 CompilerErrorDetailInfo {1899 category: format!("{:?}", d.category),1900 reason: d.reason.clone(),1901 description: d.description.clone(),1902 severity: format!("{:?}", d.logged_severity()),1903 suggestions: None,1904 details: None,1905 loc: None,1906 }1907 }1908 };1909 context.log_event(super::compile_result::LoggerEvent::CompileError {1910 fn_loc: None,1911 detail: detail_info,1912 });1913 }1914}
Findings
✓ No findings reported for this file.