SlideShare ist ein Scribd-Unternehmen logo
1 von 67
Supercharging Reflective Libraries 
with InvokeDynamic 
Ian Robertson 
Myriad Genetics
About Me 
● Application Architect at Myriad Genetics 
● 14 years experience of working on large-scale Java projects 
● Coauthor of Pojomatic – generates toString, equals and 
hashCode 
● Amateur carpenter 
● Blog: http://www.artima.com/weblogs/?blogger=ianr 
● Twitter: @nainostrebor 
● https://github.com/irobertson
Pojomatic 
● Generates equals, hashCode and toString methods 
@Override public toString() { return Pojomatic.toString(this); } 
● Version 1: reflection. 
– Fast, but slower than hand-coded equals 
– Considered bytecode generation, but then we couldn't access private 
fields. 
● Version 2: Bytecode generation with InvokeDynamic 
– Almost an order of magnitude improvement in performance!
Agenda 
● Review reflection 
● Compare reflection to hand-crafted code 
● See how ASM-generated can compete with hand-crafted code 
● See how invokeDynamic can bypass access restrictions 
● Tips and tricks
(Sub)Standard Json 
● Don't bother with quotes 
● Allow for a trailing comma
(Sub)Standard Json 
public interface Jsonifier<T> { 
String marshal(T instance); 
} 
public class Bean { 
@Json("prop1") int i; 
String s; 
@Json("prop2") String getS() { return s; } 
} 
jsonifier.marshal(bean) → 
{ prop1: 3, prop2: hello, }
Old-School Reflection 
p u blic class ReflectionJsonifier<T> 
implements Jsonifier<T> { 
public String marshal(T instance) { 
StringBuilder sb = new StringBuilder(); 
Class<T> = instance.getClass(); 
sb.append(“{”); 
… 
sb.append(“}”); 
return sb.toString(); 
} 
}
Old-School Reflection - fields 
f o r (Field f: clazz.getDeclaredFields()) { 
Json json = f.getAnnotation(Json.class); 
if (json != null) { 
sb.append(json.value() + “: “); 
f.setAccessible(true); 
sb.append(f.get(instance)); 
sb.append(”, ”); 
} 
}
Old-School Reflection - methods 
f o r (Method m: clazz.getDeclaredMethods()) { 
Json json = m.getAnnotation(Json.class); 
if (json != null) { 
sb.append(json.value() + “: “); 
m.setAccessible(true); 
sb.append(m.invoke(instance)); 
sb.append(“, ”); 
} 
}
Misc improvements 
● Do reflection once on marshaler instantiation 
● Reuse marshalers 
● Save the costs of reflection, but not reflective invocation 
● If a field type is primitive, use field.getInt(instance) instead of 
autoboxing field.get(instance);
Hand-coded 
p u blic class BeanJsonifier 
implements Jsonifier<Bean> { 
@Override 
public String marshal(Bean bean) { 
return "{prop1: " + bean.i 
+ ", prop2: " + bean.getS() + “}”; 
} 
}
Benchmarks (ns/call) 
Reflection 
HandRolled 
88 
516
REFLECTION 
Y U SO SLOW?!
Callsite Methods 
● Callsite: foo.bar() at a particular location 
● Monomorphic – One implementation for bar() at the callsite 
– Completely inlinable 
● Bimorphic – Two implementations for bar() at the callsite 
– Inlinable with an instanceof check 
● Megamorphic – 3+ implementations for bar() at the callsite 
– Virtual function call, so no inlining (hence no further optimizations)
Reflection Tends to be Megamorphic 
● A frequently used Field object will eventually generate bytecode 
to do it's get work, placing this in a new class implementing 
sun.reflect.FieldAccessor (Oracle JDK) 
● FieldAccessor.get() will be megamorphic 
– No inlining 
– No optimization 
– No happiness 
● Analogous story for Method.invoke
Bytecode Generation 
● Instead of having the JVM generate bytecode for the individual 
fields and methods, generate the entire marshal(T instance) 
method ourselves at runtime 
● Use ObjectWeb's ASM library to generate bytecode 
● Resulting code necessarily runs as fast as hand-rolled code 
● One small hitch: 
– Who speaks bytecode?
JVM Bytecode 
● Stack based language (not unlike assembly) 
● Local variables (including parameters) 
● Arithmetic 
● Tests, conditional jumps 
● Field access 
● Method invocation 
– Interface, Virtual, Static, “Special”, Dynamic
ASM 
● http://asm.ow2.org/ 
● A Java library for reading and generating Java bytecode at 
runtime 
● Uses the visitor pattern, both for reading and for writing 
bytecode 
● Doesn't make it easy to write bytecode, but does make it 
feasible
ASMifier - ASM for Dummies 
● ASM tool which reads bytecode, generates java code to 
generate bytecode using ASM 
● Command line: 
java ­classpath 
asm­all. 
jar  
org.objectweb.asm.util.ASMifier  
json/Foo.class 
● Eclipse – Bytecode Outline Plugin (http://asm.ow2.org/eclipse/)
ASMifier example 
Convert this: 
package json; 
public class Foo { 
public String bar() { 
return "hello"; 
} 
} 
Into this:
public static byte[] dump() throws Exception { 
ClassWriter cw = new ClassWriter(0); 
FieldVisitor fv; 
MethodVisitor mv; 
AnnotationVisitor av0; 
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "json/Foo", null, 
"java/lang/Object", null); 
cw.visitSource("Foo.java", null); 
{ 
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 
mv.visitCode(); 
mv.visitVarInsn(ALOAD, 0); 
mv.visitMethodInsn( 
INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); 
mv.visitInsn(RETURN); 
mv.visitMaxs(1, 1); 
mv.visitEnd(); 
} 
{ 
mv = cw.visitMethod( 
ACC_PUBLIC, "bar", "()Ljava/lang/String;", null, null); 
mv.visitCode(); 
mv.visitLdcInsn("hello"); 
mv.visitInsn(ARETURN); 
mv.visitMaxs(1, 1); 
mv.visitEnd(); 
} 
cw.visitEnd(); 
return cw.toByteArray(); 
} 
}
public static byte[] dump() throws Exception { 
ClassWriter cw = new ClassWriter(0); 
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "json/Foo", 
null, "java/lang/Object", null); 
cw.visitSource("Foo.java", null); 
generateConstructor(cw); 
generateBar(cw); 
cw.visitEnd(); 
return cw.toByteArray(); 
} 
private static void generateConstructor(ClassWriter cw) { 
MethodVisitor mv = cw.visitMethod( 
ACC_PUBLIC, "<init>", "()V", null, null); 
mv.visitCode(); 
mv.visitVarInsn(ALOAD, 0); 
mv.visitMethodInsn( 
INVOKESPECIAL, "java/lang/Object", "<init>", "()V",false); 
mv.visitInsn(RETURN); 
mv.visitMaxs(1, 1); 
mv.visitEnd(); 
} 
private static void generateBar(ClassWriter cw) { 
MethodVisitor mv = cw.visitMethod( 
ACC_PUBLIC, "bar", "()Ljava/lang/String;", null, null); 
mv.visitCode(); 
mv.visitLdcInsn("hello"); 
mv.visitInsn(ARETURN); 
mv.visitMaxs(1, 1); 
mv.visitEnd(); 
} 
}
ASMifier Example – top level 
p u blic static byte[] dump() throws Exception { 
ClassWriter cw = new ClassWriter(0); 
cw.visit(V1_8, ACC_PUBLIC,"json/Foo", 
null, "java/lang/Object", null); 
generateConstructor(cw); 
generateBar(cw); 
cw.visitEnd(); 
return cw.toByteArray(); 
}
ASMifier Example - method 
p r ivate static void generateBar(ClassWriter cw) { 
MethodVisitor mv = cw 
.visitMethod(ACC_PUBLIC, "bar", "()Ljava/lang/String;", null, null); 
mv.visitCode(); 
mv.visitLdcInsn("hello"); 
mv.visitInsn(ARETURN); 
mv.visitMaxs(1, 1); 
mv.visitEnd(); 
}
Converting Bytecode Into a Class 
● Need a ClassLoader! 
public final class ByteClassLoader extends ClassLoader { 
public ByteClassLoader(ClassLoader parent) { 
super(parent); 
} 
Class<?> loadClass(String name, byte[] bytes) { 
return defineClass(name, bytes, 0, bytes.length); 
} 
}
Hello World, Bytecode style 
byte[] bytes = dump(); 
ByteClassLoader cl = new ByteClassLoader( 
getClass().getClassLoader()); 
Class<?> c = cl.loadClass(“json/Foo”, bytes); 
Object foo = c.newInstance(); 
Method m = c.getDeclaredMethod(“bar”); 
Object result = m.invoke(foo); 
System.out.println(result);
Recall 
p u blic class BeanJsonifier 
implements Jsonifier<Bean> { 
@Override 
public String marshal(Bean bean) { 
Return "{prop1: " + bean.i 
+ ", prop2: " + bean.s() + “}”; 
} 
}
Implement an Interface 
cw.visit( 
V1_8, 
ACC_PUBLIC, 
"json/BeanJsonifier", 
"Ljava/lang/Object;” + 
“Ljson/Jsonifier<Ljson/Bean;>;", 
"java/lang/Object", 
new String[] { "json/Jsonifier" } 
);
What we write 
public String marshal(Bean bean) { 
Return "{prop1: " + bean.i 
+ ", prop2: " + bean.s() + “}”; 
}
What the Compiler Sees 
public String marshal(Bean bean) { 
return new StringBuilder("{prop1: ") 
.append(bean.i) 
.append(", prop2: ") 
.append(bean.s()) 
.append(“}”) 
.toString(); 
}
Marshal method – Construct StringBuilder 
mv.visitTypeInsn( 
NEW, "java/lang/StringBuilder"); 
mv.visitInsn(DUP); 
mv.visitLdcInsn("{prop1: "); 
mv.visitMethodInsn(INVOKESPECIAL, 
"java/lang/StringBuilder", "<init>", 
"(Ljava/lang/String;)V", false); 
// note – StringBuilder instance is still 
// on the stack, due to DUP
Marshal method – Construct StringBuilder 
mv.visitTypeInsn( 
NEW, "java/lang/StringBuilder"); 
mv.visitInsn(DUP); 
mv.visitLdcInsn("prop1: "); 
mv.visitMethodInsn(INVOKESPECIAL, 
"java/lang/StringBuilder", "<init>", 
"(Ljava/lang/String;)V", false); 
// note – StringBuilder instance is still 
// on the stack, due to DUP
Marshal method – Construct StringBuilder 
mv.visitTypeInsn( 
NEW, ".../StringBuilder"); 
mv.visitInsn(DUP); 
mv.visitLdcInsn("prop1: "); 
mv.visitMethodInsn(INVOKESPECIAL, 
".../StringBuilder", "<init>", 
"(L.../String;)V", false); 
// note – StringBuilder instance is still 
// on the stack, due to DUP
Marshal method – Construct StringBuilder 
mv.visitTypeInsn(NEW, ".../StringBuilder"); 
mv.visitInsn(DUP); 
mv.visitLdcInsn("prop1: "); 
mv.visitMethodInsn(INVOKESPECIAL, 
".../StringBuilder", "<init>", 
"(L.../String;)V", false); 
// note – StringBuilder instance is still 
// on the stack, due to DUP
Marshal method – Append Field i 
mv.visitVarInsn(ALOAD, 1); // the passed bean 
mv.visitFieldInsn( 
GETFIELD, // what to do 
"json/Bean", // class holding the field 
"i", // field name 
"I"); // field type (integer) 
mv.visitMethodInsn(INVOKEVIRTUAL, 
".../StringBuilder", "append", 
"(I)L.../StringBuilder;", false);
Marshal method – append method getS() 
mv.visitVarInsn(ALOAD, 1); 
mv.visitMethodInsn( 
INVOKEVIRTUAL, // method invocation type 
"json/Bean", // target class 
"get // method name 
"()L.../String;", // method signature 
false); // not an interface 
mv.visitMethodInsn(INVOKEVIRTUAL, 
".../StringBuilder", "append", 
"(L.../String;)L.../StringBuilder;", false);
Marshal method – finish up 
mv.visitMethodInsn( 
INVOKEVIRTUAL, ".../StringBuilder", 
"toString", "()L.../String;", false); 
mv.visitInsn(ARETURN);
Bridge Methods 
● Jsonifier<T> has method 
String marshal(T instance) 
● After erasure, this is equivalent to 
String marshal(Object instance) 
● But we wrote 
String marshal(Bean instance) 
● Compiler generates a bridge method
Marshal Bridge Method 
MethodVisitor mv = cw.visitMethod( 
ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC, 
"marshal", "(L.../Object;)L.../String;", 
null, null); 
// ... 
mv.visitVarInsn(ALOAD, 0); 
mv.visitVarInsn(ALOAD, 1); 
mv.visitTypeInsn(CHECKCAST, "json/Bean"); 
mv.visitMethodInsn( 
INVOKEVIRTUAL, "json/BeanMarshaller", 
"marshal", "(json/Bean;)L.../String;", false); 
mv.visitInsn(ARETURN);
Marshal Bridge Method 
MethodVisitor mv = cw.visitMethod( 
ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC, 
"marshal", "(L.../Object;)L.../String;", 
null, null); 
// ... 
mv.visitVarInsn(ALOAD, 0); 
mv.visitVarInsn(ALOAD, 1); 
mv.visitTypeInsn(CHECKCAST, "json/Bean"); 
mv.visitMethodInsn( 
INVOKEVIRTUAL, "json/BeanMarshaller", 
"marshal", "(json/Bean;)L.../String;", false); 
mv.visitInsn(ARETURN);
Constructing an Instance 
ByteClassLoader loader = new ByteClassLoader( 
Bean.class.getClassLoader()); 
byte[] classBytes = dump(); 
Class<?> jsonifierClass = loader.loadClass( 
“json/BeanJsonifier”, classBytes); 
Jsonifier<Bean> jsonifier = (Jsonifier<Bean>) 
jsonifierClass.newInstance(); 
jsonifier.marshal(foo);
Trouble in Paradise 
public class Bean { 
@Json("prop1") 
public int i; 
String s; 
@Json("prop2") 
public String getS() { return s; } 
}
Trouble in Paradise 
public class Bean { 
@Json("prop1") 
private int i; 
String s; 
@Json("prop2") 
private String getS() { return s; } 
}
Trouble in Paradise 
● IllegalAccessError 
– tried to access field json.Bean.i from class json.BeanMarshaller 
● Didn't happen back in the early days (as late as some versions 
of Java 5) 
● Newer Java checks all bytecode for conformance 
● Reflection can call setIsAccessible 
● But reflection is slow!
InvokeDynamic 
● Originally added to support dynamic languages, but has found 
use far beyond that 
– Lambdas use it! 
● First time it's invoked, effectively replaces itself with the 
resulting call 
● Allows for excellent inlining, on par with handwritten code! 
● Not available from java code – only bytecode 
● ASMifier cannot help us here :(
InvokeDynamic flow 
● Class calls invokeDynamic, pointing to a bootstrap method... 
● Which creates a MethodHandle... 
● Which is wrapped in a CallSite... 
● Which henceforth is run in place of the invokeDynamic call
Bootstrap Method 
● Takes arguments of type 
– MethodHandles.Lookup lookup – used for looking up MethodHandles 
– String name – the method name 
– MethodType methodType – the type of method to create 
– Any Bootstrap method constant arguments 
● Returns a CallSite 
– Wrapper around a MethodHandle
MethodHandles 
● Typically constructors, method invocations or field access 
● A MethodHandle instance comes from the Lookup passed into the 
bootstrap method 
● Lookup can see only what the calling class can see... 
● But that includes reflection! 
● Lookup.unreflect converts a java.lang.reflect.Method 
instance 
● Lookup.unreflectGetter converts a j.l.r.Field into a field 
access call site
Call Sites 
● A wrapper around a Method Handle 
● Can be mutable or constant 
● If constant, the JVM can effectively replace the invokeDynamic 
opcode with the wrapped method handle 
– Allows for inlining and subsequent optimizations
Field Access Bootstrap Method 
public static CallSite invokeField( 
MethodHandles.Lookup lookup, 
String name, 
MethodType type, 
Class<?> owner, String fieldName) { 
Field f = owner.getDeclaredField(fieldName); 
f.setAccessible(true); 
MethodHandle mh = lookup.unreflectGetter(f); 
return new ConstantCallSite(mh); 
}
Method Call Bootstrap Method 
public static CallSite invokeMethod( 
MethodHandles.Lookup lookup, 
String name, 
MethodType type, 
Class<?> owner, String methodName) { 
Method m = owner.getDeclaredMethod(methodName); 
m.setAccessible(true); 
MethodHandle mh = lookup.unreflect(m); 
return new ConstantCallSite(mh); 
}
Calling InvokeDynamic 
● The invokeDynamic JVM instruction is called with: 
– Method name (passed to the bootstrap method) 
– Method descriptor for that which will replace the invokeDynamic call 
– Bootstrap method 
– Bootstrap method constant arguments (0 or more) 
● Primitive 
● String 
● Class 
● Method/field reference
Calling invokeDynamic for Field “i” 
mv.visitInvokeDynamicInsn( 
"i", // name for “method” 
"(Ljson/Bean;)I", // signature: int i(Bean) 
new Handle( // method handle 
H_INVOKESTATIC, // static method 
"json/Bootstraps", // class name 
"getField", // bootstrap method 
bootstrapSignature), // signature (TBD) 
"Ljson/Bean;", "i"); // bootstrap const args 
// mv.visitFieldInsn(GETFIELD, "json/Bean", "i", "I");
Calling invokeDynamic for Method “s” 
mv.visitInvokeDynamicInsn( 
"s", 
“(Ljson/Bean;)java/lang/String”, 
new Handle( 
H_INVOKESTATIC, 
"json/Bootstraps", 
"invokeMethod", 
bootstrapSignature), 
“Ljson/Bean;”, "getS"); 
// mv.visitMethodInsn(INVOKE_VIRTUAL, "json/Bean", "s", 
// "()L.../String;", false);
bootstrapSignature 
String bootstrapSignature = MethodType.methodType( 
CallSite.class, 
Lookup.class, 
String.class, 
MethodType.class, 
Class.class, // first bootstrap method argument 
String.class)// second bootstrap method argument 
.toMethodDescriptorString(); 
// “(L.../MethodHandles$Lookup;L...;L.../MethodType;L.../Class;L.../String;)” 
// + “L.../CallSite;”
Still to do 
● Instead of building class for Bean.class, do it dynamically, 
based on (one-time) reflection 
● Left as exercise to reader... 
for (Field f: clazz.getDeclaredFields()) { 
Json json = f.getAnnotation(Json.class); 
if (json != null) { 
generateByteCodeForAnnotatedField(f); 
} 
}
Benchmarks (ns/call) 
Reflection 
HandRolled 
Asm 
88 
84 
516
Benchmarks (ns/call) 
Reflection 
HandRolled 
Asm 
88 
84 
516 
Your millage 
may vary...
Benchmarks (ns/call) 
Reflection 
HandRolled 
Asm 
Asm w/o nulls 
302 
88 
84 
516
Tips and Tricks
CheckClassAdapter 
● Can wrap ASM ClassWriter to catch some errors at bytecode 
generation time instead of at runtime 
● Gives much more meaningful error messages 
● If using COMPUTE_MAXS to ask ASM to compute size for you, 
then first generate bytecode, wrap CheckClassAdapter around 
a ClassReader
Line Numbers 
● cw.visitSource(“Look at JsonifierBytecodeGenerator”) 
● Label label = new Label(); 
mv.visitLabel(label); 
mv.visitLineNumber(200 + fieldNumber) 
● Resulting stack trace can be very useful in diagnosing bugs 
● Also can declare local variables to allow step-in debugging! 
– mv.visitLocalVariable(...)
Bootstrap Method Arguments 
● Many things cannot be passed directly to a bootstrap method 
– Notably – non-public classes 
– Can pass a reference to public class with static Class variables 
holding the class reference 
● The bootstrap method can also be part of the generated class 
– Probably more pain than its worth
SecurityManager 
● Run reflection and bytecode generation via 
AccessController.doPrivileged(PrivilegedAction) 
● Test using java.security.Policy.setPolicy 
– Set policy should override implies(ProtectionDomain,Permission) 
● Make sure to allow your test framework to do stuff! 
– Also call System.setSecurityManager(new SecurityManager());
Testing 
● Any generated bytecode needs extensive unit testing 
● Test what security permissions are required 
● Test different types of classes – public, private, inner, inherited, 
etc 
● Test different types of properties – primitives, Objects, arrays, 
nested arrays, etc 
● Test for synthetic methods 
● Test stack traces for line numbers
Don't Overdo it! 
● Benchmark your code before optimizing at all 
● Write as much code as possible directly in Java 
– Call out from bytecode using invokeVirtual
Thank you! 
● http://www.slideshare.net/nainostrebor/ 
● https://github.com/irobertson/invokedynamic-talk-code 
● Don't forget to fill out surveys!!!

Weitere ähnliche Inhalte

Was ist angesagt?

How to Begin Developing Ruby Core
How to Begin Developing Ruby CoreHow to Begin Developing Ruby Core
How to Begin Developing Ruby CoreHiroshi SHIBATA
 
JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015Charles Nutter
 
Understanding Asynchronous JavaScript
Understanding Asynchronous JavaScriptUnderstanding Asynchronous JavaScript
Understanding Asynchronous JavaScriptjnewmanux
 
Practical Testing of Ruby Core
Practical Testing of Ruby CorePractical Testing of Ruby Core
Practical Testing of Ruby CoreHiroshi SHIBATA
 
JavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJWORKS powered by Ordina
 
A Re-Introduction to JavaScript
A Re-Introduction to JavaScriptA Re-Introduction to JavaScript
A Re-Introduction to JavaScriptSimon Willison
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryTatsuhiko Miyagawa
 
mruby で mackerel のプラグインを作るはなし
mruby で mackerel のプラグインを作るはなしmruby で mackerel のプラグインを作るはなし
mruby で mackerel のプラグインを作るはなしHiroshi SHIBATA
 
Deep Dive async/await in Unity with UniTask(EN)
Deep Dive async/await in Unity with UniTask(EN)Deep Dive async/await in Unity with UniTask(EN)
Deep Dive async/await in Unity with UniTask(EN)Yoshifumi Kawai
 
History & Practices for UniRx(EN)
History & Practices for UniRx(EN)History & Practices for UniRx(EN)
History & Practices for UniRx(EN)Yoshifumi Kawai
 
Rapid web development using tornado web and mongodb
Rapid web development using tornado web and mongodbRapid web development using tornado web and mongodb
Rapid web development using tornado web and mongodbikailan
 
Voxxed Days Vilnius 2015 - Having fun with Javassist
Voxxed Days Vilnius 2015 - Having fun with JavassistVoxxed Days Vilnius 2015 - Having fun with Javassist
Voxxed Days Vilnius 2015 - Having fun with JavassistAnton Arhipov
 

Was ist angesagt? (20)

How to Begin Developing Ruby Core
How to Begin Developing Ruby CoreHow to Begin Developing Ruby Core
How to Begin Developing Ruby Core
 
JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015JRuby 9000 - Taipei Ruby User's Group 2015
JRuby 9000 - Taipei Ruby User's Group 2015
 
Understanding Asynchronous JavaScript
Understanding Asynchronous JavaScriptUnderstanding Asynchronous JavaScript
Understanding Asynchronous JavaScript
 
Practical Testing of Ruby Core
Practical Testing of Ruby CorePractical Testing of Ruby Core
Practical Testing of Ruby Core
 
Java script for web developer
Java script for web developerJava script for web developer
Java script for web developer
 
Practical ngx_mruby
Practical ngx_mrubyPractical ngx_mruby
Practical ngx_mruby
 
JavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UXJavaScript Basics and Best Practices - CC FE & UX
JavaScript Basics and Best Practices - CC FE & UX
 
Plack - LPW 2009
Plack - LPW 2009Plack - LPW 2009
Plack - LPW 2009
 
A Re-Introduction to JavaScript
A Re-Introduction to JavaScriptA Re-Introduction to JavaScript
A Re-Introduction to JavaScript
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
mruby で mackerel のプラグインを作るはなし
mruby で mackerel のプラグインを作るはなしmruby で mackerel のプラグインを作るはなし
mruby で mackerel のプラグインを作るはなし
 
Intro to Sail.js
Intro to Sail.jsIntro to Sail.js
Intro to Sail.js
 
Deep Dive async/await in Unity with UniTask(EN)
Deep Dive async/await in Unity with UniTask(EN)Deep Dive async/await in Unity with UniTask(EN)
Deep Dive async/await in Unity with UniTask(EN)
 
History & Practices for UniRx(EN)
History & Practices for UniRx(EN)History & Practices for UniRx(EN)
History & Practices for UniRx(EN)
 
Javascript
JavascriptJavascript
Javascript
 
Tatsumaki
TatsumakiTatsumaki
Tatsumaki
 
Rapid web development using tornado web and mongodb
Rapid web development using tornado web and mongodbRapid web development using tornado web and mongodb
Rapid web development using tornado web and mongodb
 
Voxxed Days Vilnius 2015 - Having fun with Javassist
Voxxed Days Vilnius 2015 - Having fun with JavassistVoxxed Days Vilnius 2015 - Having fun with Javassist
Voxxed Days Vilnius 2015 - Having fun with Javassist
 
Java Script Best Practices
Java Script Best PracticesJava Script Best Practices
Java Script Best Practices
 

Ähnlich wie Supercharging Libraries with InvokeDynamic

JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)Piyush Katariya
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinMarvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinJava User Group Latvia
 
