A Java bytecode manipulation library with SSA-form intermediate representation for analysis and optimization.
A comprehensive alternative to ASM, Javassist, and other bytecode libraries.
- Class file parsing - Read and write Java
.classfiles - Bytecode generation - Fluent builder API for creating classes from scratch
- Bytecode manipulation - High-level and low-level APIs for modifying bytecode
- SSA IR system - Lift bytecode to SSA form, optimize, and lower back
- Source AST system - Recover, mutate, and emit Java source from bytecode
- Class decompilation - Full class decompilation with imports, fields, and methods
- Analysis APIs - Call graph, dependency, type inference, pattern search, xrefs, data flow, similarity, PDG, SDG, CPG, taint analysis
- Visitor patterns - Traverse classes at multiple abstraction levels
- Frame computation - Automatic StackMapTable generation for Java 7+
// Load a class
ClassPool pool = ClassPool.getDefault();
ClassFile cf = pool.loadClass(inputStream);
// Create a new class
int access = new AccessBuilder().setPublic().build();
ClassFile newClass = pool.createNewClass("com/example/MyClass", access);
// Add a field with getter/setter
FieldEntry field = newClass.createNewField(access, "value", "I", new ArrayList<>());
newClass.generateGetter(field, false);
newClass.generateSetter(field, false);
// Write the class
newClass.rebuild();
byte[] bytes = newClass.write();| Guide | Description |
|---|---|
| Quick Start | Get running in 5 minutes |
| Architecture | System overview and design |
| Class Files | ClassPool, ClassFile, ConstPool |
| Bytecode API | Bytecode manipulation |
| Generation API | Fluent bytecode generation from scratch |
| Visitors | Traversal patterns |
| SSA Guide | SSA intermediate representation |
| SSA Transforms | Optimizations and analysis |
| SSA IR Migration | API changes from SSA IR redesign |
| Analysis APIs | High-level code analysis and semantic queries |
| PDG API | Program Dependence Graph with slicing |
| SDG API | Interprocedural System Dependence Graph |
| CPG API | Code Property Graph with taint analysis |
| AST Guide | Source-level AST recovery, mutation, and emission |
| AST Editor | ExprEditor-style AST transformation |
| Renamer API | Class, method, and field renaming |
| Frame Computation | StackMapTable generation |
Runnable examples are in src/main/java/com/tonic/demo/:
TestBlocks.java- SSA IR block visitorTestBytecodeVisitor.java- Bytecode-level visitorTestClassCreation.java- Creating classes programmaticallyTestSSADemo.java- Complete SSA transformationASTMutationDemo.java- AST recovery, mutation, and recompilationSourceASTDemo.java- AST node construction and source emissionast/Decompile.java- Command-line class decompilerCallGraphDemo.java- Build and query method call graphDependencyDemo.java- Analyze class dependenciesTypeInferenceDemo.java- Type and nullability inferencePatternSearchDemo.java- Search for code patterns
import com.tonic.builder.ClassBuilder;
import com.tonic.type.AccessFlags;
byte[] bytes = ClassBuilder.create("com/example/Calculator")
.version(AccessFlags.V11, 0)
.access(AccessFlags.ACC_PUBLIC)
.addMethod(AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC, "add", "(II)I")
.code()
.iload(0)
.iload(1)
.iadd()
.ireturn()
.end()
.end()
.toByteArray();YABR includes a full SSA transformation system:
Bytecode -> Lift -> SSA IR -> Optimize -> Lower -> Bytecode
| ^
[Recover] [Lower]
| |
v |
AST ----[Mutate]----> AST
SSA ssa = new SSA(constPool)
.withConstantFolding()
.withCopyPropagation()
.withDeadCodeElimination();
ssa.transform(method); // Lift, optimize, and lowerThe AST path enables source-level transformations:
// Recover AST from IR
BlockStmt ast = MethodRecoverer.recoverMethod(irMethod, method);
System.out.println(SourceEmitter.emit(ast)); // Print as Java source
// Mutate and recompile
ast.getStatements().forEach(stmt -> { /* modify */ });
new ASTLowerer(constPool).replaceBody(ast, irMethod);
ssa.lower(irMethod, method);Decompile entire class files to Java source with imports:
import com.tonic.analysis.source.decompile.ClassDecompiler;
// Simple one-liner
String source = ClassDecompiler.decompile(classFile);
// With configuration
SourceEmitterConfig config = SourceEmitterConfig.builder()
.useFullyQualifiedNames(false) // Use simple names + imports
.alwaysUseBraces(true)
.build();
String source = ClassDecompiler.decompile(classFile, config);Command-line usage:
java -cp build/classes/java/main com.tonic.demo.ast.Decompile MyClass.class
java -cp build/classes/java/main com.tonic.demo.ast.Decompile MyClass.class --fqnYABR provides high-level APIs for code analysis:
// Call Graph - find method callers/callees
CallGraph cg = CallGraph.build(classPool);
Set<MethodReference> callers = cg.getCallers(methodRef);
// Dependency Analysis - track class dependencies
DependencyAnalyzer deps = new DependencyAnalyzer(classPool);
Set<String> dependencies = deps.getDependencies("com/example/MyClass");
List<List<String>> cycles = deps.findCircularDependencies();
// Type Inference - nullability analysis
TypeInferenceAnalyzer types = new TypeInferenceAnalyzer(irMethod);
types.analyze();
Nullability nullState = types.getNullability(ssaValue);
// Pattern Search - find code patterns
PatternSearch search = new PatternSearch(classPool)
.inAllMethodsOf(classFile)
.withCallGraph();
List<SearchResult> results = search.findMethodCalls("java/io/PrintStream", "println");
// Cross-References - track all references to/from symbols
XrefDatabase xrefs = new XrefBuilder(classPool).build();
Set<Xref> callers = xrefs.getRefsToMethod(methodRef); // Who calls this?
Set<Xref> outgoing = xrefs.getRefsFromClass(className); // What does this reference?
// Data Flow - build flow graphs for taint analysis
DataFlowGraph dfg = new DataFlowGraph(irMethod);
dfg.build();
Set<DataFlowNode> reachable = dfg.getReachableNodes(paramNode);
// Method Similarity - find duplicates and similar methods
MethodSimilarityAnalyzer similarity = new MethodSimilarityAnalyzer(classPool);
similarity.buildIndex();
List<SimilarityResult> duplicates = similarity.findDuplicates();
List<SimilarityResult> renamed = similarity.findRenamedCopies(); // Obfuscation detectionSee Analysis APIs for complete documentation.
Add JitPack repository and the dependency to your build.gradle or build.gradle.kts:
Kotlin DSL (build.gradle.kts):
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
dependencies {
implementation("com.github.Tonic-Box:YABR:main-SNAPSHOT")
}Groovy DSL (build.gradle):
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.Tonic-Box:YABR:main-SNAPSHOT'
}For specific releases or commit hashes, see JitPack - YABR.
./gradlew build- Java 11+
Inspired by classpooly.
MIT