This file is the working guide for coding agents in this repository. Read it before changing code. Keep it accurate when architecture, commands, or conventions change.
java-asm is a Rust 2024 workspace for reading, writing, inspecting, and presenting Java-family bytecode. It aims to provide an ASM-tree-like Rust API while supporting current JVM class-file formats (Java 21 is the documented target) and Android DEX/APK input.
The main data flow is:
class bytes -> JVMS raw structures -> ClassNode -> Smali-like presentation
DEX/APK bytes -> DEX raw/index structures -> resolved elements -> Smali-like presentation
-> server state -> egui UI
The parser core deliberately avoids runtime parsing dependencies. Repetitive binary read/write implementations are generated by the local proc-macro crate.
asm/(java_asm): core library. Contains the public JVMS, node, DEX, opcode/constant, reference, and Smali APIs.src/jvms/: class-file structures and public reader/writer entry points.src/dex/: DEX raw structures, instructions, lazy/index-based access, and resolved elements.src/node/: higher-level ASM-tree-like model and instruction nodes.src/smali.rs: presentation tree/token types shared by JVM and DEX paths.src/impls/: internal read/write/transform implementations. Keep implementation details here unless they are intentionally public API.tests/: sample-backed integration tests for JVMS, node conversion, and DEX.
asm_macro/(java_asm_macro): derivesReadFrom/WriteIntoand constant-container helpers. Change this when binary-layout boilerplate should be generated consistently.asm_server/(java_asm_server): APK loading, lazy content access, R8/ProGuard mapping, fuzzy search, async/native-WASM task abstraction, and frontend-independent UI state/messages.src/mapping.rs: mapping parsing and presentation-time class/member/line lookup. Accessors continue to use raw bytecode names.src/targets/: target-specific runtime adapters under thenative/andwasm/directories. Keep target dispatch inmod.rs; shared APK indexing lives inimpls/apk_load.rs.
asm_cli/(java_asm_cli): native Agent-facing CLI for finding classes with basic member structure, locating nested archive entries throughinternal_path, and exporting one or many classes as Smali. It does not create anAppContaineror provide MCP transport.asm_egui/(java_asm_egui): current desktop/experimental WASM egui frontend. UI code should consumeasm_serverstate instead of reimplementing parsing.index.htmlandTrunk.toml: browser shell and Trunk build configuration.
ta/: experimental Tauri/Preact frontend. It is currently excluded from Cargo workspace members; do not assume root Cargo commands build it.asm/tests/res/: checked-in binary fixtures.CompileTesting.javais the source of the class fixture;compile_source.batregenerates it on Windows..github/workflows/rust.yml: CI builds and tests the Cargo workspace on Ubuntu.
- Preserve the distinction between wire-format structures and ergonomic structures.
- JVMS/DEX raw structs should mirror specification field order, widths, counts, offsets, and terminology.
- Resolve indexes/references and normalize data in transforms, accessors, or node conversion code.
- Do not hide raw-format facts inside GUI code.
- Keep public modules thin. Public readers/writers collect inputs and delegate to
ReadContext,WriteContext, transforms, or private machinery underasm/src/impls/. Expose only stable domain concepts. - Reuse
AsmResult<T>and add a specificAsmErrvariant when an error category is meaningful. Propagate recoverable parse/I/O failures with?; avoid newunwrap()calls in library paths. - Preserve lazy DEX access. Retain offsets and indexes until callers request resolved class data, code, or Smali.
- Keep frontend-independent state and data preparation in
asm_server; keep egui layout and interaction inasm_egui. A UI interaction that only changes presentation state, such as find navigation or scrolling, should be handled directly in the UI layer during the current frame. - Use
UIMessageonly when an action must cross into server-owned behavior. Do not introduce event queues, result containers, pending flags, synthetic IDs, or pass-through return values when the UI can derive the result from existing state. - Keep durable facts as state and derive transient presentation from them. For example, render a toast from its stored kind, message, and creation time instead of mirroring it into separate frontend state.
- Put native/WASM differences behind
asm_server::targets; keep shared APK indexing inimpls/apk_load.rs. Browser code must use the publicjava_asm_serverre-exports forInstant,SystemTime, andDuration, plus the existing scheduling helpers. Do not add directweb-timedependencies or target checks to parsing, state, or frontend code. - Keep mapping as a server-owned presentation transform.
MappedName::raw_nameis the stable class/DEX lookup key, whiledisplay_nameinitially matches it and may be replaced by imported mapping data for Smali, search, tabs, and the file tree.
- Write source code, comments, log messages, and repository documentation in English. Keep non-English text only when it is intentionally required for localization or CJK/font regression tests (for example,
Log / 日志). - Prefer the simplest design that gives each piece of data and behavior one clear owner. Remove redundant states, conversions, wrappers, branches, and forwarding layers instead of explaining them.
- Prefer direct, explicit Rust over elaborate abstractions. Add a struct, enum, trait, queue, callback, or helper only when it represents a real domain concept or removes meaningful repetition.
- Keep control and data flow short. Avoid returning a value only to pass it through several functions, cloning data to detect changes, or tracking information that can be derived cheaply from current state.
- Be efficient by default: avoid unnecessary allocation, cloning, locking, rescanning, and repainting. Keep lock scopes narrow, but do not add coordination machinery whose complexity costs more than the work it saves.
- Model external specifications literally. Names like
constant_pool_count,class_data_off,U32BasedSize, and instruction-format types are preferred over renamed business terminology. - Use newtypes/type aliases to communicate binary meaning and shared ownership (
StrRef,DescriptorRef,InternalNameRef,DUInt,ArcVarOpt<T>). - Use
pub useat domain boundaries for the intended convenient API, while leaving implementation modules private orpub(crate). - Derive
Clone,Debug,Eq,PartialEq,Default,ReadFrom, orWriteIntowhere they remove mechanical code. - Add comments for specification rules, non-obvious invariants, index/offset semantics, endianness, ownership, and safe-unwrapping arguments. Avoid comments that merely restate an obvious expression.
- Early returns and
let Some(value) = ... else { return; };are favored for guard clauses. Compact one-line guards already occur frequently; match the surrounding file instead of reformatting unrelated code. - Prefer iterator pipelines for transformations and straightforward loops when parsing bytes or mutating state.
- Use
Arcfor genuinely shared immutable names/data andparking_lot::Mutexfor shared application state; do not introduce shared ownership by default. - Clone
Arc/Rcvalues and aliases explicitly withArc::clone(&value)/Rc::clone(&value)so shared-reference cloning is distinguishable from cloning owned data. - Logging/timing is part of the debugging style: backend code uses
log::{info,error,...}and integration tests useprintln!plusInstant. - Proc-macro failures may panic with actionable messages because they are compile-time author errors. Runtime parsers should return
AsmErrinstead. - Commit subjects are short, imperative, lower-case English phrases such as
add wasm supportorsupport fuzzy search in egui.
There is no repository rustfmt.toml, and the current tree is not clean under cargo fmt --all -- --check. Do not run workspace-wide automatic formatting as a drive-by cleanup. Format only code you changed, keep the local compact layout where practical, and do not mix broad formatting churn with a functional patch.
- Locate the owning layer before editing: raw layout, read/write implementation, transformation/node API, server behavior, or frontend rendering.
- Check the relevant official format section linked from the source comments when changing JVMS/DEX behavior.
- For a new binary structure, define fields in wire order, use existing numeric wrappers, and use
ReadFrom/WriteIntoplus#[index(...)]or#[align(...)]where supported. Extendasm_macroonly if the pattern is reusable. - For a new public capability, expose a narrow entry point and keep its mechanics in
impls. - Add or update the smallest relevant test. Prefer a focused assertion over output-only coverage for new behavior, while retaining timing/output when it helps inspect binary transformations.
- Run the narrow test first, then the affected crate, then the workspace when cross-crate behavior changed.
- Inspect
git diffand ensure fixture, generated, IDE, and formatting changes are intentional.
The normal toolchain is stable Rust with edition 2024 support.
Fast core loop:
cargo check -p java_asm
cargo test -p java_asmTarget a sample-backed integration test and keep its diagnostic output:
cargo test -p java_asm --test main jvms::read_test::read_jvms_test -- --nocapture
cargo test -p java_asm --test main node::read_test::read_node -- --nocapture
cargo test -p java_asm --test main dex::read_test::read_dex_test -- --nocaptureServer/backend changes:
cargo test -p java_asm_server -- --nocaptureNative CLI:
cargo test -p java_asm_cli
cargo build --release -p java_asm_cliGUI compile/run:
cargo check -p java_asm_egui
cargo run -p java_asm_eguiBrowser development and release:
rustup target add wasm32-unknown-unknown
cargo install trunk
cd asm_egui
trunk serve
trunk build --releaseBrowser-specific constraints:
- Trunk serves the canvas in
asm_egui/index.htmlon fixed port8080and writes release output toasm_egui/dist/;.github/workflows/web.ymlpublishes the same output. - Keep
data-wasm-opt="z" data-wasm-opt-params="--all-features"; current Rust output requires the enabled WASM operations. - Browser input currently supports APK, standalone DEX, multiple selections, and nested Android archives such as APKS.
- WebGL canvas text cannot use CSS font fallback. WASM fetches a pinned Noto Sans SC subset, while native builds use host fonts; keep the web font outside the WASM binary.
- Loading is throttled through
LoadingState. DEX pipelines run concurrently, native indexing uses the fixed Rayon pool, and WASM indexing must yield often enough for repainting.
Final Rust workspace verification (matches CI's effective build/test scope):
cargo build --workspace
cargo test --workspaceRoot Cargo commands do not validate ta/. If intentionally changing the Tauri frontend, use its own ta/package.json scripts and ta/src-tauri/Cargo.toml separately.
asm/tests/main.rsis the integration-test root; its child modules share helpers, so keep module paths intact when filtering tests.- Core fixtures are embedded with
include_bytes!, making the tests independent of the process working directory. - Parser tests currently favor real
.class/.dexsamples and inspectability. When fixing a bug, add exact assertions for the affected field/instruction/error so regressions do not depend only on printed output. - The build emits existing unused/deprecated/dead-code warnings. Do not treat pre-existing warnings as failures, but do not add new warnings in touched code.
- Preserve unrelated working-tree changes. Never rewrite IDE run configurations, fixtures, lockfiles, or generated assets unless the task requires it.
- Do not update dependencies or regenerate
Cargo.lockfor an unrelated feature. - Do not silently change supported class/DEX versions or raw numeric widths.
- Do not make
ta/a workspace member without checking its current experimental status and platform requirements. - If a behavior is incomplete, prefer an explicit error or documented limitation over fabricated/default data that makes a malformed binary look valid.