Literals
Literals are numbers, strings and boolean values. They are created in ClassMaker using the Literal method.
The following table displays the way they are created and their equivalent in Java.
Java code | ClassMaker code |
---|---|
234.56789D 223.345F 2000000L 200 (short)32000 (byte)200 'Z' true "Hello World" | Literal(234.56789D) Literal(123.456F) Literal(2000000L) Literal(200) Literal((short)32000) Literal((byte)200) Literal('Z') Literal(true) Literal("Hello World") |
The Literal(int)
method deserves special mention. Even though it takes an
int
as a parameter, the Type
returned may correspond to a
byte
or short
depending upon the actual value used.
This is so that int literals can be assigned to byte and short variables without
casting. If the literal is assigned to anything else it will be automatically promoted.
Reference keywords
There are three keywords in java that deal with references.
These are null
, this
and super
.
Each has its own method in ClassMaker.
Java code | ClassMaker code |
null this super | Null() This() Super() |
Simple Arithmatic
Simple arithmetic involves unary and binary operations on numbers.
Each operator has a separate method in ClassMaker, but the method may be applied to any numeric type.
The following table displays example expressions in ClassMaker and their equivalent in Java.
Java code | ClassMaker code |
x + 5 x - 7 4 * 2 6 / 2 7 % 3 -a a ^ 3 a & 3 a | 3 ~ a a << 3 a >> 3 a >>> 3 |
Add(Get("x"), Literal(5)) Subt(Get("x"), Literal(7)) Mult(Literal(4), Literal(2)) Div((Literal(6), Literal(2)) Rem((Literal(7), Literal(3)) Neg(Get("a")) Xor(Get("a"), Literal(3)) And(Get("a"), Literal(3)) Or(Get("a"), Literal(3)) Inv(Get("a")) SHL(Get("a"), Literal(3)) SHR(Get("a"), Literal(3)) USHR(Get("a"), Literal(3)) |
The Literal(int)
method deserves special mention. Even though it takes an
int
as a parameter, the Type
returned may correspond to a
byte
or short
depending upon the actual value used.
This is so that integer literals can be assigned to byte and short variables without
casting. If the literal is assigned to anything else it will be automatically promoted.
In many cases an operand will be automatically promoted. Numeric promotion in ClassMaker
follows almost the same rules as numeric promotion in Java. See the section on Numeric promotion
for a detailed description.
Access & Assignment
ClassMaker uses Get
to access variables and Set
to
assign variables.
The methods are overloaded to handle local variables, member variables and static variables.
Java code | ClassMaker code |
a = 2 b = a obj.i = a b = obj.i MyClass.s = a b = MyClass.s |
Set("a", Literal(2)) Set("b", Get("a")) Set(Get("obj"), "i", Get("a")) Set("b", Get(Get("obj"), "i")) Set("MyClass", "s", Get("a")) Set("b", Get("MyClass", "s")) |
Increment and decrement
The increment and decrement operators have side effects. When the side effect takes place
in java is determined by whether the operator appears before or after the operand. In
ClassMaker a separate method call is used.
Inc
and PostInc
are overloaded to handle local
variables, member variables and static variables.
Java code | ClassMaker code |
++i i++ ++obj.i obj.i++ ++MyClass.i MyClass.i++ |
Inc("i") PostInc("i") Inc(Get("obj"), "i") PostInc(Get("obj"), "i") Inc("MyClass", "i") PostInc("MyClass", "i") |
Comparisons
Comparison arithmetic involves unary and binary operations that return boolean.
Each operator has a separate method in ClassMaker, but the method may be applied to any numeric type.
The following table displays example comparisons in ClassMaker and their equivalent in Java.
Java code | ClassMaker code |
a == b a != b a < b a > b a <= b a >= b !a |
EQ(Get("a"), Get("b")) NE(Get("a"), Get("b")) LT(Get("a"), Get("b")) GT(Get("a"), Get("b")) LE(Get("a"), Get("b")) GE(Get("a"), Get("b")) Not(Get("a")) |
Logic expressions
Logic expressions in Java use the &&
and ||
operators. These
operators are difficult to implement in bytecode because they involve shortcut evaluation.
The equivalent operators in ClassMaker are implemented using the
Logic
, AndThen
and OrElse
methods.
Logic expressions are used in If
statements, While
statements
and For
statements. The conditions for these statements all require a
boolean expression.
Java code | ClassMaker code |
a && b a || b a && b && c a || b || c (a && b) || c (a || b) && c (a && b) || (c && d) (a || b) && (c || d) |
Logic(AndThen(Get("a")), Get("b")) Logic(OrElse(Get("a")), Get("b")) Logic(AndThen(AndThen(Get("a")), Get("b")), Get("c")) Logic(OrElse(OrElse(Get("a")), Get("b")), Get("c")) Logic(OrElse(AndThen(Get("a")), Get("b")), Get("c")) Logic(AndThen(OrElse(Get("a")), Get("b")), Get("c")) Logic(OrElse(AndThen(Get("a")), Get("b")), Logic(AndThen(Get("c")), Get("d"))) Logic(AndThen(OrElse(Get("a")), Get("b")), Logic(OrElse(Get("c")), Get("d"))) |
Generating the byte-code for shortcut evaluation of ||
and &&
is not straight forward.
The byte-code must be generated sequentially so a separate method must be called when a boolean
value is tested and at the end of the expression to be shortcut.
The following example shows how this is done. The ClassMaker equivalent of
(a && b)
is expanded out to show the order in which the methods are called.
Variables a
and b
are assumed to hold boolean (1 or 0) values.
On the right of the example is the pseudo byte code that would be generated.
ClassMaker code | Generated byte code |
Logic(AndThen(Get("a")), Get("b")) 1. Get("a") 2. AndThen( ... ), 3. Get("b") 4. Logic( ... , ... ) |
1. LOAD a // Push a local variable (a) onto the stack 2. DUP // Duplicate the value so it still exists after the jump IF_Zero GOTO L1 // Jump to L1 if the value is false POP // Otherwise, discard the duplicate value 3. LOAD b // Push another local variable (b) onto the stack 4. L1: NOP // Mark the label L1. // The value on the stack is the result of (a && b). |
The following example expands out a more complex example of (a && b) || (c && d)
.
Java code | ClassMaker code |
(a && b) || (c && d) |
Logic(OrElse(AndThen(Get("a")), Get("b")), Logic(AndThen(Get("c")), Get("d"))) Get("a") AndThen( ... ) Get("b") OrElse( ... , ... ) Get("c") AndThen( ) Get("d") Logic( ... , ... ) Logic( ... , ... ) |
Arrays
Java code | ClassMaker code |
a = b[0] a = b[c] a = b[0][c] x = ++this.values[0] x = this.values[0]-- b[1] = a b[c] = a b[0][c] = a this.values[0] = ++x this.values[0] = x-- |
Set("a", GetAt(Get("b"), Literal(0)) Set("a", GetAt(Get("b"), Get("c")) Set("a", GetAt(GetAt(Get("b"), Literal(0), Get("c")) Set("a", IncAt(Get(This(), "values"), Literal(0)) Set("a", PostDecAt(Get(This(), "values"), Literal(0)) SetAt(Get("b"), Literal(1), Get("a")) SetAt(Get("b"), Get("c"), Get("a")) SetAt(GetAt(Get("b"), Literal(0)), Get("c"), Get("a")) SetAt(Get(This(), "values"), Literal(0), Inc("x")) SetAt(Get(This(), "values"), Literal(0), PostDec("x")) |
Strings
ClassMaker supports the concatenation of strings with the add +
operator.
If either operand is of type String
the other operand will be converted to a String
and the result will be a StringBuffer
. These automatically created StringBuffer
s are
converted back to a String
when the result is assigned to a variable or passed as an actual
parameter to a method call. An Object
is converted to a String
by calling its toString
method.
Java code | ClassMaker code | Result |
"Hello" + " World" "Total = " + total 360 + " degrees" 12 + 3 + " widgets" "widgets " + 12 + 3 |
Add(Literal("Hello"), Literal(" World")) Add(Literal("Total = "), Get("total")) Add(Literal(360), Literal(" degrees")) Add(Add(Literal(12), Literal(3)), Literal(" widgets")) Add(Add(Literal("widgets "), Literal(12)), Literal(3)) |
Hello World Total = 256 360 degrees 15 widgets widgets 123 |
Eval
Most expressions leave a result on the stack. If the expression is a statement then the result
must be popped off the stack before the next statement begins. If this is not done consistently
the class verifier may throw a verification exception when the class is loaded. This would
typically happen when a loop or branch creates multiple paths to the end of a method and
the stack size differs depending upon which path is taken.
ClassMaker provides the Eval
method to dispose of the result of an expression.
An Eval
call should surround all expression statements generated by ClassMaker,
including calls to methods that return void
. Eval
will figure out
whether or not there is a value to be discarded.
Eval
should not be called around compound statements like if statements and loops
as they cannot leave a result on the stack.
Java code | ClassMaker code |
--a; obj.i++; obj.b = 12345; |
Eval(Dec("a")); Eval(PostInc(Get("obj"), "i")); Eval(Set(Get("obj"), "b"), Literal(12345)); |