Skip to content
/ YABR Public

Java bytecode toolkit: procedural API, SSA IR, optimization transforms, mutable AST API, decompilation.

License

Notifications You must be signed in to change notification settings

Tonic-Box/YABR

Repository files navigation

YABR - Yet Another Bytecode Reader/Writer

A Java bytecode manipulation library with SSA-form intermediate representation for analysis and optimization.

A comprehensive alternative to ASM, Javassist, and other bytecode libraries.

Features

  • Class file parsing - Read and write Java .class files
  • 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+

Quick Start

// 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();

Documentation

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

Examples

Runnable examples are in src/main/java/com/tonic/demo/:

  • TestBlocks.java - SSA IR block visitor
  • TestBytecodeVisitor.java - Bytecode-level visitor
  • TestClassCreation.java - Creating classes programmatically
  • TestSSADemo.java - Complete SSA transformation
  • ASTMutationDemo.java - AST recovery, mutation, and recompilation
  • SourceASTDemo.java - AST node construction and source emission
  • ast/Decompile.java - Command-line class decompiler
  • CallGraphDemo.java - Build and query method call graph
  • DependencyDemo.java - Analyze class dependencies
  • TypeInferenceDemo.java - Type and nullability inference
  • PatternSearchDemo.java - Search for code patterns

Bytecode Generation

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();

SSA Pipeline

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 lower

The 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);

Class Decompilation

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 --fqn

Analysis APIs

YABR 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 detection

See Analysis APIs for complete documentation.

Installation

Gradle (JitPack)

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.

Building

./gradlew build

Requirements

  • Java 11+

Acknowledgements

Inspired by classpooly.

License

MIT

Ask DeepWiki

About

Java bytecode toolkit: procedural API, SSA IR, optimization transforms, mutable AST API, decompilation.

Topics

Resources

License

Stars

Watchers

Forks

Languages