55. javap
public class zt.asm.deg.Items {
public java.util.List<java.lang.Integer> ids;
public int getId(int);
Code:
0: aload_0
1: getfield #4
4: iload_1
5: invokeinterface #8, 2
10: checkcast #9
13: invokevirtual #10
16: ireturn
}
56. Groovyfied
public class zt/asm/Items {
public Ljava/util/List; ids
@groovyx.ast.bytecode.Bytecode
public int getId(int a) {
aload 0
getfield zt.asm.Items.ids >> List
iload 1
invokeinterface List.get(int) >> Object
checkcast Integer
invokevirtual Integer.intValue() >> int
ireturn
}
}
https://github.com/melix/groovy-bytecode-ast
57. Generating bytecode
from scratch
is too simple …
Transforming the bytecode is
much more fun!
60. How?
• Add –javaagent to hook into class loading
process
• Implement ClassFileTransformer
• Use bytecode manipulation libraries
(Javassist, cglib, asm) to add any custom logic
java.lang.instrument
61. How ? (2)
• Use custom ClassLoader
– Override ClassLoader#findClass
– Use ClassReader(String) to read the class
in and transform it via visitor chain
– Call ClassLoader#defineClass explicitly
with the result from the transformation
step
Programs are composed of classes, classes are thebytecode, blah blahblahJava bytecode is stack based: most of the operations push to the stack of consume values from the stack
ObjectWeb ASM is the de-facto standard decomposing, modifying, and recomposing Javabytecode. ASM provides a simple library that exposes the internal aggregate components of a given Java class through its visitor oriented API. On top of visitor API ASM provides tree API that represents classes as object constructs.ASM is very popular and is used widely in various Java open-source projects. In fact, this could be the primary motivation for someone the learn this library.
Why would learn Java bytecode and ASM at all?Well, creating a brand new JVM language might be a good idea This is what all the cool kids are doing, right?Secondly – programming model that is exposed by many frameworks is backed with bytecode generation or instrumentation. AspectJ, for instance uses bytecode instrumentation extensively.Also, there are some many awesome tools that do awesome stuff, like …. JRebel for instance
This presentation gives some pointe
Let’s start with an example: the class “Items” implements an interface,…
Includes a field declaration & assignment,..
Initializer block, ..
and a virtual method.
The most common scenario to generate bytecode that corresponds to the example source, is to create ClassWriter, visit the structure – fields, methods, etc, and after the job is done, write out the final bytes.
Actually, before going further, let’s mention javap– the Java class disassembler. The output of javap command isn’t particularly useful as there’s no way to modify it and compile back to the executable code. However, it is much more easier to read the bytecodes produced by javap, rather than the code that is written using ASM API. So, javap is good for reference when studying the bytecode.
Let’s go on and construct a ClassWriter.. The constructor takes anint which is composed of different flags.COMPUTE_MAXS says that the sizes of local variables and operand stack parts will be computed automatically. Still have to call visitMaxs with any argumentsCOMPUTE_FRAMES – everything is computed automatically. ASM will compute the stack map, still have to call visitMaxs
To create class signature, we’ll call ClassWriter#visit methodThe interface is full of constants, as one may notice.
Even this API call, not that complex, but looks ugly
Field declaration with assignment is actually split between the field declaration for the class, and assignment is performed in constructor. So this is something we need to keep in mind when generating the code.
fisitField method creates the field declaration
The ugliest part of ASM API is the descriptiors. Sorry, I’d like to rant the library a bit on this one
Fortunately, Type can help a bit with all this descriptor mess
Let’s generate some methods nowFirst of all, the constructor. There isn’t a special API to generate a constructor, only the special name, <init>, tells that the method is actually a constructor.
The real method isn’t any different from the constructor from the API point of view
Now it is time to generate the constructor body
The constructor consists of super() callField assignmentSome method calls
The super() call for the regular class is the call to Obect.<init>
Assigning a value to a field takes a series of opcodes to complete
At first the value has to be created and placed on the stack
Next,putfield, putstatic or xstoreopcode can be used to assign the value to the required location
The ASM code doesn’t look very different to the javap-like code, besides all the noise it brings
After the field is initialized, let’s add some values to the list: a byte, a short and an integer
Adding the values to the list results in method calls with a parameter. The parameter has to be loaded to the stack in order to be passed to a method.As the autoboxing takes place, Integer.valueOf is called in order to add an Integer to the List
The ASM code is getting uglier… and the captain doesn’t like it.
Let’s take a step back and select a shortcut for the taks
ASM code can be easily generated
... using ASMifierClassVisitor (ASMifier in ASM4). The output is rather noisy, with all the labels,but the noise is easy to remove.
However,javap output is still the cleanest and easiest to read and there are some alternatives to ASM visitor API
For instance, Groovy AST transformations make it possible to write code which is very close to javap output.