Programming JVM Bytecode

100
Programming JVM Bytecode

Transcript of Programming JVM Bytecode

ProgrammingJVM Bytecode

Joe Kutner@codefinger

JVM Langs Owner@Heroku

.class.java

$ cat Main.class ????3

<init>()VCodeLineNumberTablemain([Ljava/lang/String;)VSourceFile Main.java java/util/ArrayListHello Mainjava/lang/Objectadd(Ljava/lang/Object;)Z!*??

*?Y??W?

javap

class Main {public static void main(String[] args) {

(new ArrayList<String>()).add("Hello");}

}

Compiled from "Main.java"public class Main { public Main(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object... 4: return

public static void main(java.lang.String[]); Code: 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList... 7: ldc #4 // String Hello 9: invokevirtual #5 // Method java/util/ArrayList... 12: pop 13: return }

$ javac Main.java$ javap -c Main

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

[POSITION]: [OPERATION] [OPERAND]

0: new #2

[POSITION]: [OPERATION] [OPERAND]

Location of the instruction in the raw binary data

[POSITION]: [OPERATION] [OPERAND]

A mnemonic that represents one of the 256 possible opcodes

[POSITION]: [OPERATION] [OPERAND]

Arguments to the operation. The number and type of these

depends on the operation in use.

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

...Constant pool: #1 = Methodref #7.#16 // java/lang/Object... #2 = Class #17 // java/util/ArrayList #3 = Methodref #2.#16 // java/util/ArrayList.”<init>” #4 = String #18 // Hello #5 = Methodref #2.#19 // java/util/ArrayList.add:(L... #6 = Class #20 // Main #7 = Class #21 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 SourceFile #15 = Utf8 Main.java #16 = NameAndType #8:#9 // "<init>":()V #17 = Utf8 java/util/ArrayList

$ javap -v -c Main

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

Stack-Based Model of Computation

Exception in thread "main" java.lang.NullPointerException at Printer.printString(Printer.java:13) at Printer.print(Printer.java:9) at Printer.main(Printer.java:19)

Stack Frame

Operand Stack

Local Variable

Array

Constant Pool #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

Stack Frame

Operand StackLocal Vars Constant Pool #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

<ArrayList>

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

<ArrayList>

invokespecial Used to invoke an initializer, private method, or superclass method

invokeinterface Used to invoke an interface method

invokestatic Used to invoke a class-level method (i.e. static methods)

invokevirtual Used to invoke an instance method

invokedynamic Used to invoke methods dynamically (new shiny)

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

<ArrayList>

...Constant pool: #1 = Methodref #7.#16 // java/lang/Object... #2 = Class #17 // java/util/ArrayList #3 = Methodref #2.#16 // java/util/ArrayList.”<init>” #4 = String #18 // Hello #5 = Methodref #2.#19 // java/util/ArrayList.add:(L... #6 = Class #20 // Main #7 = Class #21 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 SourceFile #15 = Utf8 Main.java #16 = NameAndType #8:#9 // "<init>":()V #17 = Utf8 java/util/ArrayList

$ javap -v -c Main

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

<ArrayList>

Stack Frame

Operand StackLocal Vars Constant Pool #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

...Constant pool: #1 = Methodref #7.#16 // java/lang/Object... #2 = Class #17 // java/util/ArrayList #3 = Methodref #2.#16 // java/util/ArrayList.”<init>” #4 = String #18 // Hello #5 = Methodref #2.#19 // java/util/ArrayList.add:(L... #6 = Class #20 // Main #7 = Class #21 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 SourceFile #15 = Utf8 Main.java #16 = NameAndType #8:#9 // "<init>":()V #17 = Utf8 java/util/ArrayList #18 = Utf8 Hello #19 = NameAndType #22:#23 // add:(Ljava/lang/Object;)Z

$ javap -v -c Main

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

<“Hello”>

Stack Frame

Operand StackLocal Vars Constant Pool

<ArrayList>

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

<“Hello”>

...Constant pool: #1 = Methodref #7.#16 // java/lang/Object... #2 = Class #17 // java/util/ArrayList #3 = Methodref #2.#16 // java/util/ArrayList.”<init>” #4 = String #18 // Hello #5 = Methodref #2.#19 // java/util/ArrayList.add:(L... #6 = Class #20 // Main #7 = Class #21 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 SourceFile #15 = Utf8 Main.java #16 = NameAndType #8:#9 // "<init>":()V #17 = Utf8 java/util/ArrayList #18 = Utf8 Hello #19 = NameAndType #22:#23 // add:(Ljava/lang/Object;)Z

$ javap -v -c Main

java/util/ArrayList.add:(Ljava/lang/Object;)Z

B - byteC - charS - shortI - intJ - longF - floatD - doubleZ - booleanV - voidL - object

Stack Frame

Operand StackLocal Vars Constant Pool

ArrayList

ArrayList #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

“Hello”

Object

“Hello”

...

Methodref

Target

Arguments

Operand Stack

Stack Frame

Operand StackLocal Vars Constant PoolArrayList #1 = Methodref

#2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

<ArrayList>

<“Hello”>

Stack Frame

Operand StackLocal Vars Constant Pool #1 = Methodref #2 = Class #3 = Methodref #4 = String #5 = Methodref ...

true

Stack Frame

Operand StackLocal Vars Constant PoolArrayList #1 = Methodref

#2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

true

Stack Frame

Operand StackLocal Vars Constant PoolArrayList #1 = Methodref

#2 = Class #3 = Methodref #4 = String #5 = Methodref ...

0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop13: return

swap

iadd

getstatic [CLASS] [FIELD]

iconst_1

WritingJVM Bytecode

000000B0: 73 01 00 0A 4C 43 6F 6D 70 69 6C 65 72 3B 01 00000000C0: 07 63 6F 6D 70 69 6C 65 01 00 32 28 5B 4C 6A 61000000D0: 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 000000E0: 4C 6D 65 2F 71 6D 78 2F 6A 69 74 65 73 63 72 69000000F0: 70 74 2F 4A 69 74 65 43 6C 61 73 73 3B 01 00 0600000100: 74 6F 6B 65 6E 73 01 00 13 5B 4C 6A 61 76 61 2F00000110: 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01 00 04 6D00000120: 61 69 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 6100000130: 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 04 6100000140: 72 67 73 01 00 09 6A 69 74 65 43 6C 61 73 73 0100000150: 00 1D 4C 6D 65 2F 71 6D 78 2F 6A 69 74 65 73 6300000160: 72 69 70 74 2F 4A 69 74 65 43 6C 61 73 73 3B 0100000170: 00 06 6F 75 74 70 75 74 01 00 1A 4C 6A 61 76 61 00000180: 2F 69 6F 2F 46 69 6C 65 4F 75 74 70 75 74 53 7400000190: 72 65 61 6D 3B 01 00 0A 45 78 63 65 70 74 69 6F000001A0: 6E 73 07 00 3A 01 00 0A 53 6F 75 72 63 65 46 69000001B0: 6C 65 01 00 0A 43 6F 6D 70 69 6C 65 72 2E 6A 61000001C0: 76 61 0C 00 14 00 15 01 00 0A 43 6F 6D 70 69 6C000001D0: 65 72 24 31 01 00 04 4D 61 69 6E 0C 00 14 00 3B000001E0: 01 00 08 43 6F 6D 70 69 6C 65 72 0C 00 1B 00 1C

ASM

MethodVisitor mv = cv.visitMethod( Opcodes.ACC_PRIVATE, "setupLogger", "(Lorg/apache/maven/cli/MavenCli$CliRequest;)Lorg/codehaus/plexus/logging/Logger;", null, null);

mv.visitCode();mv.visitTypeInsn( Opcodes.NEW, "com/github/jcgay/maven/color/logger/AnsiColorLogger");

https://github.com/jcgay/maven-color

Jitescript

An ECMAScript runtime for the JVM

JiteClass

➤ CodeBlock

➤ CodeBlock

➤ CodeBlock

Compiled from "Main.java"public class Main { public Main(); Code: 0: aload_0 1: invokespecial #1 4: return

public static void main... Code: 0: new #2 3: dup 4: invokespecial #3 7: ldc #4 9: invokevirtual #5 12: pop 13: return }

new JiteClass("Main") {{ defineMethod("main", ACC_PUBLIC | ACC_STATIC, sig(void.class, String[].class), new CodeBlock() {{

// ...

}});}};

new JiteClass("Main") {{ defineMethod("main", ACC_PUBLIC | ACC_STATIC, sig(void.class, String[].class), new CodeBlock() {{ new(p(ArrayList.class)); dup(); invokestatic( p(ArrayList.class), ”<init>”, sig(void.class)); ldc(“Hello”) invokevirtual( p(ArrayList.class), “add”, sig(boolean.class, Object.class)); pop(); voidreturn(); }});}};

new JiteClass("Main") {{ defineMethod("main", ACC_PUBLIC | ACC_STATIC, sig(void.class, String[].class), new CodeBlock() {{ getstatic(p(System.class), "out", ci(PrintStream.class));

ldc(“42");

invokevirtual( p(PrintStream.class), "println", sig(void.class, Object.class));

voidreturn(); }});}};

new JiteClass("Main") {{ defineMethod("main", ACC_PUBLIC | ACC_STATIC, sig(void.class, String[].class), new CodeBlock() {{ getstatic(p(System.class), "out", ci(PrintStream.class));

ldc(“42");

invokevirtual( p(PrintStream.class), "println", sig(void.class, Object.class));

voidreturn(); }});}};

0: getstatic #13 3: ldc #15 5: invokevirtual #21 8: return

JiteClass .class

.toBytes()

Brainfu++

> > + + + + + + [ - s + + + + + + + s ] < p

p

>

<

: print a variable

: push a 0 onto the stack

: pop the top value off of the stack

+

-

[

: increment a variable

: decrement a variable

: start a loop

] : end a loop

s : swap the top two values of the stack

This is our machine: An empty stack

> > + + + + + + [ - s + + + + + + + s ] < p

> > + + + + + + [ - s + + + + + + + s ] < p

Push two zeros

0

0

> > + + + + + + [ - s + + + + + + + s ] < p

Inc to 6

0

6

> > + + + + + + [ - s + + + + + + + s ] < p

Loop

0

6

> > + + + + + + [ - s + + + + + + + s ] < p

Loop

0

5

Decrement

> > + + + + + + [ - s + + + + + + + s ] < p

Loop

5

0

Swap

> > + + + + + + [ - s + + + + + + + s ] < p

Loop

5

7

Increment

> > + + + + + + [ - s + + + + + + + s ] < p

Loop

7

5

Swap

> > + + + + + + [ - s + + + + + + + s ] < p

Loop

7

5

End of loop

> > + + + + + + [ - s + + + + + + + s ] < p

Loop

42

0

Repeat until 0

> > + + + + + + [ - s + + + + + + + s ] < p

42

Pop and Print

public JiteClass compile(final String program) { return new JiteClass("Main") {{ defineMethod("main", ACC_PUBLIC | ACC_STATIC, sig(void.class, String[].class), new CodeBlock() {{

// ... }}); }};}

for (String cmd : program.split(“ “)) {

if ("p".equals(cmd)) {

} else if ("+".equals(cmd)) {

} else if ("-".equals(cmd)) {

} else if (">".equals(cmd)) {

} else if ("<".equals(cmd)) {

} else if ("s".equals(cmd)) {

} else if ("[".equals(cmd)) {

} else if ("]".equals(cmd)) {

}}

for (String cmd : program.split(“ “)) {

if ("p".equals(cmd)) {

} else if ("+".equals(cmd)) {

} else if ("-".equals(cmd)) {

} else if (">".equals(cmd)) {

} else if ("<".equals(cmd)) {

} else if ("s".equals(cmd)) {

} else if ("[".equals(cmd)) {

} else if ("]".equals(cmd)) {

}}

if ("p".equals(cmd)) {

dup();

invokestatic( p(String.class), "valueOf", sig(String.class, int.class));

getstatic(p(System.class), "out", ci(PrintStream.class));

swap();

invokevirtual( p(PrintStream.class), "print", sig(void.class, Object.class));

}

if ("p".equals(cmd)) {

brainPrint();

}

else if ("+".equals(cmd)) {

iconst_1();

iadd();

}

else if (“-".equals(cmd)) {

iconst_1();

isub();

}

else if (“>".equals(cmd)) {

iconst_0();

}

else if (“<".equals(cmd)) {

pop();

}

else if (“s".equals(cmd)) {

swap();

}

Stack<LabelNode[]> loopStack = new Stack<LabelNode[]>();

else if (“[“.equals(cmd)) {

LabelNode begin = new LabelNode(); LabelNode end = new LabelNode(); label(begin); dup(); ifeq(end);

loopStack.push(new LabelNode[] {begin, end});}

Stack<LabelNode[]> loopStack = new Stack<LabelNode[]>();

else if (“[“.equals(cmd)) {

LabelNode begin = new LabelNode(); LabelNode end = new LabelNode(); label(begin); dup(); ifeq(end);

loopStack.push(new LabelNode[] {begin, end});}

Stack<LabelNode[]> loopStack = new Stack<LabelNode[]>();

else if (“[“.equals(cmd)) {

LabelNode begin = new LabelNode(); LabelNode end = new LabelNode(); label(begin); dup(); ifeq(end);

loopStack.push(new LabelNode[] {begin, end});}

Stack<LabelNode[]> loopStack = new Stack<LabelNode[]>();

else if (“[“.equals(cmd)) {

LabelNode begin = new LabelNode(); LabelNode end = new LabelNode();

label(begin); dup(); ifeq(end);

loopStack.push(new LabelNode[] {begin, end});}

else if (“]“.equals(cmd)) {

LabelNode[] labelNodes = loopStack.pop();

LabelNode begin = labelNodes[0]; LabelNode end = labelNodes[1];

go_to(begin); label(end);

}

14: dup

15: ifeq 39

...

36: goto 14

39: pop

$ java Compiler “< < + + +”

$ java Main

42

public class BrainCodeBlock extends CodeBlock {

public void brainPrint(String s) { /* ... */ }

public void brainPush(BrainObject o) { /* ... */ }

public BrainObject brainPop() { /* ... */ } public void brainLoop(Predicate<CodeBlock> b) {

// ... }

}

Javassist

CtClass

ClassPool pool = ClassPool.getDefault();CtClass cc = pool.get("test.Rectangle");cc.setSuperclass(pool.get("test.Point"));cc.writeFile();

class ThirdParty { public void run() { fetchData(); processData(); invokeService(); resolveConflicts(); finalize(); }}

ClassPool cp = ClassPool.getDefault();CtClass cc = cp.get(“ThirdParty");CtMethod m = cc.getDeclaredMethod(“fetchData");

m.addLocalVariable("t", CtClass.longType);m.insertBefore( "t = System.currentTimeMillis();");m.insertAfter( “{“ + “ t = System.currentTimeMillis() - t;” + “ System.out.println(\"Time (ms): \" + t);” + ”}"); byte[] byteCode = cc.toBytecode();cc.detach();

http://www.tomsquest.com/blog/2014/01/intro-java-agent-and-bytecode-manipulation/

calling ThirdPartyfetching data...Time (ms): 55processing data...Time (ms): 6032

Make a DSL…

No, make a new JVM Language

(print (* 7 5))

pragprog.com

The Dragon Book

Brainfu** (Jipsy)

List of JVM Bytecode Operations

http://github.com/jkutner/jipsy

http://docs.oracle.com/javase/specs/jvms/se8/html/

Jitescripthttps://github.com/qmx/jitescript

The next time you compile…

Run javap against the class file.

Thank YouJoe Kutner

@codefinger