Proven by paranoid runs over lorep (17 classes, fully green),
LoVisual (4081 classes) and oc2r (1278 classes) with per-class
java -Xverify:all, each fix first reproduced as a red test:
- merge: Top absorbs instead of Top-wins (javac declares Top for
slots unwritten on some inflow; precise types reject those edges).
Regression: loop_head_keeps_top_for_loop_local.
- emit: collapse [Long|Double, Top] pairs on operand stacks
(javac writes one entry; the pair is rejected). Regression:
wide_stack_join_emits_single_entries.
- xfer ArrayLoad: push the wide pair, fixing lcmp underflow.
Regression: array_load_wide_pushes_pair.
- xfer <init>: convert only the receiver's own allocation site
instead of every Uninitialized (nested construction clobbered
the outer object). Regression: nested_construction_keeps_outer.
- xfer ANewArray: array operands gain one dimension ([B] -> [[B)
instead of an illegal L-wrap ([L[B;). Covered by the same test.
- jar load: duplicate class names (multi-release module-info
twins) keep the first occurrence instead of crashing save.
Test: duplicate_class_names_keep_first.
- strings/constants: skip methods past MAX_METHOD_INSNS (8000)
so giants cannot overflow the 64KB writer ceiling. Tests:
oversized_methods_are_skipped in both passes.
- merge errors now name both types being merged.
Also: em-dash removed repo-wide (724 spots, rule added to
todo.md), allow-lints only in tests (scratch example deleted),
code comments migrated to English in every file this change
touches.
FlowPass rebuilds one method per scheme picked from FlowConfig:
state-spread (dense tableswitch dispatcher), computed-switch
(sparse lookupswitch over a counter local), butterfly (branch
inversion), exception-goto (dispatcher under try with a dead
catch-all), plus opaque goto-splices inside every method and
StackMapTable recomputation afterwards. Now in standard_pipeline
(between constants and noise); --no-flow is an honest off-flag.
Acceptance is real JVM, not shapes: per-scheme verify, rotation
spread, calc-prints-45 execution, paranoid smoke on lorep-1.1.0
(131 methods rebuilt, no new failures over standard).
Two verifier rules learned the hard way are now construction
invariants: no inserted mark shares a bytecode offset with a
foreign one (nop pads), and no frameless gap follows a terminal
(dead blocks are opaque-entered and throw).
Also fixes a latent frame-emitter bug the pass exposed: wide
stack values were written as slot pairs ([Double, Top]) while
javac writes one entry ([Double]); HotSpot rejects the pair.
Stacks now collapse like locals (emit::stack_vars), with a
regression test in classfile/tests/frames.rs.
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.
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.
Documentation had drifted from the actual implementation: the noxis
(CLI) and noxis-verify crates were fully built and tested but every
doc called them "not started". Verified against the code (build +
test run) and corrected status across README.md, todo.md,
architecture.md, techniques.md and learnings/{lessons,priorities}.md.
Also:
- restyled README.md (badges, no em dashes, usage examples, dropped
the redundant "Принципы" section already covered elsewhere)
- added docs/learnings/ai-resistance.md: a sourced writeup of what
actually works against LLM-driven reverse engineering (Elastic
Security Labs' Opus 4.6 vs Tigress study, the Acoda paper) and what
of it applies to noxis, plus concrete todo.md §7 items
- noxis-structure-check: the 250-line/4-file gate was scanning
docs/**, which fought against writing documentation freely; it now
only scans crates/*/{src,tests,benches} and only counts .rs files