classfile,core: pick frame compact form by byte delta; honor --no-* flags

StackMapTable compact-vs-extended selection used the instruction delta,
but JVM validity requires the byte offset delta <= 63. Track byte offsets
in convert_frames and branch on byte_delta while keeping the instruction
delta stored (ristretto re-derives bytes).

Pipeline::run ignored config.disable, so --no-* CLI flags had no effect;
gate each pass on config.is_enabled.
This commit is contained in:
Loki 2026-10-08 22:39:22 +02:00
parent c3b8d4dd62
commit a2e4c69210
3 changed files with 56 additions and 17 deletions

View file

@ -1,7 +1,9 @@
//! Кадры `StackMapTable` при записи: вариант в вариант, дельты как есть.
//! Кадры `StackMapTable` при записи: вариант в вариант, дельты по инструкциям.
//!
//! Наш IR хранит кадры в номерах инструкций (как backend после чтения) —
//! backend сам пересчитает их в байты при `to_bytes`.
//! Наш IR хранит `offset_delta` в номерах инструкций; backend при `to_bytes`
//! сам пересчитает их в байты. Компактный формат (`Same`/`SameLocals`) годен
//! только когда **байтовая** дельта ≤ 63 — иначе нужен расширенный вариант.
//! Выбор делаем по байтовой дельте: байты считаются от layout текущего метода.
use ristretto_classfile::attributes::{StackFrame as RtFrame, VerificationType as RtType};
@ -9,37 +11,70 @@ use super::super::pool::Pool;
use crate::error::ClassfileError;
use crate::format::{StackMapFrame, VerificationType};
/// Байтовое смещение начала инструкции (для расчёта дельт кадров).
type ByteOf<'a> = &'a (dyn Fn(u32) -> Result<u16, ClassfileError> + 'a);
/// Кадры IR → кадры backend'а (индексные дельты — как есть).
pub(super) fn convert_frames(
frames: &[StackMapFrame],
pool: &mut Pool,
byte_of: ByteOf<'_>,
) -> Result<Vec<RtFrame>, ClassfileError> {
frames
.iter()
.map(|frame| build_frame(frame, pool))
.collect()
let mut out = Vec::with_capacity(frames.len());
let mut first = true;
let mut last_instruction: u32 = 0;
let mut last_byte: u16 = 0;
for frame in frames {
let delta = frame_delta(frame);
let instruction = if first {
delta
} else {
last_instruction + delta + 1
};
let byte = byte_of(instruction)?;
// Байтовая дельта относительно конца предыдущего кадра.
let byte_delta = if first {
byte
} else {
byte.checked_sub(last_byte)
.and_then(|d| d.checked_sub(1))
.ok_or_else(|| ClassfileError::Write("кадры не по порядку".into()))?
};
out.push(build_frame(frame, pool, byte_delta)?);
last_instruction = instruction;
last_byte = byte;
first = false;
}
Ok(out)
}
fn build_frame(frame: &StackMapFrame, pool: &mut Pool) -> Result<RtFrame, ClassfileError> {
/// `byte_delta` — истинная байтовая дельта; компактный вид годен лишь при ≤ 63.
fn build_frame(
frame: &StackMapFrame,
pool: &mut Pool,
byte_delta: u16,
) -> Result<RtFrame, ClassfileError> {
let delta = frame_delta(frame);
let offset = u16::try_from(delta)
let extended = u16::try_from(delta)
.map_err(|_| ClassfileError::Write(format!("дельта кадра {delta} вне u16")))?;
Ok(match frame {
StackMapFrame::Same { .. } => {
if delta <= 63 {
if byte_delta <= 63 {
// Backend при verify()/to_bytes читает frame_type как дельту по
// ИНСТРУКЦИЯМ и сам пересчитает её в байты (≤ 63 — влезает).
RtFrame::SameFrame {
frame_type: delta as u8,
}
} else {
RtFrame::SameFrameExtended {
frame_type: 251,
offset_delta: offset,
offset_delta: extended,
}
}
}
StackMapFrame::SameLocals { local, .. } => {
let stack = vec![vtype(local, pool)?];
if delta <= 63 {
if byte_delta <= 63 {
RtFrame::SameLocals1StackItemFrame {
frame_type: 64 + delta as u8,
stack,
@ -47,14 +82,14 @@ fn build_frame(frame: &StackMapFrame, pool: &mut Pool) -> Result<RtFrame, Classf
} else {
RtFrame::SameLocals1StackItemFrameExtended {
frame_type: 247,
offset_delta: offset,
offset_delta: extended,
stack,
}
}
}
StackMapFrame::Chop { count, .. } => RtFrame::ChopFrame {
frame_type: 251u8.wrapping_sub(*count),
offset_delta: offset,
offset_delta: extended,
},
StackMapFrame::Append { locals, .. } => {
let count = u8::try_from(locals.len()).map_err(|_| {
@ -67,13 +102,13 @@ fn build_frame(frame: &StackMapFrame, pool: &mut Pool) -> Result<RtFrame, Classf
}
RtFrame::AppendFrame {
frame_type: 251 + count,
offset_delta: offset,
offset_delta: extended,
locals: types(locals, pool)?,
}
}
StackMapFrame::Full { locals, stack, .. } => RtFrame::FullFrame {
frame_type: 255,
offset_delta: offset,
offset_delta: extended,
locals: types(locals, pool)?,
stack: types(stack, pool)?,
},

View file

@ -55,7 +55,7 @@ pub(super) fn convert_code(code: &Code, pool: &mut Pool) -> Result<RtAttr, Class
resolve: &resolve,
byte_of: &byte_of,
};
let frames = convert_frames(&code.frames, pool)?;
let frames = convert_frames(&code.frames, pool, &byte_of)?;
let mut attributes = Vec::with_capacity(code.attributes.len() + 1);
for attr in &code.attributes {
attributes.push(match attr {