Video and slides synchronized, mp3 and slide download available at URL http://bit.ly/1jtTJtd.
Sponsored by New Relic. Ashley Puls introduces three common byte code manipulation frameworks: ASM, CGLib, and Javassist, providing enough detail to get one started. Filmed at qconnewyork.com.
Ashley Puls is a senior software engineer at New Relic Inc. which provides an all-in one web application performance tool. She works on the Java Agent team in Portland which focuses on instrumenting Java applications.
1 of 138
More Related Content
Living in the Matrix with Bytecode Manipulation
1. QCon NY 2014
Living in the Matrix with
Bytecode Manipulation
Wednesday, June 11, 14
2. InfoQ.com: News & Community Site
• 750,000 unique visitors/month
• Published in 4 languages (English, Chinese, Japanese and Brazilian
Portuguese)
• Post content from our QCon conferences
• News 15-20 / week
• Articles 3-4 / week
• Presentations (videos) 12-15 / week
• Interviews 2-3 / week
• Books 1 / month
Watch the video with slide
synchronization on InfoQ.com!
http://www.infoq.com/presentations
/asm-cglib-javassist
3. Presented at QCon New York
www.qconnewyork.com
Purpose of QCon
- to empower software development by facilitating the spread of
knowledge and innovation
Strategy
- practitioner-driven conference designed for YOU: influencers of
change and innovation in your teams
- speakers and topics driving the evolution and innovation
- connecting and catalyzing the influencers and innovators
Highlights
- attended by more than 12,000 delegates since 2007
- held in 9 cities worldwide
6. Outline
• What is bytecode?
• Why manipulate bytecode?
• What frameworks can be used to
manipulate bytecode?
• Examine two frameworks in depth
Wednesday, June 11, 14
7. What is Java bytecode?
.java file .class file
Java compiler
(javac)
Class Loader
Bytecode
Verifier
Java Runtime
System
Native OS
Java Virtual Machine
Source: http://www.techlila.com/write-programs-linux/
Wednesday, June 11, 14
8. What is Java bytecode?
.java file .class file
Java compiler
(javac)
Class Loader
Bytecode
Verifier
Java Runtime
System
Native OS
Java Virtual Machine
Source: http://www.techlila.com/write-programs-linux/
Wednesday, June 11, 14
9. What is Java bytecode?
.java file .class file
Java compiler
(javac)
Class Loader
Bytecode
Verifier
Java Runtime
System
Native OS
Java Virtual Machine
Source: http://www.techlila.com/write-programs-linux/
Instruction set of the Java Virtual Machine
Wednesday, June 11, 14
10. Why learn about Java bytecode
manipulation frameworks ?
Wednesday, June 11, 14
11. Why learn about Java bytecode
manipulation frameworks ?
• A bytecode manipulation framework is likely already
on your stack
• Spring
• Hibernate
• Groovy
• Clojure
• Eclipse
• JRuby
Wednesday, June 11, 14
12. • A bytecode manipulation framework is likely already
on your stack
• Spring
• Hibernate
• Groovy
• Clojure
• Eclipse
• JRuby
• Its really fun!!!
Why learn about Java bytecode
manipulation frameworks ?
Wednesday, June 11, 14
13. Why learn about Java bytecode
manipulation frameworks ?
• program analysis
• find bugs in code
• examine code complexity
• generate classes
• proxies
• remove access to certain APIs
• compiler for another language like Scala
• transform classes without Java source code
• profilers
• optimization and obfuscation
• additional logging
Wednesday, June 11, 14
14. Why learn about Java bytecode
manipulation frameworks ?
• program analysis
• find bugs in code
• examine code complexity
• generate classes
• proxies
• remove access to certain APIs
• compiler for another language like Scala
• transform classes without Java source code
• profilers
• optimization and obfuscation
• additional logging
Wednesday, June 11, 14
18. Logging
Audit Log: a message should be
logged every time an important
method is called
Source: http://www.vforteachers.com/About_NetSupport.htm
Wednesday, June 11, 14
20. Logging
public class BankTransactions {
public static void main(String[] args) {
BankTransactions bank = new BankTransactions();
// login and add i dollars to account
for (int i = 0; i < 100; i++) {
String accountId = "account" + i;
bank.login("password", accountId, "Ashley");
bank.unimportantProcessing(accountId);
bank.finalizeTransaction(accountId, Double.valueOf(i));
}
System.out.println(“Transactions completed”);
}
}
Wednesday, June 11, 14
21. Logging
public class BankTransactions {
public static void main(String[] args) {
BankTransactions bank = new BankTransactions();
// login and add i dollars to each account
for (int i = 0; i < 100; i++) {
String accountId = "account" + i;
bank.login("password", accountId, "Ashley");
bank.unimportantProcessing(accountId);
bank.finalizeTransaction(accountId, Double.valueOf(i));
}
System.out.println(“Transactions completed”);
}
}
Wednesday, June 11, 14
22. Logging
/**
* A method annotation which should be used to indicate important methods whose
* invocations should be logged.
*
*/
public @interface ImportantLog {
/**
* The method parameter indexes whose values should be logged. For example,
* if we have the method hello(int paramA, int paramB, int paramC), and we
* wanted to log the values of paramA and paramC, then fields would be ["0",
* "2"]. If we only want to log the value of paramB, then fields would be
* ["1"].
*/
String[] fields();
}
Wednesday, June 11, 14
23. Logging
public void login(String password, String accountId, String userName) {
// login logic
}
public void finalizeTransaction(String accountId, Double moneyToAdd) {
// transaction logic
}
Wednesday, June 11, 14
25. Logging
@ImportantLog(fields = { "1", "2" })
public void login(String password, String accountId, String userName) {
// login logic
}
@ImportantLog(fields = { "0", "1" })
public void finalizeTransaction(String accountId, Double moneyToAdd) {
// transaction logic
}
A call was made to method "login" on class "com/example/qcon/mains/BankTransactions".
Important params:
Index 1 value: ${accountId}
Index 2 value: ${userName}
Wednesday, June 11, 14
26. Logging
@ImportantLog(fields = { "1", "2" })
public void login(String password, String accountId, String userName) {
// login logic
}
@ImportantLog(fields = { "0", "1" })
public void finalizeTransaction(String accountId, Double moneyToAdd) {
// transaction logic
}
A call was made to method "finalizeTransaction" on class "com/example/qcon/
mains/BankTransactions".
Important params:
Index 0 value: ${accountId}
Index 1 value: ${moneyToAdd}
A call was made to method "login" on class "com/example/qcon/mains/BankTransactions".
Important params:
Index 1 value: ${accountId}
Index 2 value: ${userName}
Wednesday, June 11, 14
27. Java Agent
• Ability to modify the bytecode without modifying the
application source code
• Added in Java 1.5
• Docs: http://docs.oracle.com/javase/8/docs/api/
java/lang/instrument/package-summary.html
Wednesday, June 11, 14
28. Typical Java Process
JVM
public static void main(String[] args)
BankTransactions.class
Classloader
java com/example/qcon/mains/BankTransactions
Transactions completed
Wednesday, June 11, 14
42. lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements
ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println(" Loading class: " + className);
return null;
}
}
Wednesday, June 11, 14
43. lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements
ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
// manipulate the bytes here and then return them
return null;
}
}
Wednesday, June 11, 14
49. Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
ArrayList
HashMap
CtClass: represents a compile time class
Wednesday, June 11, 14
50. Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
CtClass: represents a compile time class
Wednesday, June 11, 14
51. Javassist
CtClass: represents a compile time class
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
ByteArrayClassPath: converts a byte[] to a CtClass
Wednesday, June 11, 14
52. Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
Wednesday, June 11, 14
53. Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
BankTrans
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
Wednesday, June 11, 14
54. Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
BankTrans
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
CtClass bank = Classpool.getDefault().get(“BankTrans”);
Wednesday, June 11, 14
55. Javassist
ClassPool: container of CtClass objects
ArrayList
HashMap
BankTrans
ArrayList
HashMap
BankTrans
ByteArrayClassPath: converts a byte[] to a CtClass
CtClass: represents a compile time class
BankTrans
Classpool.getDefault().insertClassPath(new ByteArrayClassPath(“BankTrans”, classBytes);
CtClass bank = Classpool.getDefault().get(“BankTrans”);
Wednesday, June 11, 14
58. Javassist
CtConstructor: represents a compile time constructor
init
BankTrans
CtClass: represents a compile time class
CtMethod: represents a compile time class
login
process
Wednesday, June 11, 14
59. Javassist
CtConstructor: represents a compile time constructor
init
CtClass: represents a compile time class
CtMethod: represents a compile time method
CtMethod loginMethod = loginCtClass.getDeclaredMethod(“login”);
BankTrans
login
process
Wednesday, June 11, 14
60. Javassist
CtConstructor: represents a compile time constructor
init
CtClass: represents a compile time class
CtMethod: represents a compile time method
CtMethod loginMethod = loginCtClass.getDeclaredMethod(“login”);
login
BankTrans
login
process
Wednesday, June 11, 14
73. lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
return null;
}
}
Wednesday, June 11, 14
74. lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
return null;
}
}
Wednesday, June 11, 14
75. lets code
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] cfbuffer) throws IllegalClassFormatException {
1. convert byte array to a ct class object
2. check each method of ct class for annotation @ImportantLog
3. if @ImportantLog annotation present on method, then
a. get important method parameter indexes
b. add logging statement to beginning of the method
return null;
}
Wednesday, June 11, 14
76. lets code
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] cfbuffer) throws IllegalClassFormatException {
pool.insertClassPath(new ByteArrayClassPath(className,
classfileBuffer));
CtClass cclass = pool.get(className.replaceAll("/", "."));
2. check each method of ct class for annotation @ImportantLog
3. if @ImportantLog annotation present on method, then
a. get important method parameter indexes
b. add logging statement to beginning of the method
return null;
}
Wednesday, June 11, 14
77. lets code
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] cfbuffer) throws IllegalClassFormatException {
pool.insertClassPath(new ByteArrayClassPath(className,
classfileBuffer));
CtClass cclass = pool.get(className.replaceAll("/", "."));
if (!cclass.isFrozen()) {
for (CtMethod currentMethod : cclass.getDeclaredMethods()) {
Annotation annotation = getAnnotation(currentMethod);
3. if @ImportantLog annotation present on method, then
a. get important method parameter indexes
b. add logging statement to beginning of the method
}
}
return null;
}
Wednesday, June 11, 14
78. lets code
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] cfbuffer) throws IllegalClassFormatException {
pool.insertClassPath(new ByteArrayClassPath(className,
classfileBuffer));
CtClass cclass = pool.get(className.replaceAll("/", "."));
if (!cclass.isFrozen()) {
for (CtMethod currentMethod : cclass.getDeclaredMethods()) {
Annotation annotation = getAnnotation(currentMethod);
if (annotation != null) {
List<String> parameterIndexes = getParamIndexes(annotation);
b. add logging statement to beginning of the method
}
}
}
return null;
}
Wednesday, June 11, 14
81. lets code
private Annotation getAnnotation(CtMethod method) {
MethodInfo mInfo = method.getMethodInfo();
// the attribute we are looking for is a runtime invisible attribute
// use Retention(RetentionPolicy.RUNTIME) on the annotation to make it
// visible at runtime
AnnotationsAttribute attInfo = (AnnotationsAttribute) mInfo
.getAttribute(AnnotationsAttribute.invisibleTag);
if (attInfo != null) {
// this is the type name meaning use dots instead of slashes
return attInfo.getAnnotation("com.example.qcon.mains.ImportantLog");
}
return null;
}
Wednesday, June 11, 14
90. Javassist
• Positives
• Simplicity
• Do not have to write actual bytecode
• Decent documentation
• Negatives
• Generally slower than ASM
• Less functionality
Wednesday, June 11, 14
91. ASM
• Released in Open Source in 2002
• Actively updated
• Version 5.0.3 released May 2014
• Two ASM libraries
• Event based (SAX like)
• Visitor design pattern
• Object based (DOM like)
• Documentation: http://download.forge.objectweb.org/
asm/asm4-guide.pdf
Wednesday, June 11, 14
95. ASM
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
ClassVisitor:
event filter
Wednesday, June 11, 14
96. ASM
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitField
visitMethod
ClassVisitor:
event filter
Wednesday, June 11, 14
97. ASM
ClassVisitor: delegates class events, event filter
visitAttribute visitField visitMethod visitInnerClass visitEnd
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitField
visitMethod
ClassVisitor:
event filter
Wednesday, June 11, 14
98. ASM
ClassVisitor: delegates class events, event filter
visitAttribute visitField visitMethod visitInnerClass visitEnd
ClassWriter: produces output byte[]
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitField
visitMethod
BankTrans
ClassVisitor:
event filter
Wednesday, June 11, 14
99. lets code
package com.example.qcon.agent;
import java.lang.instrument.Instrumentation;
public class Agent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Starting the agent");
inst.addTransformer(new ImportantLogClassTransformer());
}
}
Wednesday, June 11, 14
100. lets code
package com.example.qcon.agent;
import java.lang.instrument.Instrumentation;
public class Agent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Starting the agent");
inst.addTransformer(new ImportantLogClassTransformer());
}
}
Wednesday, June 11, 14
101. lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
return null;
}
}
Wednesday, June 11, 14
102. lets code
package com.example.qcon.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
public class ImportantLogClassTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
//TODO
return null;
}
}
Wednesday, June 11, 14
103. lets code
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
cr.accept(cw, 0);
return cw.toByteArray();
}
Wednesday, June 11, 14
104. lets code
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = new LogMethodClassVisitor(cw, className);
cr.accept(cv, 0);
return cw.toByteArray();
}
Wednesday, June 11, 14
105. ASM
ClassVisitor: delegates class events, event filter
visitAttribute visitField visitMethod visitInnerClass visitEnd
ClassWriter: produces output byte[]
ClassReader: given a byte[], parses a compiled class
ClassReader:
event producer
BankTrans
ClassWriter:
event consumer
visitMethod
BankTrans
LogMethod
ClassVisitor:
event filter
Wednesday, June 11, 14
114. lets code
public class LogMethodClassVisitor extends ClassVisitor {
private String className;
public LogMethodIfAnnotationVisitor(ClassVisitor cv, String pClassName) {
super(Opcodes.ASM5, cv);
className = pClassName;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature,
exceptions);
return new PrintMessageMethodVisitor(mv, name, className);
}
}
Wednesday, June 11, 14
115. lets code
public class PrintMessageMethodVisitor extends MethodVisitor {
private String methodName;
private String className;
private boolean isAnnotationPresent;
private List<String> parameterIndexes;
public PrintMessageMethodVisitor(MethodVisitor mv, String methodName,
String className) {
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
}
@Override
public void visitCode() {
}
}
Wednesday, June 11, 14
116. lets code
public class PrintMessageMethodVisitor extends MethodVisitor {
private String methodName;
private String className;
private boolean isAnnotationPresent;
private List<String> parameterIndexes;
public PrintMessageMethodVisitor(MethodVisitor mv, String methodName,
String className) {
// initialize instance variables
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
1. check method for annotation @ImportantLog
2. if annotation present, then get important method param indexes
}
@Override
public void visitCode() {
3. if annotation present, add logging to beginning of the method
}
}
Wednesday, June 11, 14
117. lets code
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if ("Lcom/example/qcon/mains/ImportantLog;".equals(desc)) {
isAnnotationPresent = true;
return new AnnotationVisitor(Opcodes.ASM5,
super.visitAnnotation(desc, visible)) {
public AnnotationVisitor visitArray(String name) {
if (“fields”.equals(name)) {
return new AnnotationVisitor(Opcodes.ASM5,
super.visitArray(name)) {
public void visit(String name, Object value) {
parameterIndexes.add((String) value);
super.visit(name, value);
}
};
} else {
return super.visitArray(name);
}
}
};
}
return super.visitAnnotation(desc, visible);
}
Wednesday, June 11, 14
118. lets code
public class PrintMessageMethodVisitor extends MethodVisitor {
private String methodName;
private String className;
private boolean isAnnotationPresent;
private List<String> parameterIndexes;
public PrintMessageMethodVisitor(MethodVisitor mv, String methodName,
String className) {
// initialize instance variables
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
1. check method for annotation @ImportantLog
2. if annotation present, then get important method param indexes
}
@Override
public void visitCode() {
3. if annotation present, add logging statement to beginning of the method
}
}
Wednesday, June 11, 14
119. lets code
public void visitCode() {
if (isAnnotationPresent) {
// create string builder
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
// add everything to the string builder
mv.visitLdcInsn("A call was made to method "");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
"java/lang/StringBuilder", "<init>",
"(Ljava/lang/String;)V", false);
mv.visitLdcInsn(methodName);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder", "append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
. . .
Wednesday, June 11, 14
120. What is Java bytecode?
use javap to examine the bytecode
Wednesday, June 11, 14
121. What is Java bytecode?
public class PrintMessage {
private String methodName;
private String className;
public PrintMessage(String mName, String cName) {
methodName = mName;
className = cName;
}
public void print() {
StringBuilder sb = new StringBuilder();
sb.append("A call was made to method "");
sb.append(methodName);
sb.append("" on class "");
sb.append(className);
sb.append("".");
System.out.println(sb.toString());
}
Wednesday, June 11, 14
122. What is Java bytecode?
javap -c bin/com/example/qcon/mains/PrintMessage
Wednesday, June 11, 14
123. What is Java bytecode?
javap -c bin/com/example/qcon/mains/PrintMessage
public void print();
Code:
0: new #38 // class java/lang/StringBuilder
3: dup
4: invokespecial #40 // Method java/lang/
StringBuilder."<init>":()V
7: astore_1
8: aload_1
9: ldc #41 // String A call was made to method "
11: invokevirtual #43 // Method java/lang/
StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: pop
15: aload_1
16: aload_0
17: getfield #14 // Field methodName:Ljava/lang/String;
20: invokevirtual #43 // Method java/lang/
StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
. . .
Wednesday, June 11, 14
124. What is Java bytecode?
each line represents one JVM instruction
public void print();
Code:
0: new #38 // class java/lang/StringBuilder
3: dup
4: invokespecial #40 // Method java/lang/
StringBuilder."<init>":()V
7: astore_1
8: aload_1
9: ldc #41 // String A call was made to method "
11: invokevirtual #43 // Method java/lang/
StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: pop
15: aload_1
16: aload_0
17: getfield #14 // Field methodName:Ljava/lang/String;
20: invokevirtual #43 // Method java/lang/
StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
. . .
Wednesday, June 11, 14
125. What is Java bytecode?
JVM instruction = 1 opcode with 0 or more operands
public void print();
Code:
0: new #38 // class java/lang/StringBuilder
3: dup
4: invokespecial #40 // Method java/lang/
StringBuilder."<init>":()V
7: astore_1
8: aload_1
9: ldc #41 // String A call was made to method "
11: invokevirtual #43 // Method java/lang/
StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: pop
15: aload_1
16: aload_0
17: getfield #14 // Field methodName:Ljava/lang/String;
20: invokevirtual #43 // Method java/lang/
StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
. . .
Wednesday, June 11, 14
126. What is Java bytecode?
Opcode Description
load
load a reference onto the stack from a local variable
iload, lload, fload, dload, aload
store
stores a value from the stack into a local variable
istore, lstore, fstore, dstore, astore
lcd push a constant from a constant pool onto the stack
return
method return instruction
ireturn, lreturn, freturn, dreturn, areturn, return
invokevirtual invoke an instance method on an object
invokespecial
invokes an instance method requiring special handling
such as an initialization method
See the spec for more info: http://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf
Wednesday, June 11, 14
127. What is Java bytecode?
Opcode Description
invokestatic invoke a class (static) method on a named class
athrow throws an exception
new create a new class
newarray create a new array
if<cond>
branch if int comparison with 0 succeeds
ifeq, ifne, iflt, ifge, ifgt, ifle
dup duplicate the top operand stack value
See the spec for more info: http://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf
Wednesday, June 11, 14
131. lets code
public void visitCode() {
if (isAnnotationPresent) {
// create string builder
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
// add everything to the string builder
mv.visitLdcInsn("A call was made to method "");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
"java/lang/StringBuilder", "<init>",
"(Ljava/lang/String;)V", false);
mv.visitLdcInsn(methodName);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/StringBuilder", "append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
. . .
Wednesday, June 11, 14
132. ASM
• Positives
• Speed and Size
• Decent documentation
• Depth of Functionality
• Number of people using framework
• Negatives
• Need to write actual byte code
• longer developer ramp-up
Wednesday, June 11, 14
133. Why learn about Java bytecode
manipulation frameworks ?
• program analysis
• find bugs in code
• examine code complexity
• generate classes
• proxies
• remove access to certain APIs
• compiler for another language like Scala
• transform classes without Java source code
• profilers
• optimization and obfuscation
• additional logging
Wednesday, June 11, 14