use std::collections::HashSet;
use react_compiler_hir::environment::Environment;
use react_compiler_hir::{
FunctionId, HirFunction, IdentifierId, InstructionValue, NonLocalBinding,
};
use react_compiler_ssa::enter_ssa::placeholder_function;
pub fn outline_functions(
func: &mut HirFunction,
env: &mut Environment,
fbt_operands: &HashSet<IdentifierId>,
) {
enum Action {
Recurse(FunctionId),
RecurseAndOutline {
instr_idx: usize,
function_id: FunctionId,
},
}
let mut actions: Vec<Action> = Vec::new();
for block in func.body.blocks.values() {
for &instr_id in &block.instructions {
let instr = &func.instructions[instr_id.0 as usize];
let lvalue_id = instr.lvalue.identifier;
match &instr.value {
InstructionValue::FunctionExpression {
lowered_func, ..
} => {
let inner_func = &env.functions[lowered_func.func.0 as usize];
if inner_func.context.is_empty()
&& inner_func.id.is_none()
&& !fbt_operands.contains(&lvalue_id)
{
actions.push(Action::RecurseAndOutline {
instr_idx: instr_id.0 as usize,
function_id: lowered_func.func,
});
} else {
actions.push(Action::Recurse(lowered_func.func));
}
}
InstructionValue::ObjectMethod { lowered_func, .. } => {
actions.push(Action::Recurse(lowered_func.func));
}
_ => {}
}
}
}
for action in actions {
match action {
Action::Recurse(function_id) => {
let mut inner_func = std::mem::replace(
&mut env.functions[function_id.0 as usize],
placeholder_function(),
);
outline_functions(&mut inner_func, env, fbt_operands);
env.functions[function_id.0 as usize] = inner_func;
}
Action::RecurseAndOutline {
instr_idx,
function_id,
} => {
let mut inner_func = std::mem::replace(
&mut env.functions[function_id.0 as usize],
placeholder_function(),
);
outline_functions(&mut inner_func, env, fbt_operands);
env.functions[function_id.0 as usize] = inner_func;
let hint: Option<String> = env.functions[function_id.0 as usize]
.id
.clone()
.or_else(|| env.functions[function_id.0 as usize].name_hint.clone());
let generated_name =
env.generate_globally_unique_identifier_name(hint.as_deref());
env.functions[function_id.0 as usize].id = Some(generated_name.clone());
let outlined_func = env.functions[function_id.0 as usize].clone();
env.outline_function(outlined_func, None);
let loc = func.instructions[instr_idx].value.loc().cloned();
func.instructions[instr_idx].value = InstructionValue::LoadGlobal {
binding: NonLocalBinding::Global {
name: generated_name,
},
loc,
};
}
}
}
}