Tuesday, March 26, 2013

JVM Code Generation with Jasmin

When you write your own programming language, you're tasked with writing a lexer and parser. But, once that's complete, you still have to execute the code somehow. If you're looking to target the JVM as a runtime, you need to compile your source code down to JVM bytecode, which can seem like a daunting task. However, Jasmin, which has been around for many years, is a very useful framework that makes this much easier. This tutorial takes you through the steps of creating a simple Hello World program with Jasmin. The result is a HelloWorld.class file that you can run at the command-prompt with Java. In future tutorials, I'll go through more complex examples, but you have to start somewhere. I put the full code on GitHub: https://github.com/travisdazell/Jasmin-HelloWorld


Step 1: Download Jasmin

You can download Jasmin here: http://jasmin.sourceforge.net.

Download and extract the ZIP file to your PC. I have it installed on my root, so the result is a C:\jasmin-2.4\ directory on my PC.


Step 2: Write the Jasmin code for the HelloWorld class.

Let's create a simple Hello World program and then discuss the code in more detail. Here's the complete example:

; class definition
.class public HelloWorld

; extends java.lang.Object
.super java/lang/Object

; default constructor -- public HelloWorld() { super(); }
.method public ()V
 aload_0
 invokenonvirtual java/lang/Object/()V
 return
.end method

; main method -- public static void main(String args[]) { ... }
.method public static main([Ljava/lang/String;)V
 .limit stack 2     ; we're allocating 2 items we plan to put on the stack
 
 ; push System.out onto the stack
 getstatic java/lang/System/out Ljava/io/PrintStream;
 
 ; push the constant "Hello World" string onto the stack
 ldc "Hello World"
 
 ; print the result that's on the top of the stack
 invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 return
.end method


Let's review the Jasmin code so that you get an understanding of what's involved. The first two lines are fairly self-explanatory. We're just defining the name of the Java class and its superclass. In this example, the superclass is just java.lang.Object.


; class definition
.class public HelloWorld

; extends java.lang.Object
.super java/lang/Object


Note the use of the slash (/) in the package structure. That's one of the differences between actual Java code and Jasmin. The next step is to define the default constructor.


; default constructor -- public HelloWorld() { super(); }
.method public()V
 aload_0
 invokenonvirtual java/lang/Object/()V
 return
.end method

The last section of code is our main method. Within this method, the first thing we do is specify the size of the stack for the method. In our case, we only need a stack size of 2, because we're only going to be putting System.out and the constant "Hello World" string on the stack. The last step is to print the result that's on top of the stack and return.


; main method -- public static void main(String args[]) { ... }
.method public static main([Ljava/lang/String;)V
 .limit stack 2     ; we're allocating 2 items we plan to put on the stack
 
 ; push System.out onto the stack
 getstatic java/lang/System/out Ljava/io/PrintStream;
 
 ; push the constant "Hello World" string onto the stack
 ldc "Hello World"
 
 ; print the result that's on the top of the stack
 invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 return
.end method


Step 3: Save the file and compile it to bytecode

Save the file as HelloWorld.j and then run the following command from the command-prompt:


C:\> java -jar "C:\jasmin-2.4\jasmin.jar" HelloWorld.j

The output will be a HelloWorld.class file that you can run using Java!


No comments:

Post a Comment