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 StringBuffers 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));
|