The following is an example of code that will generate a class using ClassMaker, alongside the equivalent java code.
Java code | ClassMaker code |
public interface Unary { int unary(int a); } public class Factorial implements Unary { public int unary(int n) { int x; x= 1; while (n>0) { x = x * n; n--; } return x; } } |
public class FactorialMaker extends ClassMakerBase { public void code() { Implements(Unary.class); Method("unary", int.class, ACC_PUBLIC); Declare("n", int.class, 0); Begin(); Declare("x", int.class, 0); Set("x", Literal(1)); Loop(); While(GT(Get("n"), Literal(0))); Set("x", Mult(Get("x"), Get("n"))); Eval(Dec("n")); EndLoop(); Return(Get("x")); End(); } } |
The relationship is obvious and quite similar, apart from the change of notation from infix to prefix. The code on the left is easier to write so you might ask "Why bother". Well, if you have ever tried to generate a proxy class, write an expression interpreter or develop a back end code generator for a compiler then you will appreciate what !ClassMaker can do for you.
However, this picture only tells half the story. We now have to use the code above to instantiate an object.
To create an instance from a java class ... | To create an instance from ClassMaker ... |
Unary exec = (Unary)new Factorial(); assertEquals("factorial(1)", 1, exec.unary(1)); assertEquals("factorial(2)", 2, exec.unary(2)); assertEquals("factorial(3)", 6, exec.unary(3)); assertEquals("factorial(4)", 24, exec.unary(4)); |
ClassMaker maker = new FactorialMaker(); Class myClass = maker.defineClass(); Unary exec = (Unary)myClass.newInstance(); assertEquals("factorial(1)", 1, exec.unary(1)); assertEquals("factorial(2)", 2, exec.unary(2)); assertEquals("factorial(3)", 6, exec.unary(3)); assertEquals("factorial(4)", 24, exec.unary(4)); |
These two samples of code do very similar things. The code on the left loads a java class from a stream of bytes located in a file called "Factorial.class". The code on the right loads a java class from a stream of bytes located within the !FactorialMaker object. The bytecodes on the left were created at compile time; the bytecodes on the right are created at runtime.
I will leave out the gory details for the moment, mainly because this is a work in progress, but you are welcome to download the code and run it in an Eclipse environment. There are extensive unit tests that serve as examples, however, the unit tests are not complete, the code is not complete and the API is still evolving.
So where am I and where am I going with this?
I created the first test class for this project on February 27, 2010. That was a test case for the ClassFileWriter class, which is straight out of the Mozilla Rhino Javascript compiler. I added the test cases because !ClassFileWriter didn't have any and I wanted to figure out how it worked. In all of the testing I did I found one trivial bug. It is an awesome piece of code and I thank Roger Lawrence for writing it. It's simple API and minimalist approach were the inspiration for the API in ClassMaker. I have extended ClassFileWriter to generate both versions of switch structures; it originally only had one.
I created the ClassMaker.java file on August 4th, 2010. I started with simple arithmetic expressions
and it can now generate most high level statements, including :
Other supported features include:
The code is feature completion and there are only some odds and ends to do around the code.
The last item is probably the most important. While I have tried to be diligent with the test cases, all I can assert is that everything I have tested works. Putting the library to use will uncover situations I have not tested and will consequently uncover bugs and useful enhancements.
Donald Strong, Melbourne, Australia.