Friday, September 04, 2009

Using AsmNodeBuilder to Test Bytecodes with Pleasure

DSL of Groovy never made me bored. I am working on some bytecode transformation and having a number of tests to run.
Testing low-level transformation means dealing with JVM instructions. For example,

ILOAD 0
INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;
LDC 0
INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;
INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter
             .compareLessThan(Ljava/lang/Object;Ljava/lang/Object;)Z
IFEQ L2


There is class MethodNode in the tree package of ASM. The class has the field instructions of class InsnList, which contains its method body. In the following, I have InsnList insn = methodNode.instructions; to perform some optimisation over it.

Before making AsmNodeBuilder, I had to create
a test data using Asm's Tree Nodes. Something looks like:

insn.add(new VarInsnNode(ILOAD, 0))

With Groovy and AsmNodeBuilder, now I can write:

insn.append {
  iload 0
}


When comparing results, I had to do:

assert insn.get(0).opcode == ILOAD
assert insn.get(0).var    == 0


Now, it becomes:

assertEquals asm { iload 0 }, insn[0]

Of course, I can also do:

assertEquals asm {
  iload 0
  invokestatic Integer,"valueOf",[int],Integer
}, insn


to assert all instructions in the list.

Look fun? Convinced? There are two classes you may use:

- InsnListHelper.groovy
- AsmNodeBuilder.java (generated from gen_asm_builder.groovy)

Here's how to
  1. call InsnListHelper.install() in the very beginning of your Groovy test case.
  2. Write a test case.
  3. use <InsnList>.append { ... } to create a method body.
  4. do your transformation.
  5. assert the result against an asm { ... } block. Note that Groovy's power assertion won't work with the block for some reasons, use assertEquals instead.
You can also look at an example here.


Sunday, July 26, 2009

Improved Complex Number in Groovy

Thank you for last comment in the previous blog. Here's the improved version of Complex Number.

Number.metaClass.plus = { n ->
  if(n instanceof Imaginary) {
    new Complex(r:delegate, i:n.v)
  }
}
Number.metaClass.getI = { ->
  new Imaginary(v: delegate)
}

class Complex {
  def r
  def i
  String toString() {
    "$r + ${i}i"
  }
}
class Imaginary {
  def v
  def plus(n) {
    if(n instanceof Number) new Complex(r:n, i:this.v)
  }
}

def b = 2 + 2.4.i
def c = 4.2.i + 2
println b
println b.class
println c
println c.class


Thursday, June 11, 2009

Complex Number in Groovy

Here's a quick note of making Groovy to support complex numbers:

1. override number operations via metaclass:
Number.metaClass.plus = { n ->
  if(n instanceof Imaginary) {
    return new Complex(r:delegate, i:n.v)
  }
}

2. define class Complex to hold real, r, and imaginary, i, parts
class Complex {
  def r
  def i
  String toString() {
    "$r + ${i}i"
  }
}


3. create class Imaginary to be a wrapper class (I borrow this idea from Scala's implicit converter).
class Imaginary {
  def v
}


4. define i as a global closure. It would be in DefaultGroovyMethods, actually.
def i = { v ->
  new Imaginary(v:v)
}


Then when you write:
def b = 2 + i(2.4)
println b
println b.class


This prints "2 + 2.4i" and the type of b is class Complex.

Saturday, June 06, 2009

Fortress - Hello World

Here's my early experiment of running Fortress Hello world program, which is listed below (taken from the tutorial at MIT by the Fortress team):

component hello
export Executable

run() = println("Hello, World !")

end


I'm a bit surprised that its compilation time is "really" slow. I may be guessing that it's caused by type inference in the Fortress compiler. JVM 1.6.0_12 was used in the experiment. One of my quick conclusion is that Fortress is not suitable for scripting. But I think its compiled program will not be slow (as I mentioned that I am suspecting its type inference engine that makes scripting slow).

Here's 5 times of running:

chanwit@lb /cygdrive/c/fortress/fortress_3625
$ time `bin/fortress chanwit/hello.fss`
bash: Hello,: command not found

real    0m11.393s
user    0m0.244s
sys     0m0.199s

chanwit@lb /cygdrive/c/fortress/fortress_3625
$ time `bin/fortress chanwit/hello.fss`
bash: Hello,: command not found

real    0m11.486s
user    0m0.181s
sys     0m0.214s

chanwit@lb /cygdrive/c/fortress/fortress_3625
$ time `bin/fortress chanwit/hello.fss`
bash: Hello,: command not found

real    0m11.412s
user    0m0.213s
sys     0m0.215s

chanwit@lb /cygdrive/c/fortress/fortress_3625
$ time `bin/fortress chanwit/hello.fss`
bash: Hello,: command not found

real    0m11.377s
user    0m0.197s
sys     0m0.198s

chanwit@lb /cygdrive/c/fortress/fortress_3625
$ time `bin/fortress chanwit/hello.fss`
bash: Hello,: command not found

real    0m11.432s
user    0m0.229s
sys     0m0.198s