structure-check: gate counts code lines, docs and comments in English

Gate ignores blank and full-line comment lines so the 250-line limit
measures code bulk, not documentation weight; docs/README.md synced.
Translate code comments to English in classfile, core config, namegen,
rename assign, constants forms. Expand Plausible word bank with
client-ecosystem terms (Module, Setting, Mixin, ...). cargo fmt clean,
check.sh green.
This commit is contained in:
Loki 2026-10-09 10:31:02 +02:00
parent ec28ec82e3
commit 39178420e4
Signed by: boba
GPG key ID: 253067914055423B
42 changed files with 532 additions and 437 deletions

View file

@ -1,8 +1,9 @@
//! Эмиссия компактных кадров в точках, где их ждёт верификатор.
//! Emitting compact frames at the points where the verifier expects them.
//!
//! Вид кадра выбирается сравнением с предыдущим кадром (`Same`/`Append`/
//! `Chop`/`SameLocals`, иначе `Full`) — как у `javac`. Хвостовые `Top`
//! при сравнении и записи отрезаются: пустой слот — не тип.
//! A frame's kind is chosen by comparing with the previous frame
//! (`Same`/`Append`/`Chop`/`SameLocals`, otherwise `Full`) — like `javac`.
//! Trailing `Top`s are trimmed both when comparing and when writing: an empty
//! slot is not a type.
use std::collections::BTreeSet;
@ -11,10 +12,10 @@ use super::xfer::State;
use super::{StackMapFrame, VerificationType};
use crate::error::ClassfileError;
/// Кадры во всех точках `targets` (отсортированы — дельты по порядку).
/// Frames at every point in `targets` (sorted — deltas in order).
///
/// Дельты — в номерах инструкций (как backend после чтения); writer сам
/// пересчитает их в байты.
/// Deltas are in instruction numbers (like the backend after reading); the writer
/// recomputes them into bytes itself.
pub(super) fn emit_frames(
states: &[Option<State>],
targets: &BTreeSet<u32>,
@ -49,8 +50,8 @@ fn select(
previous: &[VerificationType],
stack: &[VerificationType],
) -> StackMapFrame {
// Сравнение — на гранулярности переменных (`long` — одна запись,
// как у `javac`): иначе половины пар дают `Append{[Top, Long]}`.
// Comparison is at variable granularity (`long` — one entry, like `javac`);
// otherwise halves would produce `Append{[Top, Long]}`.
let current = vars(locals);
let base = vars(previous);
if current == base {
@ -93,10 +94,10 @@ fn select(
}
}
/// Слоты → переменные: `[Long, Top]` — одна запись; хвостовые `Top` — прочь.
/// Slots → variables: `[Long, Top]` — one entry; trailing `Top`s — gone.
///
/// Одиночные `Top` внутри (дыры) сохраняются: в `Full` это легальный тип,
/// а в `Append` мы их не пускаем (см. `select`).
/// Single `Top`s inside (holes) are kept: in `Full` they are a legal type, and we
/// don't allow them in `Append` (see `select`).
fn vars(locals: &[VerificationType]) -> Vec<VerificationType> {
let mut out = Vec::with_capacity(locals.len());
let mut index = 0;

View file

@ -1,4 +1,4 @@
//! CFG тела метода: метки, рёбра, точки кадров, диапазоны handler'ов.
//! A method body's CFG: labels, edges, frame points, handler ranges.
use std::collections::{BTreeMap, BTreeSet};
@ -7,7 +7,7 @@ use crate::error::ClassfileError;
use crate::insn::{Insn, Label};
use crate::model::ExceptionHandler;
/// Метки → номера инструкций без маркеров.
/// Labels → instruction numbers, without the markers.
pub(super) fn strip_marks<'a>(
insns: &'a [Insn],
merger: &Merger<'_>,
@ -31,7 +31,7 @@ pub(super) fn strip_marks<'a>(
Ok((labels, bare))
}
/// Рёбра CFG без исключений: продолжение + цели переходов.
/// CFG edges without exceptions: fallthrough + branch targets.
pub(super) fn successors(
insn: &Insn,
index: u32,
@ -40,8 +40,8 @@ pub(super) fn successors(
merger: &Merger<'_>,
) -> Result<Vec<u32>, ClassfileError> {
let mut out = Vec::new();
// Без продолжения: переходы без возврата и выходы из метода.
// `Jsr`/`Ret` сюда не доходят (их отвергает `transfer`), но рёбра есть.
// No fallthrough: jumps that don't return and method exits.
// `Jsr`/`Ret` never reach here (rejected by `transfer`), but the edges exist.
let terminal = matches!(
insn,
Insn::Goto(..)
@ -67,7 +67,7 @@ pub(super) fn successors(
Ok(out)
}
/// Точки, где верификатор требует кадры: цели переходов + входы handler'ов.
/// Points where the verifier requires frames: branch targets + handler entries.
pub(super) fn frame_targets(
bare: &[&Insn],
labels: &BTreeMap<Label, u32>,
@ -91,12 +91,12 @@ pub(super) fn frame_targets(
Ok(targets)
}
/// Точка handler'а в номерах инструкций: `(start, end, handler, тип перехвата)`.
/// A handler point in instruction numbers: `(start, end, handler, catch type)`.
///
/// Диапазоны полуоткрытые `[start, end)`, как в JVMS.
/// Ranges are half-open `[start, end)`, as in the JVMS.
pub(super) type HandlerRange = (u32, u32, u32, Option<String>);
/// Handler'ы в номерах инструкций для рёбер исключений.
/// Handlers in instruction numbers, for exception edges.
pub(super) fn handler_ranges(
table: &[ExceptionHandler],
labels: &BTreeMap<Label, u32>,

View file

@ -1,12 +1,12 @@
//! Разбор дескрипторов типов (`(...)V`, `[L...;`).
//! Descriptor parsing (`(...)V`, `[L...;`).
use super::super::VerificationType;
/// Ошибка разбора дескриптора (контекст добавляет вызывающий код).
/// Descriptor parse error (context is added by the caller).
#[derive(Debug)]
pub(crate) struct DescError(pub String);
/// Уложить слоты значения в locals с позиции; вернуть следующую позицию.
/// Write value slots into `locals` at position; return next position.
pub(crate) fn write_local(
locals: &mut [VerificationType],
slot: usize,
@ -22,7 +22,7 @@ pub(crate) fn write_local(
Ok(slot + width)
}
/// Разобрать дескриптор массива на (глубина, элемент без `;`).
/// Parse an array descriptor into (depth, element without `;`).
pub(crate) fn split_array(descriptor: &str) -> Option<(usize, &str)> {
let dims = descriptor.bytes().take_while(|&b| b == b'[').count();
if dims == 0 {
@ -31,7 +31,7 @@ pub(crate) fn split_array(descriptor: &str) -> Option<(usize, &str)> {
Some((dims, descriptor[dims..].trim_end_matches(';')))
}
/// Слоты одного полевого дескриптора (`long`/`double` — парой с `Top`).
/// Slots for a single field descriptor (`long`/`double` — pair with `Top`).
pub(crate) fn field_slots(descriptor: &str) -> Result<Vec<VerificationType>, DescError> {
let mut chars = descriptor.chars();
match chars.next() {
@ -57,10 +57,10 @@ pub(crate) fn field_slots(descriptor: &str) -> Result<Vec<VerificationType>, Des
}
}
/// Слоты параметров и возврата (`None` — `void`).
/// Parameter and return slots (`None` — `void`).
type MethodSlots = (Vec<Vec<VerificationType>>, Option<Vec<VerificationType>>);
/// Дескриптор метода: слоты параметров и слоты возврата (`None` — `void`).
/// Method descriptor: parameter slots and return slots (`None` — `void`).
pub(crate) fn method_slots(descriptor: &str) -> Result<MethodSlots, DescError> {
let (params, ret) = descriptor
.split_once('(')
@ -128,7 +128,7 @@ pub(crate) fn method_slots(descriptor: &str) -> Result<MethodSlots, DescError> {
Ok((out, ret))
}
/// Тип элемента по дескриптору массива в стеке.
/// Element type from an array descriptor on the stack.
pub(crate) fn array_element(
array: &VerificationType,
kind: crate::insn::ArrayKind,

View file

@ -1,12 +1,12 @@
//! Слияние типов (LUB) и разбор дескрипторов.
//! Type merging (LUB) and descriptor parsing.
//!
//! Решётка: `Top` — «пусто» (берётся сосед), `Null` впитывается в ссылку,
//! одинаковые — сами в себя, ссылки — наименьший общий предок по иерархии.
//! Несочетаемые инициализированные типы (`int` vs ссылка после `astore`
//! в handler'е) дают `Top`, как обрезанные кадры `javac`: `Top` в кадре
//! принимает любое фактическое значение, поэтому это всегда sound.
//! Исключение — неинициализированные типы: их слияние означает сломанный
//! поток конструирования, здесь честная ошибка, а не молчаливый `Top`.
//! The lattice: `Top` is "empty" (the neighbour wins), `Null` is absorbed into a
//! reference, equal types map to themselves, references take the least upper
//! bound over the hierarchy. Incompatible initialized types (`int` vs a reference
//! after `astore` in a handler) yield `Top`, like `javac`'s truncated frames: a
//! `Top` in a frame accepts any actual value, so this is always sound. The
//! exception is uninitialized types: merging them means a broken construction
//! flow — here an honest error, not a silent `Top`.
pub(super) mod descs;
@ -17,13 +17,13 @@ use crate::model::Method;
use descs::write_local;
/// Иерархия классов для LUB: достаточно цепочки суперклассов.
/// The class hierarchy for LUB: the superclass chain is enough.
pub trait Hierarchy {
/// Прямой суперкласс (`None` — `java/lang/Object` или неизвестный).
/// The direct superclass (`None` — `java/lang/Object` or unknown).
fn superclass(&self, class: &str) -> Option<String>;
}
/// Контекст слияния: имена для ошибок + иерархия для LCA.
/// Merge context: names for errors + hierarchy for LCA.
pub(super) struct Merger<'h> {
pub class: String,
pub method: String,
@ -38,7 +38,7 @@ impl Merger<'_> {
}
}
/// Влить входящее состояние; `true` — слот изменился.
/// Merge an incoming state; `true` — the slot changed.
pub fn merge_into(
&self,
slot: &mut Option<State>,
@ -61,7 +61,7 @@ impl Merger<'_> {
}
}
/// Начальное состояние: параметры (+ `this`) поверх `Top`.
/// The initial state: parameters (+ `this`) over `Top`.
pub fn initial(&self, method: &Method, max_locals: u16) -> Result<State, ClassfileError> {
let is_static = method.access_flags & 0x0008 != 0;
let (params, _) =
@ -127,19 +127,19 @@ impl Merger<'_> {
Ok(first.clone())
}
(Object(a), Object(b)) => Ok(Object(self.common_superclass(a, b))),
// Одинаковые уже разобраны выше; здесь неинициализированные
// всегда разные — это сломанный поток конструирования.
// Equal ones are handled above; here uninitialized are always
// different — that's a broken construction flow.
(VerificationType::UninitializedThis | VerificationType::Uninitialized(_), _)
| (_, VerificationType::UninitializedThis | VerificationType::Uninitialized(_)) => {
Err(self.invalid("слияние неинициализированных типов"))
}
// Несочетаемые инициализированные (например, `int` из тела `try`
// и `Throwable` из handler'а в одном слоте): `Top`, как у `javac`.
// Incompatible initialized (e.g. `int` from the `try` body and
// `Throwable` from a handler in one slot): `Top`, like `javac`.
_ => Ok(Top),
}
}
/// Наименьший общий предок: подъём по цепочкам, запасной — `Object`.
/// Least common ancestor: climb the chains, fallback — `Object`.
fn common_superclass(&self, first: &str, second: &str) -> String {
if first == second {
return first.to_string();
@ -176,7 +176,7 @@ impl Merger<'_> {
"java/lang/Object".to_string()
}
/// LCA массивов: та же размерность + LCA элементов, иначе `Object`.
/// Arrays' LCA: same dimensionality + element LCA, otherwise `Object`.
fn array_lca(&self, first: &str, second: &str) -> String {
let (dims_a, elem_a) = descs::split_array(first).unwrap_or((0, first));
let (dims_b, elem_b) = descs::split_array(second).unwrap_or((0, second));

View file

@ -1,12 +1,12 @@
//! `StackMapTable`: представление фреймов и вычисление после трансформаций.
//! `StackMapTable`: frame representation and computation after transformations.
//!
//! Фреймы — самая дорогая часть любого обфускатора на Rust: JVM-верификатор
//! (class version ≥ 50) требует точного `StackMapTable`, иначе класс не грузится.
//! Варианты реализации и их цены — `docs/stack.md`.
//! Frames are the most expensive part of any Rust obfuscator: the JVM verifier
//! (class version ≥ 50) demands an exact `StackMapTable`, or the class won't load.
//! Implementation options and their costs — `docs/stack.md`.
//!
//! Метод: CFG по меткам → worklist с типами (`xfer`) → слияние LUB (`merge`)
//! → эмиссия компактных кадров (`emit`). Байтовые смещения берутся тем же
//! преобразованием, что использует writer, — иначе кадры разъедутся с байтами.
//! Method: CFG over labels → worklist with types (`xfer`) → LUB merge (`merge`)
//! → emit compact frames (`emit`). Byte offsets come from the same encoding the
//! writer uses — otherwise frames would drift apart from the bytes.
mod emit;
mod flow;
@ -27,7 +27,7 @@ pub use merge::Hierarchy;
use merge::Merger;
use xfer::{State, transfer};
/// Тип верификации из JVMS §4.10.1.
/// A verification type from JVMS §4.10.1.
#[derive(Debug, Clone, PartialEq)]
pub enum VerificationType {
Top,
@ -37,14 +37,14 @@ pub enum VerificationType {
Long,
Null,
UninitializedThis,
/// Внутреннее имя класса (`java/lang/Object`) или дескриптор массива.
/// The class's internal name (`java/lang/Object`) or an array descriptor.
Object(String),
/// Байтовое смещение инструкции `new`.
/// The byte offset of the `new` instruction.
Uninitialized(u16),
}
impl VerificationType {
/// Тип, соответствующий примитиву.
/// The type matching a primitive.
pub fn of_prim(p: Prim) -> Self {
match p {
Prim::Int => VerificationType::Integer,
@ -55,7 +55,7 @@ impl VerificationType {
}
}
/// Запись `StackMapTable` (JVMS §4.7.4).
/// A `StackMapTable` entry (JVMS §4.7.4).
#[derive(Debug, Clone, PartialEq)]
pub enum StackMapFrame {
Same {
@ -80,10 +80,11 @@ pub enum StackMapFrame {
},
}
/// Вычислить фреймы для тела метода после всех трансформаций.
/// Compute the frames for a method body after all transformations.
///
/// Кадры нужны только классам версии ≥ 50; `jsr`/`ret` отвергаются (такие
/// классы старше и кадры им не нужны). Иерархия — для LUB ссылочных типов.
/// Frames are needed only for version ≥ 50 classes; `jsr`/`ret` are rejected
/// (such classes are older and need no frames). The hierarchy is for the LUB of
/// reference types.
pub fn compute_frames(
class: &str,
method: &Method,
@ -112,7 +113,7 @@ pub fn compute_frames(
emit::emit_frames(&states, &targets, &initial.locals, &merger)
}
/// Байтовое смещение каждой инструкции — тем же кодированием, что в writer'е.
/// The byte offset of each instruction — using the same encoding as the writer.
fn layout(
bare: &[&Insn],
labels: &BTreeMap<Label, u32>,
@ -144,7 +145,7 @@ fn layout(
.collect()
}
/// Основной цикл: состояния текут по рёбрам CFG до неподвижной точки.
/// Main loop: states flow along CFG edges to a fixed point.
fn run_worklist(
merger: &Merger<'_>,
bare: &[&Insn],
@ -187,7 +188,7 @@ fn run_worklist(
Ok(())
}
/// Влить состояние в инструкцию; `true` — изменилось, продолжить обход.
/// Merge a state into an instruction; `true` — changed, keep walking.
fn flow_into(
merger: &Merger<'_>,
states: &mut [Option<State>],
@ -218,8 +219,8 @@ fn flow_into(
Ok(())
}
/// Рёбра CFG без исключений: продолжение + цели переходов.
/// Тип исключения на входе в handler (`catch-all` — любое throwable).
/// CFG edges without exceptions: fallthrough + branch targets.
/// The exception type at a handler's entry (`catch-all` — any throwable).
fn catch_type(catch: &Option<String>) -> VerificationType {
match catch {
Some(name) => VerificationType::Object(name.clone()),