use std::collections::HashMap;
use log::info;
use crate::{
analysis::{MethodRef, SsaFunction, SsaOp},
compiler::{CompilerContext, EventKind, ModificationScope, SsaPass},
metadata::token::Token,
CilObject, Result,
};
#[derive(Debug, Clone)]
pub(crate) struct ResourceTarget {
pub(crate) target_token: Token,
pub(crate) is_virtual: bool,
}
pub struct ResourceRestorationPass {
redirects: HashMap<Token, ResourceTarget>,
}
impl ResourceRestorationPass {
#[must_use]
pub fn new(redirects: HashMap<Token, ResourceTarget>) -> Self {
info!(
"JIEJIE.NET resources: received {} interception→BCL redirects",
redirects.len()
);
Self { redirects }
}
}
impl SsaPass for ResourceRestorationPass {
fn name(&self) -> &'static str {
"jiejie-resource-restoration"
}
fn description(&self) -> &'static str {
"Replaces JIEJIE.NET resource interception calls with original BCL calls"
}
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.redirects.is_empty() {
return Ok(false);
}
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() {
let (dest, method_token, args) = match instr.op() {
SsaOp::Call { dest, method, args } => (dest, method.token(), args),
SsaOp::CallVirt { dest, method, args } => (dest, method.token(), args),
_ => continue,
};
if let Some(target) = self.redirects.get(&method_token) {
let new_op = if target.is_virtual {
SsaOp::CallVirt {
dest: *dest,
method: MethodRef::new(target.target_token),
args: args.clone(),
}
} else {
SsaOp::Call {
dest: *dest,
method: MethodRef::new(target.target_token),
args: args.clone(),
}
};
replacements.push((block_idx, instr_idx, new_op));
}
}
}
if replacements.is_empty() {
return Ok(false);
}
for (block_idx, instr_idx, new_op) in replacements.iter() {
ssa.replace_instruction_op(*block_idx, *instr_idx, new_op.clone());
}
for _ in &replacements {
ctx.events.record(EventKind::ArtifactRemoved);
}
Ok(true)
}
}