Invokedynamic / JSR-292
Invokedynamic / JSR-292Invokedynamic / JSR-292
Invokedynamic / JSR-292ytoshima
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsPlaying With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsMike Hagedorn
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing UpDavid Padbury
 
Node js
Node jsNode js
Node jshazzaz
 
Writing JavaScript for C# Blazor.pptx
Writing JavaScript for C# Blazor.pptxWriting JavaScript for C# Blazor.pptx
Writing JavaScript for C# Blazor.pptxEd Charbeneau
 
Why Nodejs Guilin Shanghai
Why Nodejs Guilin ShanghaiWhy Nodejs Guilin Shanghai
Why Nodejs Guilin ShanghaiJackson Tian
 
Why Node.js
Why Node.jsWhy Node.js
Why Node.jsguileen
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascriptDoeun KOCH
 
How to Write Node.js Module
How to Write Node.js ModuleHow to Write Node.js Module
How to Write Node.js ModuleFred Chien
 
A few good JavaScript development tools
A few good JavaScript development toolsA few good JavaScript development tools
A few good JavaScript development toolsSimon Kim
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous JavascriptGarrett Welson
 
Javascript Everywhere
Javascript EverywhereJavascript Everywhere
Javascript EverywherePascal Rettig
 

Ähnlich wie Supercharging Libraries with InvokeDynamic (20)

