Let's look at an Oolong program in action. This is the source to the "Hello, world" program written in Oolong, as it would appear in an Oolong source file hello.j:
.class public hello .super java/lang/Object .method public static main([Ljava/lang/String;)V .limit stack 2 .limit locals 1 getstatic java/lang/System/out Ljava/io/PrintStream; ldc "Hello, world" invokevirtual java/io/PrintStream/println (Ljava/lang/String;)V return .end method .end class
2.1.1 .class and .super Directives
Lines that begin with periods (.) are called directives. The first directive is .class, which tells the Oolong assembler the name of the class being compiled. In the example, the name of the class being declared is hello. If the .class directive is missing, then the name of the class is assumed to be the same as the name of the source file, minus the .j extension.
The .class directive may also contain a set of modifiers that control access to the class. This class is declared public, meaning that it may be accessed from any other class in the JVM. This is identical to declaring a class public in Java.
The rest of the file up to the next .end class is considered part of the same class. The .end class directive is optional. If you don't include it, then the rest of the file is all part of the same class. You may have more than one class definition per file, as long as you end each class definition with an .end class directive.
The next line after the .class directive contains a .super directive. The .super directive tells Oolong the name of the superclass of this class. This example declares the superclass to be java/lang/Object, which is the default. The .super directive works like the extends clause in a Java program.
The name java/lang/Object names the same class as java.lang.Object does in Java. The JVM uses slashes (/) instead of periods (.) to separate parts of class names. If you were to look inside a class file created by compiling a Java program, you would see that all the periods in class names had been converted to slashes. Because Oolong is closer to the JVM class file format than Java, we use slashes instead of periods.
The .class and .super directives in this example are equivalent to the Java
public class hello extends java.lang.Object
In Oolong, you must use the full name of the class. In Java, this line is equivalent to
public class hello extends Object
because Java assumes that by Object you mean the particular Object found in the package java.lang. The class file does not make this assumption, so you must write out the full name of every class. That can get tiring, but it ensures that the class file is not ambiguous. This saves the JVM time trying to figure out which class you mean, and it ensures that you get the same result each time, even if new classes are added to the system.
2.1.2 main Method
The .method directive marks the beginning of a new method. Every line after that is a part of the method until the .end method directive. The .method directive here is
.method public static main([Ljava/lang/String;)V
The .method directive names the method being created. In this case, the method is named main.
Before the method name is a list of access keywords, which control how the class can be used. This method is marked public, meaning that it can be used by anybody, and static, meaning that it isn't attached to any particular instance of the class. Unlike Java, the access keywords may come in any order.
Also unlike Java, the return type of the method does not appear at the end of the list of keywords. Instead, the arguments and return types are written together in the descriptor following the method name. The descriptor is a way of expressing a type as letters and symbols. A method descriptor contains the types of the arguments between parentheses, followed by the return type.
Most types are represented by a single character: V for void, I for int, and so on. A left bracket ([) means an array of whatever type follows. L means an object of the type named by everything up to the next semicolon. Thus, Ljava/lang/String; is the type written as java.lang.String in Java, and [Ljava/lang/String; is an array of java/lang/String. The complete list can be found in section 2.5.
The descriptor of main is ([Ljava/lang/String;)V. This says that main takes an array of strings, and returns a void.
The .limit directives tell how much space to allocate for the execution of this method: .limit stack puts an upper limit on how many slots on the operand stack the program will use; .limit locals specifies the number of local variable slots that will be used. The Oolong assembler will guess if the directive isn't given.
The remaining lines up to .end method represent JVM instructions. The Oolong assembler translates the instructions into bytecodes. The first word of each instruction is called a mnemonic. Each mnemonic corresponds to an opcode in the class file, which is a single byte representing an instruction. It's called a mnemonic because it's supposed to be easier to remember than the actual opcode for the instruction. The mnemonic may be followed by several arguments. The allowable arguments depend on the mnemonic. They provide more detail about how the instruction should operate.
Let's look at what the instructions mean, one by one. The first instruction is
getstatic java/lang/System/out Ljava/io/PrintStream;
This instruction tells the JVM to get the value of the field out from the class java/lang/System. This is a static field, meaning that it is a property of the class as a whole instead of any individual object. The value of the object obtained from this operation is expected to be a java/io/PrintStream object. A reference to this object is placed on the stack.
The second instruction is
ldc "Hello, world"
This causes the JVM to create a java/lang/String object with the value Hello, world. This object is placed on the stack above the out object. The stack now looks like this:
The next instruction is
The invokevirtual instruction is used to invoke a method on an object. The arguments to the instruction name the method to be called (println), the class in which the method is to be found (java/io/PrintStream), and the descriptor of the method ((Ljava/lang/String;)V).
The JVM checks that the arguments that actually appear on the stack correspond to those expected by the method. The target of the invocation should be a java/io/PrintStream; that's the out object loaded by the first instruction. The argument should be a String; that's the Hello, world string loaded by the second instruction. The method is called, which removes both elements from the stack. The method does its job, then returns nothing (void), which means that the stack is empty.
The final instruction is
This instruction terminates the method and causes control to return to whoever called the method.