I Know Kung Fu - Juggling Java Bytecode
Upcoming SlideShare
Loading in...5
×
 

I Know Kung Fu - Juggling Java Bytecode

on

  • 817 views

How can we change Java bytecode using ASM library.

How can we change Java bytecode using ASM library.
Mostly for fun.

Statistics

Views

Total Views
817
Views on SlideShare
817
Embed Views
0

Actions

Likes
4
Downloads
27
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

I Know Kung Fu - Juggling Java Bytecode I Know Kung Fu - Juggling Java Bytecode Presentation Transcript

  • I Know Kung Fu – Writing Java BytecodeAlexander Shopov <ash@kambanaria.org>
  • [ash@edge ~]$ whoami By day: Software Engineer at Cisco By night: OSS contributor Coordinator of Bulgarian Gnome TP Contacts: E-mail: ash@kambanaria.org Jabber: al_shopov@jabber.minus273.org LinkedIn: http://www.linkedin.com/in/alshopov Web: Just search “al_shopov”
  • Please Learn And ShareLicense: CC-BY v3.0Creative Common View slide
  • DisclaimerMy opinions, knowledge and experience! Not my employers. View slide
  • Plan● What is bytecode and how to juggle it● Why do it, why not do it● How to do it – Class file structure – Manipulation with ASM● How to consume your own dog food – Classloaders – Proxies – Agents
  • What it is● Bytecodes are the assembler of JVM. By writing new bytecode sequences or changing existing ones we can program the JVM directly – outside of the domain of the Java Programming Language;● Lower level than writing Java - JVM assembler;● Much easier than writing a whole (optimizing) compiler;● Harder than using a compiler;● Similar to the Jedi Dark Side – pathway to many abilities some consider to be unnatural.
  • Why do it?● Makes impossible things possible &● Makes hard things attainable &● Makes long tasks shorter &● Makes slow things faster &● Makes repetition go away &● Makes your head blow away!!!
  • Common use cases● Program analysis – find bugs, unused code paths, reverse engineering, metrics, etc.● Program generation – compilers, stub/skeleton compilers, fast layers without reflection.● Transformation – optimize, deoptimize/obfuscate, aspect weaving, performance monitoring.
  • Why not do it?● Source stops being close to executable &● Executable stops being close to any source &● There can be better or easier ways of doing things &● Fewer people knowing what happens means no one can help you &● Problems will be your fault! Unit test ∀ ! &● You take the red pill and find out the rabbit hole goes deep, deep, deep… EOT
  • Common misuse cases● I write it, you maintain it.● Do it because it is cool or others are doing it.● Over engineering / over generalization.● Solution in a search of a problem.● Preferring NIH to a popular solution.
  • Bytecode crash course in 1-2-3 F4 Thread D Thread C Thread B F3 F3 Thread A F2 F2 F2 F1 F1 F1 F1 F0 F0 F0 F0
  • Bytecode crash course in 1-2-3 JVM (heap) 0 1 2 3 4 5 6 … Class PC Local variables Method code F0 Class Pool of constants Stack
  • Bytecode crash course in 1-2-3● Bytecodes retrieve things from class bytestream, constant pool, local variables array● Perform computations only on stack● Store temporary results in local variables array● Enter frame by method call● Return from frame with a possible result (top thing on stack), throw top of stack● JVM can also throw and unroll frames
  • If you need more information● Check my other presentation:Lifting the Veil – Reading Java Bytecode
  • How to do it● Many ways with different pluses and minuses● Easiest way to write byte code – use the Java compiler. – javac – you all know that – java compiler api – since 1.6● But more of that – wait for the end● Until then…
  • Soot jDec CGlib JCF Editor cojen Jiapi RetroweaverJavaAssist BCEL Tea Trove jReloader jclasslib Roo Serp AspectJ gnu.bytecode
  • Soot jDec CGlib JCF Editor cojen Jiapi One of the older frameworks, RetroweaverJavaAssist BCEL Tea Trove jReloader jclasslib Roo Serp AspectJ gnu.bytecode
  • Bytecode manipulation● Not constrained by Java● Not constrained by compiler● Not constrained by source availability● Still constrained by JVM● Generate or change bytecode to the limits of JVM
  • Classfile Structure, 0xcafebabeModifiers, name, super class, interfacesConstant pool: numeric, string and type constantsSource file name (optional)Enclosing class referenceAnnotation*Attribute*Inner class* NameField* Modifiers, name, type Annotation* Attribute*Method* Modifiers, name, return and parameter types Annotation* Attribute* Compiled code
  • What is ASM● Extremely well designed bytecode manipulation library● Modular, small and fast – pick all● Events based – visitor (+SAX), additionally has tree API (DOM like)● Provides a small bytecode generation variant● Minimal – just transformation, can stack transformations, no classloading
  • Timeline● 1.5 – 30 Aug 2004 – up to JDK 1.6 incl.● 2.0 – 17 May 2005● 3.0 – 01 Nov 2006● 4.0 – 29 Oct 2011 – framework for compatibility
  • Who uses ASM?● Languages and AOP tools: AspectJ, BeanShell, CGLIB, Clojure, Groovy, Jruby, Jython, Coroutines● Tools and frameworks: Fractal, Terracotta, Javeleon, JRebel● Persistence: OpenEJB, Oracle BerkleyDB, EclipseLink● Monitoring: WebLogic, JiP● Testing and code analysis: Cobertura, Eclipse
  • Most JDKs Use It!● Oracle – Sun – HotSpot – JDK, OpenJDK – BEA/Appeal VM – JRockit● IBM – J9● Azul – Zing – $JAVA_HOME/jre/lib/rt.jar – com.sun.xml.internal.ws.org.objectweb.asm – com.sun.xml.internal.ws.model.WrapperBeanGenerator ⇒ RuntimeModeler ⇒ Web Services implementation
  • Read and explore Class ClassReader Visitor
  • Read, transform, write Class Class Class Class Reader Visitor Visitor Writer
  • I know what I am doing Class Class Class Class Class Class Class ClassReader Visitor Visitor Visitor Visitor Visitor Visitor Writer
  • I believe I can fly, I believe I can touch the sky… Class Class Class Class Class ClassReader Visitor Visitor Visitor Visitor Writer Class Class Class Class Class Visitor Visitor Visitor Visitor Writer Class Class Class Class Class ClassReader Visitor Visitor Visitor Visitor Writer Class Class Class Class Visitor Visitor Visitor Writer Class Class Class Class ClassReader Visitor Visitor Visitor Visitor
  • Basic Pattern Of Transformation● Read bytestream of a class● Generate events from that● Make changes to the events● Receive the events and serialize to bytestream● Lather, rinse, repeat
  • Basic diagram of class visitingUser ClassVisitor visit visitSource ● Order is important visitOuterClass visitAnnotation ● Bold return other * visitAttribute visitors visitInnerClass visitFiled * ● * marks repeat vistiMethod visitEnd
  • Basic diagram of annotation visiting User AnnotationVisitor visit * visitEnum ● Order is important visitAnnotation visitArray ● Bold return other visitEnd visitors ● * marks repeat
  • Basic diagram of field visitingUser FieldVisitor visitAnnotation * visitAttribute ● Order is important visitEnd ● Bold return other visitors ● * marks repeat
  • Basic diagram of method visitingUser MethodVisitor visitAnnotationDefault visitAnnotation * ● Order is important visitParameterAnnotation visitAttribute ● Bold return other visitCode visitors visitFrame visitXInsn * ● * marks repeat visitLabel visitTryCatchBlock visitLocalVariable visitLineNumber visitMaxs visitEnd
  • Our humble beginningpackage org.kambanaria.writebytecode.asm;public class Zombunny { //Зомбайо public Integer getVersion() { return Integer.valueOf(1); }}
  • Our building blockspublic class StupidClassLoader extends ClassLoader { private Map<String, byte[]> bytes = new HashMap<String, byte[]>(); public StupidClassLoader() { super(StupidClassLoader.class.getClassLoader()); } public void provide(String className, byte[] classBytes) { bytes.put(className, classBytes); } @Override public Class<?> loadClass(String name) throws ClassNotFoundException { byte[] classBytes = bytes.get(name); Class<?> loaded; if (classBytes != null) { loaded = defineClass(name, classBytes, 0, classBytes.length); } else { ClassLoader parent = getParent(); if (null == parent) { parent = ClassLoader.getSystemClassLoader(); } loaded = parent.loadClass(name); } return loaded; }}
  • Our building blockspublic final class Utilities { private Utilities() { } public static final String CLASS_NAME = "org.kambanaria.writebytecode.asm.Zombunny"; public static final String METHOD_NAME = "getVersion"; public static String toClassPathResourceName(String clazz) { return clazz.replace(., /) + ".class"; } public static byte[] retrieveBytesFromClassPath(String className) throws IOException { String path = toClassPathResourceName(className); InputStream is = Utilities.class.getClassLoader().getResourceAsStream(path); InputStream classBytes = new BufferedInputStream(is); return is2bytes(classBytes); } public static byte[] patch(byte[] bytes, DemoClassAdapter adapter) { ClassReader cr = new ClassReader(bytes); cr.accept(adapter, ClassReader.SKIP_FRAMES); return adapter.getCw().toByteArray(); } public static byte[] is2bytes(InputStream is) throws IOException { int max = 1024 * 1024; byte[] bytes = new byte[max]; // 1MB int read = is.read(bytes); // Dont do that byte[] result = new byte[read]; System.arraycopy(bytes, 0, result, 0, result.length); return result; } public static Object call0ArgsMethodOn(Object o, String methodName) // throws ReflectiveOperationException { Class<?> c = o.getClass(); Method m = c.getDeclaredMethod(methodName, (Class<?>[]) null); m.setAccessible(true); return m.invoke(o, (Object[]) null); }}
  • Our building blockspublic class DemoClassAdapter extends ClassVisitor { public DemoClassAdapter(ClassVisitor cv) { super(Opcodes.ASM4, cv); } public ClassWriter getCw() { return (ClassWriter) cv; }}
  • Rename classpublic class Rename extends DemoClassAdapter { private String suffix; public Rename(ClassVisitor cv, String suffix) { super(cv); this.suffix = suffix; } @Override public void visit(int version, int access, String name, // String signature, String superName, String[] interfaces) { cv.visit(version, access, name + suffix, signature, superName, interfaces); }}
  • Result is as ifpackage org.kambanaria.writebytecode.asm;public class ZombunnySUFFIX { //Зомбайо public Integer getVersion() { return Integer.valueOf(1); }}
  • Add fieldpublic class AddField extends DemoClassAdapter { public AddField(ClassVisitor cv) { super(cv); } @Override public void visitEnd() { cv.visitField(ACC_PRIVATE, "_version", // Type.getDescriptor(Integer.class), null, null); cv.visitEnd(); }}
  • Add fieldpublic class Zombunny { //Зомбайо private Integer _version; public Integer getVersion() { return Integer.valueOf(1); }}
  • Change methodpublic class ChangeMethod extends DemoClassAdapter { public ChangeMethod(ClassVisitor cv) { super(cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, // String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (Utilities.METHOD_NAME.equals(name) && "()Ljava/lang/Integer;".equals(desc)) { return new DemoMethodVisitor(Opcodes.ASM4, mv); } else { return mv; } } class DemoMethodVisitor extends MethodVisitor { public DemoMethodVisitor(int version, MethodVisitor mv) { super(version, mv); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, "org/kambanaria/writebytecode/asm/Zombunny", // "_version", "Ljava/lang/Integer;"); mv.visitInsn(ARETURN); mv.visitEnd(); } }}
  • And change methodpublic class Zombunny { //Зомбайо private Integer _version; public Integer getVersion() { return _version; }}
  • Fix constructorspublic class ManipulateConstructors extends DemoClassAdapter { public ManipulateConstructors(ClassVisitor cv) { super(cv); } @Override public MethodVisitor visitMethod(int access, String name, String desc, // String signature, String[] exceptions) { if ("<init>".equals(name)) { MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mv.visitVarInsn(ALOAD, 0); mv.visitTypeInsn(NEW, "java/lang/Integer"); mv.visitInsn(DUP); mv.visitInsn(ICONST_2); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V"); mv.visitFieldInsn(PUTFIELD, "org/kambanaria/writebytecode/asm/Zombunny", // "_version", "Ljava/lang/Integer;"); mv.visitInsn(RETURN); mv.visitMaxs(4, 1); mv.visitEnd(); return mv; } else { return cv.visitMethod(access, name, desc, signature, exceptions); } } @Override public void visitEnd() { MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Integer;)V", // null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(PUTFIELD, "org/kambanaria/writebytecode/asm/Zombunny", // "_version", "Ljava/lang/Integer;"); mv.visitInsn(RETURN); mv.visitMaxs(2, 2); mv.visitEnd(); cv.visitEnd(); }}
  • And fix constructorspublic class Zombunny { //Зомбайо private Integer _version; public Zombunny() { _version = new Integer(2); } public Zombunny(Integer version) { _version = version; } public Integer getVersion() { return _version; }}
  • Add interfacepublic class AddInterface extends DemoClassAdapter { private static final String COMPARABLE = "java/lang/Comparable"; public AddInterface(ClassVisitor cv) { super(cv); } @Override public void visit(int version, int access, String name, String signature,// String superName, String[] interfaces) { if (Arrays.asList(interfaces).contains(COMPARABLE)) { super.visit(version, access, name, signature, superName, interfaces); } else { int l = interfaces.length; String[] newInterfaces = new String[l + 1]; System.arraycopy(interfaces, 0, newInterfaces, 0, l); newInterfaces[l] = COMPARABLE; super.visit(version, access, name, signature, superName, newInterfaces); } }}
  • And add interfacepublic class Zombunny implements Comparable { //Зомбайо private Integer _version; public Zombunny() { _version = new Integer(2); } public Zombunny(Integer version) { _version = version; } public Integer getVersion() { return _version; }}
  • Add interface implementationpublic class AddMethod extends DemoClassAdapter { public AddMethod(ClassVisitor cv) {super(cv);} @Override public void visitEnd() { MethodVisitor compareToObject = cv.visitMethod(ACC_PUBLIC, "compareTo", // "(Ljava/lang/Object;)I", null, null); compareToObject.visitCode(); compareToObject.visitVarInsn(ALOAD, 1); compareToObject.visitTypeInsn(INSTANCEOF, "org/kambanaria/writebytecode/asm/Zombunny"); Label lbl0 = new Label(); compareToObject.visitJumpInsn(IFNE, lbl0); compareToObject.visitTypeInsn(NEW, "java/lang/ClassCastException"); compareToObject.visitInsn(DUP); compareToObject.visitMethodInsn(INVOKESPECIAL, "java/lang/ClassCastException", // "<init>", "()V"); compareToObject.visitInsn(ATHROW); compareToObject.visitLabel(lbl0); compareToObject.visitVarInsn(ALOAD, 0); compareToObject.visitVarInsn(ALOAD, 1); compareToObject.visitTypeInsn(CHECKCAST, "org/kambanaria/writebytecode/asm/Zombunny"); compareToObject.visitMethodInsn(INVOKEVIRTUAL, "org/kambanaria/writebytecode/asm/Zombunny", // "compareTo", "(Lorg/kambanaria/writebytecode/asm/Zombunny;)I"); compareToObject.visitInsn(IRETURN); compareToObject.visitMaxs(2, 2); compareToObject.visitEnd(); MethodVisitor compareToZombunny = cv.visitMethod(ACC_PUBLIC, "compareTo", // "(Lorg/kambanaria/writebytecode/asm/Zombunny;)I", null, null); compareToZombunny.visitCode(); compareToZombunny.visitVarInsn(ALOAD, 0); compareToZombunny.visitFieldInsn(GETFIELD, "org/kambanaria/writebytecode/asm/Zombunny", // "_version", "Ljava/lang/Integer;"); compareToZombunny.visitVarInsn(ALOAD, 1); compareToZombunny.visitFieldInsn(GETFIELD, "org/kambanaria/writebytecode/asm/Zombunny", // "_version", "Ljava/lang/Integer;"); compareToZombunny.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", // "(Ljava/lang/Integer;)I"); compareToZombunny.visitInsn(IRETURN); compareToZombunny.visitMaxs(2, 2); compareToZombunny.visitEnd(); cv.visitEnd(); }}
  • And add interfacepublic class Zombunny implements Comparable { //Зомбайо private Integer _version; public Zombunny() { _version = new Integer(2); } public Zombunny(Integer version) { _version = version; } public Integer getVersion() { return _version; } public int compareTo(Object o) { if (o instanceof Zombunny) { return compareTo((Zombunny) o); } else { throw new ClassCastException(); } } public int compareTo(Zombunny o) { return _version.compareTo(o._version); }}
  • The class we get method frompackage org.kambanaria.writebytecode.asm;public class Version { private Integer _version; public Version(Integer version) { _version = version; } public Integer getVersion() { return _version; } @Override public String toString() { return "Version: " + _version; }}
  • Chimerizationpublic class Chimerize extends DemoClassAdapter { protected ClassNode twig; protected MethodNode nm; private static final String TWIG_NAME = "org.kambanaria.writebytecode.asm.Version"; public Chimerize(ClassVisitor cv) throws IOException { super(cv); ClassReader rdr = new ClassReader(TWIG_NAME); twig = new ClassNode(); rdr.accept(twig, 0); for (Object o : twig.methods) { MethodNode method = (MethodNode) o; if (method.name.equals("toString")) { // Guess what is missing? nm = method; } } } @Override public void visitEnd() { MethodVisitor chimeric =cv.visitMethod(nm.access, nm.name, nm.desc, nm.signature, null); nm.instructions.resetLabels(); Remapper remapper = new Remapper() { @Override public String map(String name) { return name.replace("Version", "Zombunny"); } }; nm.accept(new RemappingMethodAdapter(nm.access, nm.desc, chimeric, remapper)); cv.visitEnd(); }}
  • Finallypublic class Zombunny implements Comparable { //Зомбайо private Integer _version; public Zombunny() { _version = new Integer(2); } public Zombunny(Integer version) { _version = version; } public Integer getVersion() { return _version; } public int compareTo(Object o) { if (o instanceof Zombunny) { return compareTo((Zombunny) o); } else { throw new ClassCastException(); } } public int compareTo(Zombunny o) { return _version.compareTo(o._version); } @Override public String toString(){ return "Version: " + _version; }}
  • Peek at test construction chainpublic class ChimerizeTest { Comparable sut; @Before public void setUp() throws IOException, ReflectiveOperationException { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES + // ClassWriter.COMPUTE_MAXS); ClassVisitor cv = new AddField(new ManipulateConstructors( // new AddMethod(new AddInterface(new Chimerize(cw))))); ClassReader rdr = new ClassReader(Utilities.CLASS_NAME); rdr.accept(cv, 0); byte[] newClassBytes = cw.toByteArray(); StupidClassLoader ldr = new StupidClassLoader(); ldr.provide(Utilities.CLASS_NAME, newClassBytes); Class<?> newClass = ldr.loadClass(Utilities.CLASS_NAME); Constructor<?> constructor = newClass.getDeclaredConstructor(Integer.class); sut = (Comparable) constructor.newInstance(new Integer(42)); }}
  • Utilities● Type● Generate the visitors with: Asmfier – java -classpath asm.jar:asm-util.jar:OUR_CP org.objectweb.asm.util.ASMifier CLASS● TraceClassVisitor● CheckClassAdapter
  • Additional APIs● org.objectweb.asm.commons – commonly needed adaptors● org.objectweb.asm.xml – bridge to SAX 2.0, manipulate via XSLT, XQuery● org.objectweb.asm.tree – deserialize to tree
  • Classloaders● Dynamically load software components for Java platform● Lazy – loaded on demand, as late as possible● Type-safe linkage – must not violate type safety, no runtime checks● User-defined extensibility – normal, user controlled objects● Multiple communicating namespaces – types determined by class name and classloader
  • Classloader Chain Bootstrap CL Extension CL System CL Application Programmerprimordial, native jre/lib/ext/*.jar $CLASSPATH classloader classloader jre/lib/*.jar -Djava.ext.dirs -Djava.class.path
  • Classloader Hierarchy Application Programmer classloader classloader Bootstrap CL Extension CL System CL Application Programmerprimordial, native jre/lib/ext/*.jar $CLASSPATH classloader classloader jre/lib/*.jar -Djava.ext.dirs -Djava.class.path Application Programmer classloader classloader
  • Enterprise Classloader Hierarchy Programmer Application classloader classloader Programmer classloader Programmer Bootstrap CL Extension CL System CL classloader Applicationprimordial, native jre/lib/ext/*.jar $CLASSPATH classloader Programmer jre/lib/*.jar -Djava.ext.dirs -Djava.class.path classloader Programmer Application classloader classloader Programmer classloader
  • Class Loader APIpublic abstract class ClassLoader { protected ClassLoader(ClassLoader parent); protected ClassLoader(); protected Class<?> loadClass(String name, boolean resolve); protected Class<?> findClass(String name); protected final Class<?> defineClass(String name, byte[] b, int off, int len); protected final void resolveClass(Class<?> c); public URL getResource(String name); public Enumeration<URL> getResources(String name); public final MyClassLoader getParent(); public void setDefaultAssertionStatus(boolean); public void setPackageAssertionStatus(String packageName,boolean enabled); public void setClassAssertionStatus(String className, boolean enabled); public void clearAssertionStatus();}
  • When are class loaded?● Statically: – Instance creation: new Integer(42); – Reference to static field or method: System.out;● Dynamically: – Class.forName("java.lang.HashMap"); – Class.forName("java.lang.HashMap", boolean initialize, ClassLoader loader);
  • Delegating Classloaders – Standard
  • Plugin or Web Classloaders
  • OSGI & others OMG!!!
  • Type Compatibility● A classloader can see and use (with exact type) instances of classes loaded by the ancestral chain and the classloader itself● Instances of classes loaded by sibling or descendant classloaders are invisible, they are just java.lang.Object Object a; "SomeClass".equals(a.getClass().getName()); a instanceof Object; (SomeClass)a -> ClassCastException● Use reflection
  • Proxies● Dynamic proxy acts as a pass through/router to the real object – runtime implementations of interfaces – public, final and not abstract – extend java.lang.reflect.Proxy● Proxy’s behaviour is determined by an implementation of java.lang.reflect.InvocationHandler
  • Square peg in a non square hole Some object Some interface
  • Fit it with an InvocationHandler Proxy Objectpublic interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}
  • public class MyInvocationHandler implements InvocationHandler { private Object delegate; public MyInvocationHandler(Object... params) { delegate = makeDelegate(); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Class<?>[] types = method.getParameterTypes(); Method m = reachMethod(methodName, types); Object result; try { result = m.invoke(delegate, args); } catch (InvocationTargetException e) { throw e.getCause(); } return result; } private static Method reachMethod(String name, Class<?>... parTypes) { Method m = null; /* Logic to determine method */ return m; } private static Object makeDelegate(Object... params) { /* Produce real object instead */ return new Object(); }}
  • Java agents● Package java.lang.instrument - allow Java programming language agents to instrument programs running on the JVM.● java ... -javaagent:jarpath[=options]● Manifest attributes● byte[] -> byte[]
  • Almost Real Example
  • Action takes place here
  • A planet, an icy atmosphere
  • This country
  • Outsources to this country
  • They deliver cheaper than the world, faster than the speed of thought
  • The entrepreneurial spirit of that country
  • Makes them sell the software to this country
  • Where it fails spectacularly whileclearly working in the other two ones
  • Investigation takes place
  • Finds the culpritSimpleDateFormat frmt = new SimpleDateFormat("E MM/dd/yyyy");SimpleDateFormatpublicSimpleDateFormat(String pattern)Constructs a SimpleDateFormat using thegiven pattern and the default date formatsymbols for the default locale.Note: This constructor may not support allLocales. For full coverage, use the factorymethods in the DateFormat class.Parameters:pattern - the pattern describing the date andtime formatThrows:NullPointerException - if the given pattern is nullIllegalArgumentException - if the given pattern is invalid
  • Could this have been avoided? Hindi (300×106, 4th) ShukravārLANG=de_DE.UTF-8 java -jar SimpleDateFormat.jar Freitag 11/16/2012LANG=en_US.UTF-8 java -jar SimpleDateFormat.jar Friday 11/16/2012LANG=hi_IN.UTF-8 java -jar SimpleDateFormat.jar शुकवार ११/१६/२०१२LANG=bn_IN.UTF-8 java -jar SimpleDateFormat.jar Friday 11/16/2012LANG=bg_BG.UTF-8 java -jar SimpleDateFormat.jar Петък 11/16/2012 Bengali (200×106, 7th) শুক্রার ্রব Shukrobar
  • En UseLo glis ca h le India proposes:
  • USA retransmits: Use English Locale
  • Germany says: No!
  • USA try in German: Verwenden Sie Englische Locale
  • Germany says: Nein!
  • USA try manners: Bitte?
  • Germany says: NEIN!
  • USA checks what they said with Google translate:
  • mv.visitMethodInsn(INVOKESPECIAL, "java/text/SimpleDateFormat", // "<init>", "(Ljava/lang/String;)V");mv.visitFieldInsn(GETSTATIC, "java/util/Locale", // "US", "Ljava/util/Locale;");mv.visitMethodInsn(INVOKESPECIAL, "java/text/SimpleDateFormat", // "<init>", "(Ljava/lang/String;Ljava/util/Locale;)V");
  • Compiler API
  • import java.util.Random;public class I { public boolean singOutOfTune() { return new Random().nextBoolean(); }}
  • import java.util.Random;public class I { public boolean singOutOfTune() { return new Random().nextBoolean(); }}public class You { public static String DID = "StandUp&WalkOutOnMe"; public static String DIDNOT = "LendMeAnEar"; public String wouldDo(boolean iF) { return iF ? "StandUp&WalkOutOnMe" : "LendMeAnEar"; }}
  • import java.util.Random;public class I { public boolean singOutOfTune() { return new Random().nextBoolean(); }}public class You { public static String DID = "StandUp&WalkOutOnMe"; public static String DIDNOT = "LendMeAnEar"; public String wouldDo(boolean iF) { return iF ? "StandUp&WalkOutOnMe" : "LendMeAnEar"; }}public class ExistingClassesTest { @Test public void test1000times() { int times = 1000; do { boolean didI; I i = new I(); You you = new You(); String what = you.wouldDo(didI = i.singOutOfTune()); assertEquals(what, didI ? You.DID : You.DIDNOT); } while (--times > 0); }}
  • import java.util.Random;public class I { public boolean singOutOfTune() { return new Random().nextBoolean(); }}public class You { public static String DID = "StandUp&WalkOutOnMe"; public static String DIDNOT = "LendMeAnEar"; public String wouldDo(boolean iF) { return iF ? "StandUp&WalkOutOnMe" : "LendMeAnEar"; }}public class ExistingClassesTest { @Test public void test1000times() { int times = 1000; do { boolean didI; I i = new I(); You you = new You(); String what = you.wouldDo(didI = i.singOutOfTune()); assertEquals(what, didI ? You.DID : You.DIDNOT); } while (--times > 0); // What would you do if I sang out of tune ? The Beatles }}
  • public class SourceStrings { private SourceStrings() } public final static String I = " " + "import java.util.Random; " + "public class I { " + " public boolean singOutOfTune() { " + " return new Random().nextBoolean(); " + " } " + "} "; public final static String YOU = " " + "public class You { " + " public static String DID = "StandUp&WalkOutOnMe"; " + " public static String DIDNOT = "LendMeAnEar"; " + " public String wouldDo(boolean iF) { " + " return iF ? "StandUp&WalkOutOnMe" : "LendMeAnEar";" + " } " + "} ";}class StringSourceCodeObject extends SimpleJavaFileObject { final String _source; public StringSourceCodeObject(String fqName, String source) { super(URI.create("string:///" + fqName.replaceAll(".", "/") // + Kind.SOURCE.extension), Kind.SOURCE); _source = source; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return _source; }
  • public class CompilerAPI { public static void main(String args[]) throws Exception { /* Creating dynamic java source code file object */ JavaFileObject iObject = new StringSourceCodeObject("I", SourceStrings.I); JavaFileObject youObject = new StringSourceCodeObject("You", SourceStrings.YOU); JavaFileObject jfObjects[] = new JavaFileObject[]{iObject, youObject}; /* Units to compile */ Iterable<JavaFileObject> units = Arrays.asList(jfObjects); /* Instantiating the java compiler */ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); /* Get compiler file manager to show what to read. */ // (DEFAULT LISTENER, Locale.getDefault(), Charset.defaultCharset() ) JavaFileManager manager = compiler.getStandardFileManager(null, null, null); /* Compilation options - here: place in target directory */ String[] compileOptions = new String[]{"-d", "target/classes"}; Iterable<String> options = Arrays.asList(compileOptions); /* Diagnostic placeholder */ DiagnosticCollector<JavaFileObject> sink = new DiagnosticCollector<JavaFileObject>(); /* 1st null: where to write (default), 2nd null: no annotations processed */ CompilationTask task = compiler.getTask(null, manager, sink, options, null, units); /* Go, go, go */ boolean status = task.call(); if (!status) { for (Diagnostic<? extends JavaFileObject> d : sink.getDiagnostics()) { System.err.format("Error on line %d in %s", d.getLineNumber(), d); } } manager.close();// TRY to close the file manager }}
  • public class CompilerAPITest { Object i; Object you; @BeforeClass public static void setUpClass() throws Exception { CompilerAPI.main(null); } @Before public void setUp() throws ReflectiveOperationException { i = Class.forName("I").newInstance(); you = Class.forName("You").newInstance(); } @After public void tearDown() { i = null; you = null; } @Test public void testMain() throws Exception { assertEquals(i.getClass().getName(), "I"); assertEquals(you.getClass().getName(), "You"); Method m1 = i.getClass().getMethod("singOutOfTune", (Class<?>[]) null); Object didI = m1.invoke(i, (Object[]) null); for (Method m2 : you.getClass().getMethods()) { if ("wouldDo".equals(m2.getName())) { Class<?>[] args = m2.getParameterTypes(); if (1 == args.length && args[0].isAssignableFrom(boolean.class)) { System.out.println("m2.invoke(you, didI)"); return; } } } fail("We did not find our method!"); }
  • Some Links● Code: https://github.com/alshopov/WriteBytecode● Presentation: The presentation is to be improved.