<- Back to README | Class Files | Generation API | Visitors ->
YABR provides two APIs for bytecode manipulation: the high-level Bytecode class and the low-level CodeWriter class.
The Bytecode class provides a convenient, high-level API for common bytecode operations.
// From a MethodEntry
Bytecode bytecode = new Bytecode(methodEntry);
// From a CodeWriter
Bytecode bytecode = new Bytecode(codeWriter);// Reference types (objects)
bytecode.addALoad(0); // Load 'this' (for instance methods)
bytecode.addALoad(1); // Load first reference parameter
// Integers
bytecode.addILoad(0); // Load int from local 0
bytecode.addILoad(1); // Load int from local 1
// Other primitives
bytecode.addLLoad(2); // Load long from local 2
bytecode.addFLoad(3); // Load float from local 3
bytecode.addDLoad(4); // Load double from local 4
// Generic load based on descriptor
bytecode.addLoad(1, "I"); // int
bytecode.addLoad(1, "J"); // long
bytecode.addLoad(1, "Ljava/lang/String;"); // object// Integers
bytecode.addIConst(0); // iconst_0
bytecode.addIConst(5); // iconst_5
bytecode.addIConst(100); // bipush 100
bytecode.addIConst(1000); // sipush 1000
bytecode.addIConst(100000); // ldc (from constant pool)
// Longs
bytecode.addLConst(0L); // lconst_0
bytecode.addLConst(1L); // lconst_1
bytecode.addLConst(100L); // ldc2_w
// Floats
bytecode.addFConst(0.0f); // fconst_0
bytecode.addFConst(1.0f); // fconst_1
bytecode.addFConst(2.0f); // fconst_2
bytecode.addFConst(3.14f); // ldc
// Doubles
bytecode.addDConst(0.0); // dconst_0
bytecode.addDConst(1.0); // dconst_1
bytecode.addDConst(2.718); // ldc2_w
// Null
bytecode.addAConstNull(); // aconst_null
// Strings
bytecode.addLdc("Hello, World!");bytecode.addAStore(1); // Store reference to local 1
bytecode.addIStore(2); // Store int to local 2ConstPool cp = bytecode.getConstPool();
// Using field reference index
int fieldRef = cp.getIndexOf(cp.findOrAddField("com/example/MyClass", "myField", "I"));
bytecode.addGetField(fieldRef);
bytecode.addPutField(fieldRef);
// Static fields
bytecode.addGetStatic(fieldRef);
bytecode.addPutStatic(fieldRef);
// Using strings (adds to constant pool automatically)
bytecode.addGetStatic("java/lang/System", "out", "Ljava/io/PrintStream;");// Using method reference index
int methodRef = cp.getIndexOf(cp.findOrAddMethodRef(classIdx, natIdx));
bytecode.addInvokeVirtual(methodRef);
bytecode.addInvokeSpecial(methodRef); // constructors, super calls
bytecode.addInvokeStatic(methodRef);
// Using strings
bytecode.addInvokeVirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V");
bytecode.addInvokeStatic("java/lang/Math", "max", "(II)I");
// Interface methods
bytecode.addInvokeInterface(interfaceMethodRef, argCount);
// Dynamic invocation
bytecode.addInvokeDynamic(cpIndex);// New instance
int classRef = cp.getIndexOf(cp.findOrAddClass("java/lang/StringBuilder"));
bytecode.addNew(classRef);// Labels
bytecode.defineLabel("loop_start");
// Goto
bytecode.addGoto("loop_start");
bytecode.addGotoW("loop_start"); // Wide goto for large offsets
// Increment local variable
bytecode.addIInc(0, 1); // local[0]++
bytecode.addIInc(1, -1); // local[1]--// Using ReturnType enum
bytecode.addReturn(ReturnType.RETURN); // void
bytecode.addReturn(ReturnType.IRETURN); // int
bytecode.addReturn(ReturnType.LRETURN); // long
bytecode.addReturn(ReturnType.FRETURN); // float
bytecode.addReturn(ReturnType.DRETURN); // double
bytecode.addReturn(ReturnType.ARETURN); // reference
// Using opcode
bytecode.addReturn(0xB1); // return
bytecode.addReturn(0xAC); // ireturn
// From descriptor
bytecode.addReturn(ReturnType.fromDescriptor("I")); // ireturn
bytecode.addReturn(ReturnType.fromDescriptor("V")); // returnBy default, instructions are appended to the end. You can insert before existing code:
bytecode.setInsertBefore(true);
bytecode.setInsertBeforeOffset(0); // Insert at beginning
// Now instructions will be inserted at the beginning
bytecode.addGetStatic("java/lang/System", "out", "Ljava/io/PrintStream;");
bytecode.addLdc("Method entry");
bytecode.addInvokeVirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V");After making changes, finalize to write back to the method:
bytecode.finalizeBytecode();
// Check if modifications were made
if (bytecode.isModified()) {
// Optionally compute stack frames
bytecode.computeFrames();
}For more control, use CodeWriter directly:
CodeWriter cw = new CodeWriter(methodEntry);
// Get existing instructions
List<Instruction> instructions = cw.getInstructions();
// Insert at specific offset
cw.insertInvokeVirtual(offset, methodRefIndex);
cw.insertGetStatic(offset, fieldRefIndex);
cw.insertALoad(offset, varIndex);
cw.insertILoad(offset, varIndex);
cw.insertIInc(offset, varIndex, increment);
cw.insertLDC(offset, cpIndex);
cw.insertLDCW(offset, cpIndex);
// Append instructions
cw.appendInstruction(instruction);
cw.insertInstruction(offset, instruction);
// Get bytecode info
int size = cw.getBytecodeSize();
boolean endsWithReturn = cw.endsWithReturn();
// Write changes
cw.write();
// Frame computation
cw.computeFrames();
cw.forceComputeFrames();// System.out.println("Hello");
bytecode.addGetStatic("java/lang/System", "out", "Ljava/io/PrintStream;");
bytecode.addLdc("Hello");
bytecode.addInvokeVirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V");// super();
bytecode.addALoad(0); // this
int superInit = cp.getIndexOf(cp.findOrAddMethodRef(
cp.getIndexOf(cp.findOrAddClass("java/lang/Object")),
cp.getIndexOf(cp.findOrAddNameAndType(
cp.getIndexOf(cp.findOrAddUtf8("<init>")),
cp.getIndexOf(cp.findOrAddUtf8("()V"))
))
));
bytecode.addInvokeSpecial(superInit);// this.field = value;
bytecode.addALoad(0); // this
bytecode.addILoad(1); // value parameter
bytecode.addPutField(fieldRef);
// return this.field;
bytecode.addALoad(0); // this
bytecode.addGetField(fieldRef);
bytecode.addReturn(ReturnType.IRETURN);// MyClass.staticField = 42;
bytecode.addIConst(42);
bytecode.addPutStatic(fieldRef);
// int x = MyClass.staticField;
bytecode.addGetStatic(fieldRef);
bytecode.addIStore(1);YABR supports all JVM instructions through the com.tonic.analysis.instruction package:
| Category | Examples |
|---|---|
| Constants | IConstInstruction, LConstInstruction, LdcInstruction |
| Loads | ALoadInstruction, ILoadInstruction, LLoadInstruction |
| Stores | AStoreInstruction, IStoreInstruction, LStoreInstruction |
| Stack | DupInstruction, PopInstruction, SwapInstruction |
| Math | IAddInstruction, ISubInstruction, IMulInstruction |
| Conversion | I2LInstruction, L2IInstruction, I2FInstruction |
| Comparison | IfInstruction, IfIcmpInstruction, IfAcmpInstruction |
| Control | GotoInstruction, ReturnInstruction, SwitchInstruction |
| Reference | GetFieldInstruction, PutFieldInstruction, NewInstruction |
| Invoke | InvokeVirtualInstruction, InvokeStaticInstruction |
After modifying bytecode, you may need to recompute StackMapTable frames:
// Via Bytecode
bytecode.computeFrames();
// Via CodeWriter
codeWriter.computeFrames();
// Via ClassFile (all methods)
classFile.computeFrames();
// Via ClassFile (specific method)
classFile.computeFrames("methodName", "(II)I");<- Back to README | Class Files | Generation API | Visitors ->