Mastering Java ByteCode
Mastering Java ByteCodeMastering Java ByteCode
Mastering Java ByteCode
 
JavaScript (without DOM)
JavaScript (without DOM)JavaScript (without DOM)
JavaScript (without DOM)
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey BuzdinMarvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
 
Invokedynamic / JSR-292
Invokedynamic / JSR-292Invokedynamic / JSR-292
Invokedynamic / JSR-292
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.jsPlaying With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.js
 
Nashorn
NashornNashorn
Nashorn
 
RxSwift to Combine
RxSwift to CombineRxSwift to Combine
RxSwift to Combine
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Node js
Node jsNode js
Node js
 
Writing JavaScript for C# Blazor.pptx
Writing JavaScript for C# Blazor.pptxWriting JavaScript for C# Blazor.pptx
Writing JavaScript for C# Blazor.pptx
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
Why Nodejs Guilin Shanghai
Why Nodejs Guilin ShanghaiWhy Nodejs Guilin Shanghai
Why Nodejs Guilin Shanghai
 
Why Node.js
Why Node.jsWhy Node.js
Why Node.js
 
Advanced javascript
Advanced javascriptAdvanced javascript
Advanced javascript
 
How to Write Node.js Module
How to Write Node.js ModuleHow to Write Node.js Module
How to Write Node.js Module
 
