passes: junk classes with real bodies and no fingerprint

NoisePass now builds branching helper methods (counted int loop,
random arithmetic, even/odd merge, non-throwing ops only) whose
results the entry sums into a private static field, instead of an
empty run()V. Names come from the same NameGenerator styles as
real code and live in a random real package (no more shadow/decoy
uniformity for grep to catch); every generated method gets fresh
StackMapTable frames. weave_first_reachable takes (class, entry)
pairs. New NoiseConfig.junk_methods_per_class (default 3).

Tests assert invariants (unique valid identifiers, calls, field
store, determinism) plus real-JVM verification of junk classes;
paranoid+junk_ratio=1 smoke on lorep: 18 junk classes, all load.
This commit is contained in:
Loki 2026-10-09 14:04:24 +02:00
parent 74ad4552e4
commit 2cbb9db31e
Signed by: boba
GPG key ID: 253067914055423B
11 changed files with 648 additions and 159 deletions

View file

@ -1,22 +1,23 @@
//! Вплетание junk-классов в граф достижимости живых методов.
//! Weaving junk classes into the reachability graph of live methods.
use noxis_analysis::ClassHierarchy;
use noxis_classfile::{Insn, InvokeKind, Program, compute_frames};
use noxis_core::{CoreError, PassId};
use super::JUNK_ENTRY;
/// The woven call descriptor (entries are always `()V`).
const WOVEN_DESCRIPTOR: &str = "()V";
/// Вплетает вызов первого junk-класса в первый метод с кодом.
/// Weave a call of the first junk entry into the first method with code.
///
/// Вызов встаёт перед терминалом (`Return`/`AReturn`/`AThrow`), чтобы не
/// породить недостижимый хвост без фреймов; фреймы пересчитываются.
/// The call lands before the terminal (`Return`/`AReturn`/`AThrow`) so it
/// never strands an unreachable tail without frames; frames are recomputed.
pub(super) fn weave_first_reachable(
program: &mut Program,
real_classes: usize,
junk_names: &[String],
entries: &[(String, String)],
) -> Result<usize, CoreError> {
let missing = || CoreError::pass(PassId::Noise, "цель вплетания пропала");
let Some(owner) = junk_names.first() else {
let missing = || CoreError::pass(PassId::Noise, "weave target vanished");
let Some((owner, entry)) = entries.first() else {
return Err(missing());
};
let target = program.classes[..real_classes.min(program.classes.len())]
@ -39,8 +40,8 @@ pub(super) fn weave_first_reachable(
let call = Insn::Invoke {
kind: InvokeKind::Static,
owner: owner.clone(),
name: JUNK_ENTRY.0.to_string(),
descriptor: JUNK_ENTRY.1.to_string(),
name: entry.clone(),
descriptor: WOVEN_DESCRIPTOR.to_string(),
itf: false,
};
{