Mr. Hat's Lisbon Arithmetic

A series of not so random ones and zeros.

Installing ASMifier

With the release of Java 8, it is time to figure out the impact of the new language features on some of my projects. To get a better idea of what is going on in the bytecode, I have been transforming some compiled classes into ASM class visitors, using ASMifier. I will be sharing those in some later posts, but I thought I would get started with some instruction on how to install and use ASMifier to look at bytecode.

So, what is ASM and ASMifier

For those not familure with ASM, it is a bytecode processing library. It is lower level than other bytecode inspection/generation tools I have used, but it gives you a lot of control over what is going on in class files that you generate. It is great for transforming existing bytecode or generating bytecode from scratch.

ASMifier is a tool that transforms a class file into the source code that would generate that class using ASM’s APIs. This is really convienent for building class transforms, since you can easily see the class calls that are going to be made by visiting a particular class file. When I am creating class transforms, my workflow usually looks like this:

  1. create an example of the transform’s input and output in java
  2. compile and run asmifier on both classes
  3. create a transform that maps the input example to the output example.

This process has worked well for me and lends itself to a generalized JUnit test class. Perhaps we can cover that in a future post.

Getting ASMifier Installed

ASMifier is located in the asm-util.jar file and requires the asm.jar file to operate. ASM provides an all jar, so an easy way to get ASMifier running is to get the all jar on the path, prepended a shell script to it, and mark it as executable. Assuming that ~/bin is on your path, these commands will add an asmifier command to your shell.

printf '#!/bin/sh\n\nexec java -classpath "$0" org.objectweb.asm.util.ASMifier "$@"\n\n' > ~/bin/asmifier
curl 'http://central.maven.org/maven2/org/ow2/asm/asm-all/5.0.1/asm-all-5.0.1.jar' >> ~/bin/asmifier
chmod +x ~/bin/asmifier

Now, we can get some usage information by typing asmifier in the terminal.

asmifier
Prints the ASM code to generate the given class.
Usage: ASMifier [-debug] <fully qualified class name or class file name>

Using ASMifier

Now that we have ASMifier installed, let’s run a quick transform on a simple “Hello World!” class file. To begin, we will need some compiled source. Write this class file into a file called HelloWorld.java.

HelloWorld.java (HelloWorld.java) download
1
2
3
4
5
public class HelloWorld {
  public static void main( String... args) {
    System.out.println("Hello World!");
  }
}

Then compile it with javac, creating HelloWorld.class.

javac HelloWorld.java

Now we can run asmifier on this class file, and see the method calls that will be made when visiting it.

asmifier HelloWorld.class > HelloWorldDump.java
HelloWorldDump.java (HelloWorldDump.java) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.util.*;
import org.objectweb.asm.*;
import org.objectweb.asm.attrs.*;
public class HelloWorldDump implements Opcodes {

public static byte[] dump () throws Exception {

ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;

cw.visit(52, ACC_PUBLIC + ACC_SUPER, "HelloWorld", null, "java/lang/Object", 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 + ACC_STATIC + ACC_VARARGS, "main", "([Ljava/lang/String;)V", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("Hello World!");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
cw.visitEnd();

return cw.toByteArray();
}
}

Note: Unfortunately, package org.objectweb.asm.attrs was removed in ASM 5.0, but this was not reflected in the ASMifier tool, so you will need to remove that import if you want to compile this generated file.

That is it for this post. In some future articles, we will look at the way lambdas and method references are represented in ASM class visitors. Stay tuned.

Welcome!

I am Christian Thomas Trimble and what follows is a series of not so random ones and zeros. Expect to see posts about programming, math and well researched nonsense. Enjoy!

- Management