use crate::{
analysis::{ConstValue, FieldRef, MethodRef, SsaFunction, SsaOp},
compiler::{CompilerContext, EventKind, ModificationScope, SsaPass},
metadata::token::Token,
CilObject, Result,
};
pub struct ArrayInitRestorationPass {
accessor_token: Token,
field_tokens: Vec<Token>,
init_array_method: Option<Token>,
init_array_target: Option<Token>,
}
impl ArrayInitRestorationPass {
#[must_use]
pub fn new(
accessor_token: Token,
field_tokens: Vec<Token>,
init_array_method: Option<Token>,
init_array_target: Option<Token>,
) -> Self {
Self {
accessor_token,
field_tokens,
init_array_method,
init_array_target,
}
}
}
impl SsaPass for ArrayInitRestorationPass {
fn name(&self) -> &'static str {
"jiejie-array-init-restore"
}
fn description(&self) -> &'static str {
"Replaces JIEJIE.NET field handle container calls with direct field handle constants"
}
fn modification_scope(&self) -> ModificationScope {
ModificationScope::InstructionsOnly
}
fn run_on_method(
&self,
ssa: &mut SsaFunction,
_method_token: Token,
ctx: &CompilerContext,
_assembly: &CilObject,
) -> Result<bool> {
if self.field_tokens.is_empty() {
return Ok(false);
}
let constants = ssa.find_constants();
let mut replacements: Vec<(usize, usize, SsaOp)> = Vec::new();
for (block_idx, block) in ssa.blocks().iter().enumerate() {
for (instr_idx, instr) in block.instructions().iter().enumerate() {
if let SsaOp::Call { dest, method, args } = instr.op() {
let method_token = method.token();
if method_token == self.accessor_token {
if args.len() != 1 {
continue;
}
let Some(ConstValue::I32(index)) = constants.get(&args[0]) else {
continue;
};
let index = *index as usize;
if index >= self.field_tokens.len() {
continue;
}
let field_token = self.field_tokens[index];
if let Some(dest) = dest {
replacements.push((
block_idx,
instr_idx,
SsaOp::Const {
dest: *dest,
value: ConstValue::FieldHandle(FieldRef::new(field_token)),
},
));
}
continue;
}
if let (Some(init_method), Some(init_target)) =
(self.init_array_method, self.init_array_target)
{
if method_token == init_method && args.len() == 3 {
replacements.push((
block_idx,
instr_idx,
SsaOp::Call {
dest: *dest,
method: MethodRef::new(init_target),
args: vec![args[0], args[1]],
},
));
}
}
}
}
}
if replacements.is_empty() {
return Ok(false);
}
for (block_idx, instr_idx, new_op) in &replacements {
ssa.replace_instruction_op(*block_idx, *instr_idx, new_op.clone());
}
for (_, _, new_op) in &replacements {
let kind = match new_op {
SsaOp::Const { .. } => EventKind::ConstantFolded,
_ => EventKind::ArtifactRemoved,
};
ctx.events.record(kind);
}
Ok(true)
}
}