obfuskator/crates/noxis-classfile/tests/pool/main.rs
loki5512344 f08bf60635 classfile: intern constant-pool entries on write (fix LVTT/LVT ClassFormatError)
ristretto's ConstantPool never deduplicates: every add_* pushes a new entry.
HotSpot matches each LocalVariableTypeTable entry to a LocalVariableTable
entry by raw name_index, so when a name (e.g. a local's) was interned twice
the two tables got different indices and the class failed to load:
'ClassFormatError: LVTT entry ... does not match any LVT entry'.

The writer now memoizes every pool builder by its semantic key and rebuilds
composite constants (Class/NameAndType/Fieldref/Methodref/MethodHandle/
Dynamic) from our own utf8/class primitives so ristretto never mints a
shadow Utf8. This also shrinks the pool (no more ldc -> ldc_w widening that
changed code length on a pure round-trip).

Split pool.rs into a pool/ module (refs in pool/refs.rs) to respect the
file-size gate. Regression test: tests/pool asserts the written pool has no
duplicate CONSTANT_Utf8. Verified: pure parse->write of the neoforge jar
(92 classes) now loads under java -Xverify:all with 0 errors.
2026-10-08 22:55:49 +02:00

47 lines
2.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Инвариант писателя: записанный пул не содержит дублей `CONSTANT_Utf8`.
//!
//! Backend не дедуплицирует (`add_*` всегда толкает новый элемент), а HotSpot
//! сопоставляет запись `LocalVariableTypeTable` с `LocalVariableTable` по
//! сырому `name_index`. Дублирующий `Utf8` для одного имени даёт разные индексы
//! и рушит класс: `ClassFormatError: LVTT entry ... does not match any LVT
//! entry`. Писатель обязан интернировать строки — поэтому в записанном пуле
//! каждый `CONSTANT_Utf8` уникален.
#![allow(clippy::expect_used, clippy::unwrap_used)]
use std::collections::HashSet;
use std::path::PathBuf;
use noxis_classfile::format;
use ristretto_classfile::{ClassFile as RtClass, Constant};
#[test]
fn written_pool_has_no_duplicate_utf8() {
let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data");
let mut names: Vec<PathBuf> = std::fs::read_dir(&dir)
.expect("tests/data")
.filter_map(|entry| entry.ok().map(|entry| entry.path()))
.filter(|path| path.extension().is_some_and(|ext| ext == "class"))
.collect();
names.sort();
assert!(!names.is_empty(), "нет фикстур в tests/data");
for path in &names {
let parsed = format::parse(&std::fs::read(path).expect("read")).expect("parse");
let written = format::write(&parsed).expect("write");
let raw = RtClass::from_bytes(&written).expect("written parses");
raw.verify().expect("written verifies");
let mut seen = HashSet::new();
for index in 1..=raw.constant_pool.len() {
if let Some(Constant::Utf8(value)) = raw.constant_pool.get(index as u16) {
let bytes = value.as_bytes().to_vec();
assert!(
seen.insert(bytes),
"дубль CONSTANT_Utf8 {:?} в {}",
String::from_utf8_lossy(value.as_bytes()),
parsed.this_class
);
}
}
}
}