Introduction to Java ByteCode Manipulation with ASM

There are several libraries currently available for java bytecode manipulation such as ASM, ApacheBCEL, Javassist and etc. In this article I will discuss about the ASM library, what is it for, how to use it, and how you could resolve some common issues that you may encounter. This will be a series of articles, and the first one will focus on introducing the library and its features.

What is ASM?

ASM is a framework that allows manipulating and generating JVM bytecode. It allows modifying existing classes, programmatically generating new classes and analyzing existing classes, directly in their bytecode (binary) format. It also has some inbuilt algorithms for ceratin analyses as well.

ASM API

ASM provides two basic sets of APIs: A tree-based API and an event-based API.

Tree-based API:

Constructs a tree-like structure of the classes it visits. A ‘ClassNode’ being the root of the tree, it consists of fields, methods, inner classes and other information as children.

Pros:

  • Provides full control over the class that is being visited.
  • Since the entire class is available as a tree, it is easy to manipulate, transform and even rewrite parts of the tree.

Cons:

  • Slower than the event-based API.

Event-based API:

This API primarily based on the visitor pattern and provides two basic visitors: ClassVisitor - for visiting analyzing and transforming an existing class, and ClassWriter - for generating new classes.

Pros:

  • Faster than the tree API.

Cons:

  • Has less control over the currently visited class.
  • Requires to strictly follow the order of the methods of the visitors.
  • Rewriting a class (or parts of it) is not straightforward - Have to generate a new class completely.

However, due to the performance and the lightweight nature of the event-based API, it is being used more often for generating and manipulating bytecode. Hence I will also discuss the event-based API in this article.

Generating Classes

Let's look at how we can generate a simple class using the ASM event-based API.

Note: Here I have used ClassWriter.COMPUTE_MAXS as the flag for the class writer. This tells the AMS to automatically calculate the maximum stack size and the maximum number of local variables of the methods.

The above code will generate a simple class with no methods. To write this as a class file, we can get the bytes usingcw.toByteArray() and write it to a file.

Adding methods

Let's add a java main method for the above class. For simplicity, our main method will not have any logic inside.

Even though the main method is empty, you can see I have added a return statement mv.VisitInsn(Opcodes.RETURN). It is followed by mv.visitMaxs(0,0) which is used to set the mas-stack size for the current method. Since we have asked the ASM to calculate this for us during the ClassWriter initialization, whatever the value we set via mv.visitMaxs(... , ...) will be ignored. However, it is still required to call this method before the mv.visitEnd() is called.

A Complete Sample

Here’s a complete sample with a java class that prints a “Hello world!”.

Running the above sample will generate a class file GeneratedClass.class. You can run this using:

This will print Hello world! in the console.

In the next article I will discuss a bit more detail about the JVM byte codes, methods of the ASM API to be used to emit different bytes codes, and how to write some complex logic from bytecode level using the ASM.

Compiler Developer | Statistician | Machine Learning Enthusiast

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store