A few good JavaScript development tools
A few good JavaScript development toolsA few good JavaScript development tools
A few good JavaScript development tools
 
RxSwift to Combine
RxSwift to CombineRxSwift to Combine
RxSwift to Combine
 
Intro to Asynchronous Javascript
Intro to Asynchronous JavascriptIntro to Asynchronous Javascript
Intro to Asynchronous Javascript
 
Let's JavaScript
Let's JavaScriptLet's JavaScript
Let's JavaScript
 
Javascript Everywhere
Javascript EverywhereJavascript Everywhere
Javascript Everywhere
 

Kürzlich hochgeladen

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxOnBoard
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 

Kürzlich hochgeladen (20)

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
Maximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptxMaximizing Board Effectiveness 2024 Webinar.pptx
Maximizing Board Effectiveness 2024 Webinar.pptx
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 

Supercharging Libraries with InvokeDynamic

  • 1. Supercharging Reflective Libraries with InvokeDynamic Ian Robertson Myriad Genetics
  • 2. About Me ● Application Architect at Myriad Genetics ● 14 years experience of working on large-scale Java projects ● Coauthor of Pojomatic – generates toString, equals and hashCode ● Amateur carpenter ● Blog: http://www.artima.com/weblogs/?blogger=ianr ● Twitter: @nainostrebor ● https://github.com/irobertson
  • 3. Pojomatic ● Generates equals, hashCode and toString methods @Override public toString() { return Pojomatic.toString(this); } ● Version 1: reflection. – Fast, but slower than hand-coded equals – Considered bytecode generation, but then we couldn't access private fields. ● Version 2: Bytecode generation with InvokeDynamic – Almost an order of magnitude improvement in performance!
  • 4. Agenda ● Review reflection ● Compare reflection to hand-crafted code ● See how ASM-generated can compete with hand-crafted code ● See how invokeDynamic can bypass access restrictions ● Tips and tricks
  • 5. (Sub)Standard Json ● Don't bother with quotes ● Allow for a trailing comma
  • 6. (Sub)Standard Json public interface Jsonifier<T> { String marshal(T instance); } public class Bean { @Json("prop1") int i; String s; @Json("prop2") String getS() { return s; } } jsonifier.marshal(bean) → { prop1: 3, prop2: hello, }
  • 7. Old-School Reflection p u blic class ReflectionJsonifier<T> implements Jsonifier<T> { public String marshal(T instance) { StringBuilder sb = new StringBuilder(); Class<T> = instance.getClass(); sb.append(“{”); … sb.append(“}”); return sb.toString(); } }
  • 8. Old-School Reflection - fields f o r (Field f: clazz.getDeclaredFields()) { Json json = f.getAnnotation(Json.class); if (json != null) { sb.append(json.value() + “: “); f.setAccessible(true); sb.append(f.get(instance)); sb.append(”, ”); } }
  • 9. Old-School Reflection - methods f o r (Method m: clazz.getDeclaredMethods()) { Json json = m.getAnnotation(Json.class); if (json != null) { sb.append(json.value() + “: “); m.setAccessible(true); sb.append(m.invoke(instance)); sb.append(“, ”); } }
  • 10. Misc improvements ● Do reflection once on marshaler instantiation ● Reuse marshalers ● Save the costs of reflection, but not reflective invocation ● If a field type is primitive, use field.getInt(instance) instead of autoboxing field.get(instance);
  • 11. Hand-coded p u blic class BeanJsonifier implements Jsonifier<Bean> { @Override public String marshal(Bean bean) { return "{prop1: " + bean.i + ", prop2: " + bean.getS() + “}”; } }
  • 12. Benchmarks (ns/call) Reflection HandRolled 88 516
  • 13. REFLECTION Y U SO SLOW?!
  • 14. Callsite Methods ● Callsite: foo.bar() at a particular location ● Monomorphic – One implementation for bar() at the callsite – Completely inlinable ● Bimorphic – Two implementations for bar() at the callsite – Inlinable with an instanceof check ● Megamorphic – 3+ implementations for bar() at the callsite – Virtual function call, so no inlining (hence no further optimizations)
  • 15. Reflection Tends to be Megamorphic ● A frequently used Field object will eventually generate bytecode to do it's get work, placing this in a new class implementing sun.reflect.FieldAccessor (Oracle JDK) ● FieldAccessor.get() will be megamorphic – No inlining – No optimization – No happiness ● Analogous story for Method.invoke
  • 16. Bytecode Generation ● Instead of having the JVM generate bytecode for the individual fields and methods, generate the entire marshal(T instance) method ourselves at runtime ● Use ObjectWeb's ASM library to generate bytecode ● Resulting code necessarily runs as fast as hand-rolled code ● One small hitch: – Who speaks bytecode?
  • 17. JVM Bytecode ● Stack based language (not unlike assembly) ● Local variables (including parameters) ● Arithmetic ● Tests, conditional jumps ● Field access ● Method invocation – Interface, Virtual, Static, “Special”, Dynamic
  • 18. ASM ● http://asm.ow2.org/ ● A Java library for reading and generating Java bytecode at runtime ● Uses the visitor pattern, both for reading and for writing bytecode ● Doesn't make it easy to write bytecode, but does make it feasible
  • 19. ASMifier - ASM for Dummies ● ASM tool which reads bytecode, generates java code to generate bytecode using ASM ● Command line: java ­classpath asm­all. jar org.objectweb.asm.util.ASMifier json/Foo.class ● Eclipse – Bytecode Outline Plugin (http://asm.ow2.org/eclipse/)
  • 20. ASMifier example Convert this: package json; public class Foo { public String bar() { return "hello"; } } Into this:
  • 21. public static byte[] dump() throws Exception { ClassWriter cw = new ClassWriter(0); FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; cw.visit(52, ACC_PUBLIC + ACC_SUPER, "json/Foo", null, "java/lang/Object", null); cw.visitSource("Foo.java", null); { mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn( INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } { mv = cw.visitMethod( ACC_PUBLIC, "bar", "()Ljava/lang/String;", null, null); mv.visitCode(); mv.visitLdcInsn("hello"); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } }
  • 22. public static byte[] dump() throws Exception { ClassWriter cw = new ClassWriter(0); cw.visit(52, ACC_PUBLIC + ACC_SUPER, "json/Foo", null, "java/lang/Object", null); cw.visitSource("Foo.java", null); generateConstructor(cw); generateBar(cw); cw.visitEnd(); return cw.toByteArray(); } private static void generateConstructor(ClassWriter cw) { MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn( INVOKESPECIAL, "java/lang/Object", "<init>", "()V",false); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } private static void generateBar(ClassWriter cw) { MethodVisitor mv = cw.visitMethod( ACC_PUBLIC, "bar", "()Ljava/lang/String;", null, null); mv.visitCode(); mv.visitLdcInsn("hello"); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); } }
  • 23. ASMifier Example – top level p u blic static byte[] dump() throws Exception { ClassWriter cw = new ClassWriter(0); cw.visit(V1_8, ACC_PUBLIC,"json/Foo", null, "java/lang/Object", null); generateConstructor(cw); generateBar(cw); cw.visitEnd(); return cw.toByteArray(); }
  • 24. ASMifier Example - method p r ivate static void generateBar(ClassWriter cw) { MethodVisitor mv = cw .visitMethod(ACC_PUBLIC, "bar", "()Ljava/lang/String;", null, null); mv.visitCode(); mv.visitLdcInsn("hello"); mv.visitInsn(ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); }
  • 25. Converting Bytecode Into a Class ● Need a ClassLoader! public final class ByteClassLoader extends ClassLoader { public ByteClassLoader(ClassLoader parent) { super(parent); } Class<?> loadClass(String name, byte[] bytes) { return defineClass(name, bytes, 0, bytes.length); } }
  • 26. Hello World, Bytecode style byte[] bytes = dump(); ByteClassLoader cl = new ByteClassLoader( getClass().getClassLoader()); Class<?> c = cl.loadClass(“json/Foo”, bytes); Object foo = c.newInstance(); Method m = c.getDeclaredMethod(“bar”); Object result = m.invoke(foo); System.out.println(result);
  • 27. Recall p u blic class BeanJsonifier implements Jsonifier<Bean> { @Override public String marshal(Bean bean) { Return "{prop1: " + bean.i + ", prop2: " + bean.s() + “}”; } }
  • 28. Implement an Interface cw.visit( V1_8, ACC_PUBLIC, "json/BeanJsonifier", "Ljava/lang/Object;” + “Ljson/Jsonifier<Ljson/Bean;>;", "java/lang/Object", new String[] { "json/Jsonifier" } );
  • 29. What we write public String marshal(Bean bean) { Return "{prop1: " + bean.i + ", prop2: " + bean.s() + “}”; }
  • 30. What the Compiler Sees public String marshal(Bean bean) { return new StringBuilder("{prop1: ") .append(bean.i) .append(", prop2: ") .append(bean.s()) .append(“}”) .toString(); }
  • 31. Marshal method – Construct StringBuilder mv.visitTypeInsn( NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("{prop1: "); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false); // note – StringBuilder instance is still // on the stack, due to DUP
  • 32. Marshal method – Construct StringBuilder mv.visitTypeInsn( NEW, "java/lang/StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("prop1: "); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V", false); // note – StringBuilder instance is still // on the stack, due to DUP
  • 33. Marshal method – Construct StringBuilder mv.visitTypeInsn( NEW, ".../StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("prop1: "); mv.visitMethodInsn(INVOKESPECIAL, ".../StringBuilder", "<init>", "(L.../String;)V", false); // note – StringBuilder instance is still // on the stack, due to DUP
  • 34. Marshal method – Construct StringBuilder mv.visitTypeInsn(NEW, ".../StringBuilder"); mv.visitInsn(DUP); mv.visitLdcInsn("prop1: "); mv.visitMethodInsn(INVOKESPECIAL, ".../StringBuilder", "<init>", "(L.../String;)V", false); // note – StringBuilder instance is still // on the stack, due to DUP
  • 35. Marshal method – Append Field i mv.visitVarInsn(ALOAD, 1); // the passed bean mv.visitFieldInsn( GETFIELD, // what to do "json/Bean", // class holding the field "i", // field name "I"); // field type (integer) mv.visitMethodInsn(INVOKEVIRTUAL, ".../StringBuilder", "append", "(I)L.../StringBuilder;", false);
  • 36. Marshal method – append method getS() mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn( INVOKEVIRTUAL, // method invocation type "json/Bean", // target class "get // method name "()L.../String;", // method signature false); // not an interface mv.visitMethodInsn(INVOKEVIRTUAL, ".../StringBuilder", "append", "(L.../String;)L.../StringBuilder;", false);
  • 37. Marshal method – finish up mv.visitMethodInsn( INVOKEVIRTUAL, ".../StringBuilder", "toString", "()L.../String;", false); mv.visitInsn(ARETURN);
  • 38. Bridge Methods ● Jsonifier<T> has method String marshal(T instance) ● After erasure, this is equivalent to String marshal(Object instance) ● But we wrote String marshal(Bean instance) ● Compiler generates a bridge method
  • 39. Marshal Bridge Method MethodVisitor mv = cw.visitMethod( ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC, "marshal", "(L.../Object;)L.../String;", null, null); // ... mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, "json/Bean"); mv.visitMethodInsn( INVOKEVIRTUAL, "json/BeanMarshaller", "marshal", "(json/Bean;)L.../String;", false); mv.visitInsn(ARETURN);
  • 40. Marshal Bridge Method MethodVisitor mv = cw.visitMethod( ACC_PUBLIC | ACC_BRIDGE | ACC_SYNTHETIC, "marshal", "(L.../Object;)L.../String;", null, null); // ... mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, "json/Bean"); mv.visitMethodInsn( INVOKEVIRTUAL, "json/BeanMarshaller", "marshal", "(json/Bean;)L.../String;", false); mv.visitInsn(ARETURN);
  • 41. Constructing an Instance ByteClassLoader loader = new ByteClassLoader( Bean.class.getClassLoader()); byte[] classBytes = dump(); Class<?> jsonifierClass = loader.loadClass( “json/BeanJsonifier”, classBytes); Jsonifier<Bean> jsonifier = (Jsonifier<Bean>) jsonifierClass.newInstance(); jsonifier.marshal(foo);
  • 42. Trouble in Paradise public class Bean { @Json("prop1") public int i; String s; @Json("prop2") public String getS() { return s; } }
  • 43. Trouble in Paradise public class Bean { @Json("prop1") private int i; String s; @Json("prop2") private String getS() { return s; } }
  • 44. Trouble in Paradise ● IllegalAccessError – tried to access field json.Bean.i from class json.BeanMarshaller ● Didn't happen back in the early days (as late as some versions of Java 5) ● Newer Java checks all bytecode for conformance ● Reflection can call setIsAccessible ● But reflection is slow!
  • 45. InvokeDynamic ● Originally added to support dynamic languages, but has found use far beyond that – Lambdas use it! ● First time it's invoked, effectively replaces itself with the resulting call ● Allows for excellent inlining, on par with handwritten code! ● Not available from java code – only bytecode ● ASMifier cannot help us here :(
  • 46. InvokeDynamic flow ● Class calls invokeDynamic, pointing to a bootstrap method... ● Which creates a MethodHandle... ● Which is wrapped in a CallSite... ● Which henceforth is run in place of the invokeDynamic call
  • 47. Bootstrap Method ● Takes arguments of type – MethodHandles.Lookup lookup – used for looking up MethodHandles – String name – the method name – MethodType methodType – the type of method to create – Any Bootstrap method constant arguments ● Returns a CallSite – Wrapper around a MethodHandle
  • 48. MethodHandles ● Typically constructors, method invocations or field access ● A MethodHandle instance comes from the Lookup passed into the bootstrap method ● Lookup can see only what the calling class can see... ● But that includes reflection! ● Lookup.unreflect converts a java.lang.reflect.Method instance ● Lookup.unreflectGetter converts a j.l.r.Field into a field access call site
  • 49. Call Sites ● A wrapper around a Method Handle ● Can be mutable or constant ● If constant, the JVM can effectively replace the invokeDynamic opcode with the wrapped method handle – Allows for inlining and subsequent optimizations
  • 50. Field Access Bootstrap Method public static CallSite invokeField( MethodHandles.Lookup lookup, String name, MethodType type, Class<?> owner, String fieldName) { Field f = owner.getDeclaredField(fieldName); f.setAccessible(true); MethodHandle mh = lookup.unreflectGetter(f); return new ConstantCallSite(mh); }
  • 51. Method Call Bootstrap Method public static CallSite invokeMethod( MethodHandles.Lookup lookup, String name, MethodType type, Class<?> owner, String methodName) { Method m = owner.getDeclaredMethod(methodName); m.setAccessible(true); MethodHandle mh = lookup.unreflect(m); return new ConstantCallSite(mh); }
  • 52. Calling InvokeDynamic ● The invokeDynamic JVM instruction is called with: – Method name (passed to the bootstrap method) – Method descriptor for that which will replace the invokeDynamic call – Bootstrap method – Bootstrap method constant arguments (0 or more) ● Primitive ● String ● Class ● Method/field reference
  • 53. Calling invokeDynamic for Field “i” mv.visitInvokeDynamicInsn( "i", // name for “method” "(Ljson/Bean;)I", // signature: int i(Bean) new Handle( // method handle H_INVOKESTATIC, // static method "json/Bootstraps", // class name "getField", // bootstrap method bootstrapSignature), // signature (TBD) "Ljson/Bean;", "i"); // bootstrap const args // mv.visitFieldInsn(GETFIELD, "json/Bean", "i", "I");
  • 54. Calling invokeDynamic for Method “s” mv.visitInvokeDynamicInsn( "s", “(Ljson/Bean;)java/lang/String”, new Handle( H_INVOKESTATIC, "json/Bootstraps", "invokeMethod", bootstrapSignature), “Ljson/Bean;”, "getS"); // mv.visitMethodInsn(INVOKE_VIRTUAL, "json/Bean", "s", // "()L.../String;", false);
  • 55. bootstrapSignature String bootstrapSignature = MethodType.methodType( CallSite.class, Lookup.class, String.class, MethodType.class, Class.class, // first bootstrap method argument String.class)// second bootstrap method argument .toMethodDescriptorString(); // “(L.../MethodHandles$Lookup;L...;L.../MethodType;L.../Class;L.../String;)” // + “L.../CallSite;”
  • 56. Still to do ● Instead of building class for Bean.class, do it dynamically, based on (one-time) reflection ● Left as exercise to reader... for (Field f: clazz.getDeclaredFields()) { Json json = f.getAnnotation(Json.class); if (json != null) { generateByteCodeForAnnotatedField(f); } }
  • 57. Benchmarks (ns/call) Reflection HandRolled Asm 88 84 516
  • 58. Benchmarks (ns/call) Reflection HandRolled Asm 88 84 516 Your millage may vary...
  • 59. Benchmarks (ns/call) Reflection HandRolled Asm Asm w/o nulls 302 88 84 516
  • 61. CheckClassAdapter ● Can wrap ASM ClassWriter to catch some errors at bytecode generation time instead of at runtime ● Gives much more meaningful error messages ● If using COMPUTE_MAXS to ask ASM to compute size for you, then first generate bytecode, wrap CheckClassAdapter around a ClassReader
  • 62. Line Numbers ● cw.visitSource(“Look at JsonifierBytecodeGenerator”) ● Label label = new Label(); mv.visitLabel(label); mv.visitLineNumber(200 + fieldNumber) ● Resulting stack trace can be very useful in diagnosing bugs ● Also can declare local variables to allow step-in debugging! – mv.visitLocalVariable(...)
  • 63. Bootstrap Method Arguments ● Many things cannot be passed directly to a bootstrap method – Notably – non-public classes – Can pass a reference to public class with static Class variables holding the class reference ● The bootstrap method can also be part of the generated class – Probably more pain than its worth
  • 64. SecurityManager ● Run reflection and bytecode generation via AccessController.doPrivileged(PrivilegedAction) ● Test using java.security.Policy.setPolicy – Set policy should override implies(ProtectionDomain,Permission) ● Make sure to allow your test framework to do stuff! – Also call System.setSecurityManager(new SecurityManager());
  • 65. Testing ● Any generated bytecode needs extensive unit testing ● Test what security permissions are required ● Test different types of classes – public, private, inner, inherited, etc ● Test different types of properties – primitives, Objects, arrays, nested arrays, etc ● Test for synthetic methods ● Test stack traces for line numbers
  • 66. Don't Overdo it! ● Benchmark your code before optimizing at all ● Write as much code as possible directly in Java – Call out from bytecode using invokeVirtual
  • 67. Thank you! ● http://www.slideshare.net/nainostrebor/ ● https://github.com/irobertson/invokedynamic-talk-code ● Don't forget to fill out surveys!!!