The Cafebabe Bytecode Generation Library
IMPORTANT: This page is now outdated. Cafebabe has moved to GitHub and so has the documentation.
The main Cafebabe page is here.
Full examples are available here.
Abstract Byte Codes
Loading constant values
Constant values can be pushed on the stack in several different ways. Integers in particular can be pushed using the various ICONST_n codes, BIPUSH, SIPUSH and LDC or LDC_W for larger values recovered from the stack.
The abstract bytecodes:
- Ldc(arg)
…can be used instead. It can take as parameter an integer, a float, a double, a long or a string, and will generate the appropriate code. For instance:
codeHandler << Ldc(1.0f)
…is equivalent to adding the bytecode FCONST_0, and:
codeHandler << Ldc(35042)
…will add the value 35042 to the constant pool and emit the appropriate LDC operation.
Loading and storing locals
All Java bytecodes XLOAD, XLOAD_n, XSTORE and XSTORE_n where X is one of { A, D, F, I, L } and n is in 0…3 can be replaced by their more general counterparts XLoad(n) and XStore(n), where the appropriate instructions are automatically generated (shortcut, standard or “wide” mode).
IInc
- IInc(varIndex, increment)
…can be used instead of the standard bytecode IINC. It automatically switches to the “wide” mode when the increment or the variable index exceed one-byte values.
Accessing fields
Accessing fields is achieved through the following abstract bytecodes:
- GetField(className, fieldName, fieldType)
- PutField(className, fieldName, fieldType)
- GetStatic(className, fieldName, fieldType)
- PutStatic(className, fieldName, fieldType)
The correspondence to Java bytecodes should be obvious. The operands must be prepared on the stack as for the actual bytecodes. All names are given as strings. Thus, to put the value 42 in a static field answer
of type int in a class Universe
of a package questions
, for instance, we write:
codeHandler << Ldc(42) << PutStatic("questions/Universe", "answer", "I")
Note that the field type must be encoded according to the convention for type descriptors in the JVM (see top of this page). The class name corresponds to the fully qualified Java name for the class, where dots are substituted by slashes.
Method calls
The abstract bytecodes:
- InvokeVirtual(className, methodName, methodSignature),
- InvokeStatic(className, methodName, methodSignature) and
- InvokeSpecial(className, methodName, methodSignature)
…can be used to call methods. The receiver (for non-static methods) and the arguments must be prepared on the stack as for the corresponding bytecodes, but all relevant information will be added to the constant pool automatically. Again, the method signature must be encoded following the JVM convention. Run javap -c -v
on some .class
files to see examples, or read this page.
Creating objects and arrays
There is an abstract byte code to create new instances of classes using their default constructor:
- DefaultNew(className) creates and initializes a new object of class className and leaves it on the stack
Similarly for arrays:
- NewArray(className) creates a new array containing objects of type className (the size has to be on top of the stack)
- NewArray(primitiveTypeCode) creates a new array of a primitive type. The integer primitiveTypeCode should correspond to the codes specified in the JVM for the NEWARRAY instruction.
Labels and control flow
All control instructions have equivalent abstract bytecodes which refer to their target using strings rather than offsets. Labels can be inserted which are, well, labeled with these strings. For example, the following:
codeHandler << Ldc(42) << Goto("after") << POP << Ldc(41) << Label("After") << IRETURN
…will generate code for a method which always returns 42, as the control flow “jumps over” the instructions which would replace 42 by 41.
The exhaustive list of control flow abstract bytecodes is given here. The correspondence with actual Java bytecodes should be obvious.
- Goto(target)
- IfEq(target)
- IfNe(target)
- IfLt(target)
- IfLe(target)
- IfGt(target)
- IfGe(target)
- IfNull(target)
- IfNonNull(target)
- If_ICmpEq(target)
- If_ICmpNe(target)
- If_ICmpLt(target)
- If_ICmpLe(target)
- If_ICmpGt(target)
- If_ICmpGe(target)
- If_ACmpEq(target)
- If_ACmpNe(target)