Declaring methods
Methods are declared in !ClassMaker using the following methods.
public void Method(String methodName, Class returnType, int methodModifiers) throws ClassMakerException public void Method(String methodName, Type returnType, int methodModifiers) throws ClassMakerException public void Begin() throws ClassMakerException public void End() throws ClassMakerException
There are two versions of Method
and Declare
.
Most of the time you will use the version that takes a java reflection Class
.
The version that takes a !ClassMaker Type
is only necessary when you are passing a class
that you are generating at runtime.
Java code | ClassMaker code |
String name; public void exec() { name = "Hello World"; } |
Declare("name", String.class, ACC_PUBLIC); Method("exec", void.class, ACC_PUBLIC); Begin(); Eval(Set(This(), "name", Literal("Hello World"))); End(); |
Valid bitmasks for the methodModifiers
of a Method
call include ACC_PUBLIC
,
ACC_PROTECTED
or ACC_PRIVATE
. The modifiers may be bitwise OR
ed with
ACC_STATIC
and ACC_FINAL
, as appropriate.
Provide a zero (0) value if there are no modifiers.
Formal Parameters
Formal parameters are declared in ClassMaker
using the following methods.
public void Declare(String name, Class javaClass, int modifiers) throws ClassMakerException public void Declare(String name, String typeName, int modifiers) throws ClassMakerException public void Declare(String name, Type type, int modifiers) throws ClassMakerException
Java code | ClassMaker code |
public void setName(String name) { this.name = name; } |
Method("setName", void.class, ACC_PUBLIC); Declare("name", String.class, 0); Begin(); Eval(Set(This(), "name", Get("name"))); End(); |
The only valid values for the modifiers
of a Declare
call when used to declare a
formal parameter are ACC_FINAL
or zero (0).
Returning values
These methods are used in ClassMaker
to return from a method.
public void Return() throws ClassMakerException public void Return(Type type) throws ClassMakerException
The former method must be used for void
methods and the latter for any method that returns a value.
A call to Return
may be required as the last statement of a method, even if that call is unreachable.
Java code | ClassMaker code |
String name; public String getName() { return name; } |
Method("getName", String.class, ACC_PUBLIC); Begin(); Return(Get(This(), "name")); End(); |
protected int add(int a, int b) { return a + b; } |
maker.Method("add", int.class, ACC_PROTECTED); maker.Declare("a", int.class, 0); maker.Declare("b", int.class, 0); maker.Begin(); maker.Return(maker.Add(maker.Get("a"), maker.Get("b"))); maker.End(); |
Calling methods
ClassMaker
provides the following methods to perform methods calls.
// Calls to non-static methods public Type Call(Type type, String methodName, CallStack actualParameters) throws ClassMakerException // Calls to static methods public Type Call(Class javaClass, String methodName, CallStack actualParameters) throws ClassMakerException public Type Call(String className, String methodName, CallStack actualParameters) throws ClassMakerException
The first version of Call
is the one you will use most of the time. The other methods are used to generate calls
to static methods.
Java code | ClassMaker code |
run(); obj.run(); obj = ExampleMethodsTest.getInstance(); |
Eval(Call(This(), "run", null)); Eval(Call(Get(This(), "obj"), "run", null)); Eval(Set(This(), "obj", Call(ExampleMethodsTest.class, "getInstance", null))); |
Calling methods with parameters
All of the Call
methods above take a CallStack
as their third parameter.
ClassMaker
provides the following methods to create a call stack.
public CallStack Push() throws ClassMakerException; public CallStack Push(Type param) throws ClassMakerException; public class CallStack { public CallStack Push(Type param) throws ClassMakerException; }
These methods can be daisy-chained together to create a call stack of actual parameters.
The Types
on the call stack will be used by the method resolver to determine
which method to call.
Push( <value1> ).Push( <value2> ).Push( <value3> );
Java code | ClassMaker code |
x = add(1, 2); y = obj.add(x, 3); ExampleMethodsTest.setInstance(this); |
Eval(Set(This(), "x", Call(This(), "add", Push(Literal(1)).Push(Literal(2))))); Eval(Set(This(), "y", Call(Get(This(), "obj"), "add", Push(Get(This(), "x")).Push(Literal(3))))); Eval(Call(ExampleMethodsTest.class, "setInstance", Push(This()))); |
Note that Push()
provides an empty CallStack
. An empty CallStack
will be created if
the callStack
parameter is null
.
Constructors
Two special methods are provided to assist with creating constructor methods.
public void Init(ClassType classType, CallStack actualParameters) throws ClassMakerException public ClassType Super() throws ClassMakerException
The Init
method is used to invoke a constructor method. The Super()
method
pushes a reference to the current instance onto the stack. It differs from This()
in that it returns the type of the parent class.
Every class you generate must have at least one constructor. A typical
constructor looks like this.
Method("<init>", void.class, ACC_PUBLIC); Begin(); Init(Super(), null); Return(); End();
You can provide your constructor either in the code()
method
of ClassMakerBase
or by overriding the special defaultConstructor()
method.
ClassMakerBase
will provide a default constructor if you don't.
A constructor method must be called "<init>"
and must include a call to a
constructor in the super class with an appropriate CallStack
.
Init(Super(), ... );
Abstract methods
Interface methods and abstract methods do not have a body.
This is indicated in !ClassMaker using the Forward
method.
This method is used instead of providing a body for the method being generated.
public void Forward() throws ClassMakerException
An abstract method in an abstract class must be declared as such using the ClassMaker.ACC_ABSTRACT
bit mask.
All methods in an interface are implicitly abstract so the ACC_ABSTRACT
bit mask is optional.
Java code | ClassMaker code |
public abstract int getId(); public abstract void setId(int value); |
Method("getId", int.class, ACC_PUBLIC | ACC_ABSTRACT); Forward(); Method("setId", void.class, ACC_PUBLIC | ACC_ABSTRACT); Declare("value", int.class, 0); Forward(); |
Forward declarations
ClassMaker
is a single pass byte-code generator by default. This means that
a method cannot be used until it has been declared. This creates a problem if two
methods mutually call each other. ClassMaker
resolves this issue by allowing
methods to be forward declared using the Forward
method.
public void Forward() throws ClassMakerException
Methods that are used before they are declared must be declared twice. The
first time the method is declared without a body using the Forward
method.
This declaration must occur before the method is first used.
The second declaration of the method, which provides the body of the method,
may be declared anywhere after the forward declaration.
Both the forward declaration and the actual declaration of the method must
have the same parameters otherwise the generator will complain.
maker.Method("add", int.class, ClassMaker.ACC_PRIVATE); maker.Declare("a", int.class, 0); maker.Declare("b", int.class, 0); maker.Forward(); maker.Method("eval", int.class, ClassMaker.ACC_PUBLIC); maker.Begin(); maker.Return(maker.Call(maker.This(), "add", maker.Push(maker.Literal(1)).Push(maker.Literal(2)))); maker.End(); maker.Method("add", int.class, ClassMaker.ACC_PRIVATE); maker.Declare("a", int.class, 0); maker.Declare("b", int.class, 0); maker.Begin(); maker.Return(maker.Add(maker.Get("a"), maker.Get("b"))); maker.End();
In this example the add
method is forward declared and then called in the eval
method.
The actual declaration of the add
method occurs after it is first used.
There is no equivalent in java of a forward declaration, however there is forward declaration
in C and C++ which are the predecessors of java and upon which the java syntax is based. The
original Kernigan and Richie (K&R) C also had a style of declaring formal parameters similar to that
used in ClassMaker
.
Note that ClassMaker
may be configured to be a two pass code generator, in which case it is
not necessary to forward declare methods.
Recursive data structures
There are times when a class needs to refer to itself, such as when building recursive data structures
like linked lists and binary trees.
This method returns the Type
of the class currently being generated.
public ClassType getClassType()
The following example shows how one might recursively search through a linked list.
Java code | ClassMaker code |
String name; Link next; public Link find(String target) { if (target.equals(name)) return this; else return next.find(target); } |
Declare("name", String.class, 0); Declare("next", getClassType(), 0); Method("find", getClassType(), ACC_PUBLIC); Declare("target", String.class, 0); Begin(); If(Call(Get("target"), "equals", Push(Get(This(), "name")))); Return(This()); Else(); Return(Call(Get(This(), "next"), "find", Push(Get("target")))); EndIf(); End(); |