javassist.compiler.CodeGen Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package javassist.compiler;
import java.util.ArrayList;
import java.util.Arrays;
import javassist.compiler.ast.*;
import javassist.bytecode.*;
/* The code generator is implemeted by three files:
* CodeGen.java, MemberCodeGen.java, and JvstCodeGen.
* I just wanted to split a big file into three smaller ones.
*/
public abstract class CodeGen extends Visitor implements Opcode, TokenId {
static final String javaLangObject = "java.lang.Object";
static final String jvmJavaLangObject = "java/lang/Object";
static final String javaLangString = "java.lang.String";
static final String jvmJavaLangString = "java/lang/String";
protected Bytecode bytecode;
private int tempVar;
TypeChecker typeChecker;
/**
* true if the last visited node is a return statement.
*/
protected boolean hasReturned;
/**
* Must be true if compilation is for a static method.
*/
public boolean inStaticMethod;
protected ArrayList breakList, continueList;
/**
* doit() in ReturnHook is called from atReturn().
*/
protected static abstract class ReturnHook {
ReturnHook next;
/**
* Returns true if the generated code ends with return,
* throw, or goto.
*/
protected abstract boolean doit(Bytecode b, int opcode);
protected ReturnHook(CodeGen gen) {
next = gen.returnHooks;
gen.returnHooks = this;
}
protected void remove(CodeGen gen) {
gen.returnHooks = next;
}
}
protected ReturnHook returnHooks;
/* The following fields are used by atXXX() methods
* for returning the type of the compiled expression.
*/
protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ...
protected int arrayDim;
protected String className; // JVM-internal representation
public CodeGen(Bytecode b) {
bytecode = b;
tempVar = -1;
typeChecker = null;
hasReturned = false;
inStaticMethod = false;
breakList = null;
continueList = null;
returnHooks = null;
}
public void setTypeChecker(TypeChecker checker) {
typeChecker = checker;
}
protected static void fatal() throws CompileError {
throw new CompileError("fatal");
}
public static boolean is2word(int type, int dim) {
return dim == 0 && (type == DOUBLE || type == LONG);
}
public int getMaxLocals() { return bytecode.getMaxLocals(); }
public void setMaxLocals(int n) {
bytecode.setMaxLocals(n);
}
protected void incMaxLocals(int size) {
bytecode.incMaxLocals(size);
}
/**
* Returns a local variable that single or double words can be
* stored in.
*/
protected int getTempVar() {
if (tempVar < 0) {
tempVar = getMaxLocals();
incMaxLocals(2);
}
return tempVar;
}
protected int getLocalVar(Declarator d) {
int v = d.getLocalVar();
if (v < 0) {
v = getMaxLocals(); // delayed variable allocation.
d.setLocalVar(v);
incMaxLocals(1);
}
return v;
}
/**
* Returns the JVM-internal representation of this class name.
*/
protected abstract String getThisName();
/**
* Returns the JVM-internal representation of this super class name.
*/
protected abstract String getSuperName() throws CompileError;
/* Converts a class name into a JVM-internal representation.
*
* It may also expand a simple class name to java.lang.*.
* For example, this converts Object into java/lang/Object.
*/
protected abstract String resolveClassName(ASTList name)
throws CompileError;
/* Expands a simple class name to java.lang.*.
* For example, this converts Object into java/lang/Object.
*/
protected abstract String resolveClassName(String jvmClassName)
throws CompileError;
/**
* @param name the JVM-internal representation.
* name is not exapnded to java.lang.*.
*/
protected static String toJvmArrayName(String name, int dim) {
if (name == null)
return null;
if (dim == 0)
return name;
else {
StringBuffer sbuf = new StringBuffer();
int d = dim;
while (d-- > 0)
sbuf.append('[');
sbuf.append('L');
sbuf.append(name);
sbuf.append(';');
return sbuf.toString();
}
}
protected static String toJvmTypeName(int type, int dim) {
char c = 'I';
switch(type) {
case BOOLEAN :
c = 'Z';
break;
case BYTE :
c = 'B';
break;
case CHAR :
c = 'C';
break;
case SHORT :
c = 'S';
break;
case INT :
c = 'I';
break;
case LONG :
c = 'J';
break;
case FLOAT :
c = 'F';
break;
case DOUBLE :
c = 'D';
break;
case VOID :
c = 'V';
break;
}
StringBuffer sbuf = new StringBuffer();
while (dim-- > 0)
sbuf.append('[');
sbuf.append(c);
return sbuf.toString();
}
public void compileExpr(ASTree expr) throws CompileError {
doTypeCheck(expr);
expr.accept(this);
}
public boolean compileBooleanExpr(boolean branchIf, ASTree expr)
throws CompileError
{
doTypeCheck(expr);
return booleanExpr(branchIf, expr);
}
public void doTypeCheck(ASTree expr) throws CompileError {
if (typeChecker != null)
expr.accept(typeChecker);
}
public void atASTList(ASTList n) throws CompileError { fatal(); }
public void atPair(Pair n) throws CompileError { fatal(); }
public void atSymbol(Symbol n) throws CompileError { fatal(); }
public void atFieldDecl(FieldDecl field) throws CompileError {
field.getInit().accept(this);
}
public void atMethodDecl(MethodDecl method) throws CompileError {
ASTList mods = method.getModifiers();
setMaxLocals(1);
while (mods != null) {
Keyword k = (Keyword)mods.head();
mods = mods.tail();
if (k.get() == STATIC) {
setMaxLocals(0);
inStaticMethod = true;
}
}
ASTList params = method.getParams();
while (params != null) {
atDeclarator((Declarator)params.head());
params = params.tail();
}
Stmnt s = method.getBody();
atMethodBody(s, method.isConstructor(),
method.getReturn().getType() == VOID);
}
/**
* @param isCons true if super() must be called.
* false if the method is a class initializer.
*/
public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid)
throws CompileError
{
if (s == null)
return;
if (isCons && needsSuperCall(s))
insertDefaultSuperCall();
hasReturned = false;
s.accept(this);
if (!hasReturned)
if (isVoid) {
bytecode.addOpcode(Opcode.RETURN);
hasReturned = true;
}
else
throw new CompileError("no return statement");
}
private boolean needsSuperCall(Stmnt body) throws CompileError {
if (body.getOperator() == BLOCK)
body = (Stmnt)body.head();
if (body != null && body.getOperator() == EXPR) {
ASTree expr = body.head();
if (expr != null && expr instanceof Expr
&& ((Expr)expr).getOperator() == CALL) {
ASTree target = ((Expr)expr).head();
if (target instanceof Keyword) {
int token = ((Keyword)target).get();
return token != THIS && token != SUPER;
}
}
}
return true;
}
protected abstract void insertDefaultSuperCall() throws CompileError;
public void atStmnt(Stmnt st) throws CompileError {
if (st == null)
return; // empty
int op = st.getOperator();
if (op == EXPR) {
ASTree expr = st.getLeft();
doTypeCheck(expr);
if (expr instanceof AssignExpr)
atAssignExpr((AssignExpr)expr, false);
else if (isPlusPlusExpr(expr)) {
Expr e = (Expr)expr;
atPlusPlus(e.getOperator(), e.oprand1(), e, false);
}
else {
expr.accept(this);
if (is2word(exprType, arrayDim))
bytecode.addOpcode(POP2);
else if (exprType != VOID)
bytecode.addOpcode(POP);
}
}
else if (op == DECL || op == BLOCK) {
ASTList list = st;
while (list != null) {
ASTree h = list.head();
list = list.tail();
if (h != null)
h.accept(this);
}
}
else if (op == IF)
atIfStmnt(st);
else if (op == WHILE || op == DO)
atWhileStmnt(st, op == WHILE);
else if (op == FOR)
atForStmnt(st);
else if (op == BREAK || op == CONTINUE)
atBreakStmnt(st, op == BREAK);
else if (op == TokenId.RETURN)
atReturnStmnt(st);
else if (op == THROW)
atThrowStmnt(st);
else if (op == TRY)
atTryStmnt(st);
else if (op == SWITCH)
atSwitchStmnt(st);
else if (op == SYNCHRONIZED)
atSyncStmnt(st);
else {
// LABEL, SWITCH label stament might be null?.
hasReturned = false;
throw new CompileError(
"sorry, not supported statement: TokenId " + op);
}
}
private void atIfStmnt(Stmnt st) throws CompileError {
ASTree expr = st.head();
Stmnt thenp = (Stmnt)st.tail().head();
Stmnt elsep = (Stmnt)st.tail().tail().head();
compileBooleanExpr(false, expr);
int pc = bytecode.currentPc();
int pc2 = 0;
bytecode.addIndex(0); // correct later
hasReturned = false;
if (thenp != null)
thenp.accept(this);
boolean thenHasReturned = hasReturned;
hasReturned = false;
if (elsep != null && !thenHasReturned) {
bytecode.addOpcode(Opcode.GOTO);
pc2 = bytecode.currentPc();
bytecode.addIndex(0);
}
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
if (elsep != null) {
elsep.accept(this);
if (!thenHasReturned)
bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
hasReturned = thenHasReturned && hasReturned;
}
}
private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError {
ArrayList prevBreakList = breakList;
ArrayList prevContList = continueList;
breakList = new ArrayList();
continueList = new ArrayList();
ASTree expr = st.head();
Stmnt body = (Stmnt)st.tail();
int pc = 0;
if (notDo) {
bytecode.addOpcode(Opcode.GOTO);
pc = bytecode.currentPc();
bytecode.addIndex(0);
}
int pc2 = bytecode.currentPc();
if (body != null)
body.accept(this);
int pc3 = bytecode.currentPc();
if (notDo)
bytecode.write16bit(pc, pc3 - pc + 1);
boolean alwaysBranch = compileBooleanExpr(true, expr);
bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
patchGoto(breakList, bytecode.currentPc());
patchGoto(continueList, pc3);
continueList = prevContList;
breakList = prevBreakList;
hasReturned = alwaysBranch;
}
protected void patchGoto(ArrayList list, int targetPc) {
int n = list.size();
for (int i = 0; i < n; ++i) {
int pc = ((Integer)list.get(i)).intValue();
bytecode.write16bit(pc, targetPc - pc + 1);
}
}
private void atForStmnt(Stmnt st) throws CompileError {
ArrayList prevBreakList = breakList;
ArrayList prevContList = continueList;
breakList = new ArrayList();
continueList = new ArrayList();
Stmnt init = (Stmnt)st.head();
ASTList p = st.tail();
ASTree expr = p.head();
p = p.tail();
Stmnt update = (Stmnt)p.head();
Stmnt body = (Stmnt)p.tail();
if (init != null)
init.accept(this);
int pc = bytecode.currentPc();
int pc2 = 0;
if (expr != null) {
compileBooleanExpr(false, expr);
pc2 = bytecode.currentPc();
bytecode.addIndex(0);
}
if (body != null)
body.accept(this);
int pc3 = bytecode.currentPc();
if (update != null)
update.accept(this);
bytecode.addOpcode(Opcode.GOTO);
bytecode.addIndex(pc - bytecode.currentPc() + 1);
int pc4 = bytecode.currentPc();
if (expr != null)
bytecode.write16bit(pc2, pc4 - pc2 + 1);
patchGoto(breakList, pc4);
patchGoto(continueList, pc3);
continueList = prevContList;
breakList = prevBreakList;
hasReturned = false;
}
private void atSwitchStmnt(Stmnt st) throws CompileError {
compileExpr(st.head());
ArrayList prevBreakList = breakList;
breakList = new ArrayList();
int opcodePc = bytecode.currentPc();
bytecode.addOpcode(LOOKUPSWITCH);
int npads = 3 - (opcodePc & 3);
while (npads-- > 0)
bytecode.add(0);
Stmnt body = (Stmnt)st.tail();
int npairs = 0;
for (ASTList list = body; list != null; list = list.tail())
if (((Stmnt)list.head()).getOperator() == CASE)
++npairs;
// opcodePc2 is the position at which the default jump offset is.
int opcodePc2 = bytecode.currentPc();
bytecode.addGap(4);
bytecode.add32bit(npairs);
bytecode.addGap(npairs * 8);
long[] pairs = new long[npairs];
int ipairs = 0;
int defaultPc = -1;
for (ASTList list = body; list != null; list = list.tail()) {
Stmnt label = (Stmnt)list.head();
int op = label.getOperator();
if (op == DEFAULT)
defaultPc = bytecode.currentPc();
else if (op != CASE)
fatal();
else {
pairs[ipairs++]
= ((long)computeLabel(label.head()) << 32) +
((long)(bytecode.currentPc() - opcodePc) & 0xffffffff);
}
hasReturned = false;
((Stmnt)label.tail()).accept(this);
}
Arrays.sort(pairs);
int pc = opcodePc2 + 8;
for (int i = 0; i < npairs; ++i) {
bytecode.write32bit(pc, (int)(pairs[i] >>> 32));
bytecode.write32bit(pc + 4, (int)pairs[i]);
pc += 8;
}
if (defaultPc < 0 || breakList.size() > 0)
hasReturned = false;
int endPc = bytecode.currentPc();
if (defaultPc < 0)
defaultPc = endPc;
bytecode.write32bit(opcodePc2, defaultPc - opcodePc);
patchGoto(breakList, endPc);
breakList = prevBreakList;
}
private int computeLabel(ASTree expr) throws CompileError {
doTypeCheck(expr);
expr = TypeChecker.stripPlusExpr(expr);
if (expr instanceof IntConst)
return (int)((IntConst)expr).get();
else
throw new CompileError("bad case label");
}
private void atBreakStmnt(Stmnt st, boolean notCont)
throws CompileError
{
if (st.head() != null)
throw new CompileError(
"sorry, not support labeled break or continue");
bytecode.addOpcode(Opcode.GOTO);
Integer pc = new Integer(bytecode.currentPc());
bytecode.addIndex(0);
if (notCont)
breakList.add(pc);
else
continueList.add(pc);
}
protected void atReturnStmnt(Stmnt st) throws CompileError {
atReturnStmnt2(st.getLeft());
}
protected final void atReturnStmnt2(ASTree result) throws CompileError {
int op;
if (result == null)
op = Opcode.RETURN;
else {
compileExpr(result);
if (arrayDim > 0)
op = ARETURN;
else {
int type = exprType;
if (type == DOUBLE)
op = DRETURN;
else if (type == FLOAT)
op = FRETURN;
else if (type == LONG)
op = LRETURN;
else if (isRefType(type))
op = ARETURN;
else
op = IRETURN;
}
}
for (ReturnHook har = returnHooks; har != null; har = har.next)
if (har.doit(bytecode, op)) {
hasReturned = true;
return;
}
bytecode.addOpcode(op);
hasReturned = true;
}
private void atThrowStmnt(Stmnt st) throws CompileError {
ASTree e = st.getLeft();
compileExpr(e);
if (exprType != CLASS || arrayDim > 0)
throw new CompileError("bad throw statement");
bytecode.addOpcode(ATHROW);
hasReturned = true;
}
/* overridden in MemberCodeGen
*/
protected void atTryStmnt(Stmnt st) throws CompileError {
hasReturned = false;
}
private void atSyncStmnt(Stmnt st) throws CompileError {
int nbreaks = getListSize(breakList);
int ncontinues = getListSize(continueList);
compileExpr(st.head());
if (exprType != CLASS && arrayDim == 0)
throw new CompileError("bad type expr for synchronized block");
Bytecode bc = bytecode;
final int var = bc.getMaxLocals();
bc.incMaxLocals(1);
bc.addOpcode(DUP);
bc.addAstore(var);
bc.addOpcode(MONITORENTER);
ReturnHook rh = new ReturnHook(this) {
protected boolean doit(Bytecode b, int opcode) {
b.addAload(var);
b.addOpcode(MONITOREXIT);
return false;
}
};
int pc = bc.currentPc();
Stmnt body = (Stmnt)st.tail();
if (body != null)
body.accept(this);
int pc2 = bc.currentPc();
int pc3 = 0;
if (!hasReturned) {
rh.doit(bc, 0); // the 2nd arg is ignored.
bc.addOpcode(Opcode.GOTO);
pc3 = bc.currentPc();
bc.addIndex(0);
}
if (pc < pc2) { // if the body is not empty
int pc4 = bc.currentPc();
rh.doit(bc, 0); // the 2nd arg is ignored.
bc.addOpcode(ATHROW);
bc.addExceptionHandler(pc, pc2, pc4, 0);
}
if (!hasReturned)
bc.write16bit(pc3, bc.currentPc() - pc3 + 1);
rh.remove(this);
if (getListSize(breakList) != nbreaks
|| getListSize(continueList) != ncontinues)
throw new CompileError(
"sorry, cannot break/continue in synchronized block");
}
private static int getListSize(ArrayList list) {
return list == null ? 0 : list.size();
}
private static boolean isPlusPlusExpr(ASTree expr) {
if (expr instanceof Expr) {
int op = ((Expr)expr).getOperator();
return op == PLUSPLUS || op == MINUSMINUS;
}
return false;
}
public void atDeclarator(Declarator d) throws CompileError {
d.setLocalVar(getMaxLocals());
d.setClassName(resolveClassName(d.getClassName()));
int size;
if (is2word(d.getType(), d.getArrayDim()))
size = 2;
else
size = 1;
incMaxLocals(size);
/* NOTE: Array initializers has not been supported.
*/
ASTree init = d.getInitializer();
if (init != null) {
doTypeCheck(init);
atVariableAssign(null, '=', null, d, init, false);
}
}
public abstract void atNewExpr(NewExpr n) throws CompileError;
public abstract void atArrayInit(ArrayInit init) throws CompileError;
public void atAssignExpr(AssignExpr expr) throws CompileError {
atAssignExpr(expr, true);
}
protected void atAssignExpr(AssignExpr expr, boolean doDup)
throws CompileError
{
// =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
int op = expr.getOperator();
ASTree left = expr.oprand1();
ASTree right = expr.oprand2();
if (left instanceof Variable)
atVariableAssign(expr, op, (Variable)left,
((Variable)left).getDeclarator(),
right, doDup);
else {
if (left instanceof Expr) {
Expr e = (Expr)left;
if (e.getOperator() == ARRAY) {
atArrayAssign(expr, op, (Expr)left, right, doDup);
return;
}
}
atFieldAssign(expr, op, left, right, doDup);
}
}
protected static void badAssign(Expr expr) throws CompileError {
String msg;
if (expr == null)
msg = "incompatible type for assignment";
else
msg = "incompatible type for " + expr.getName();
throw new CompileError(msg);
}
/* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
*
* expr and var can be null.
*/
private void atVariableAssign(Expr expr, int op, Variable var,
Declarator d, ASTree right,
boolean doDup) throws CompileError
{
int varType = d.getType();
int varArray = d.getArrayDim();
String varClass = d.getClassName();
int varNo = getLocalVar(d);
if (op != '=')
atVariable(var);
// expr is null if the caller is atDeclarator().
if (expr == null && right instanceof ArrayInit)
atArrayVariableAssign((ArrayInit)right, varType, varArray, varClass);
else
atAssignCore(expr, op, right, varType, varArray, varClass);
if (doDup)
if (is2word(varType, varArray))
bytecode.addOpcode(DUP2);
else
bytecode.addOpcode(DUP);
if (varArray > 0)
bytecode.addAstore(varNo);
else if (varType == DOUBLE)
bytecode.addDstore(varNo);
else if (varType == FLOAT)
bytecode.addFstore(varNo);
else if (varType == LONG)
bytecode.addLstore(varNo);
else if (isRefType(varType))
bytecode.addAstore(varNo);
else
bytecode.addIstore(varNo);
exprType = varType;
arrayDim = varArray;
className = varClass;
}
protected abstract void atArrayVariableAssign(ArrayInit init,
int varType, int varArray, String varClass) throws CompileError;
private void atArrayAssign(Expr expr, int op, Expr array,
ASTree right, boolean doDup) throws CompileError
{
arrayAccess(array.oprand1(), array.oprand2());
if (op != '=') {
bytecode.addOpcode(DUP2);
bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
}
int aType = exprType;
int aDim = arrayDim;
String cname = className;
atAssignCore(expr, op, right, aType, aDim, cname);
if (doDup)
if (is2word(aType, aDim))
bytecode.addOpcode(DUP2_X2);
else
bytecode.addOpcode(DUP_X2);
bytecode.addOpcode(getArrayWriteOp(aType, aDim));
exprType = aType;
arrayDim = aDim;
className = cname;
}
protected abstract void atFieldAssign(Expr expr, int op, ASTree left,
ASTree right, boolean doDup) throws CompileError;
protected void atAssignCore(Expr expr, int op, ASTree right,
int type, int dim, String cname)
throws CompileError
{
if (op == PLUS_E && dim == 0 && type == CLASS)
atStringPlusEq(expr, type, dim, cname, right);
else {
right.accept(this);
if (invalidDim(exprType, arrayDim, className, type, dim, cname,
false) || (op != '=' && dim > 0))
badAssign(expr);
if (op != '=') {
int token = assignOps[op - MOD_E];
int k = lookupBinOp(token);
if (k < 0)
fatal();
atArithBinExpr(expr, token, k, type);
}
}
if (op != '=' || (dim == 0 && !isRefType(type)))
atNumCastExpr(exprType, type);
// type check should be done here.
}
private void atStringPlusEq(Expr expr, int type, int dim, String cname,
ASTree right)
throws CompileError
{
if (!jvmJavaLangString.equals(cname))
badAssign(expr);
convToString(type, dim); // the value might be null.
right.accept(this);
convToString(exprType, arrayDim);
bytecode.addInvokevirtual(javaLangString, "concat",
"(Ljava/lang/String;)Ljava/lang/String;");
exprType = CLASS;
arrayDim = 0;
className = jvmJavaLangString;
}
private boolean invalidDim(int srcType, int srcDim, String srcClass,
int destType, int destDim, String destClass,
boolean isCast)
{
if (srcDim != destDim)
if (srcType == NULL)
return false;
else if (destDim == 0 && destType == CLASS
&& jvmJavaLangObject.equals(destClass))
return false;
else if (isCast && srcDim == 0 && srcType == CLASS
&& jvmJavaLangObject.equals(srcClass))
return false;
else
return true;
return false;
}
public void atCondExpr(CondExpr expr) throws CompileError {
booleanExpr(false, expr.condExpr());
int pc = bytecode.currentPc();
bytecode.addIndex(0); // correct later
expr.thenExpr().accept(this);
int dim1 = arrayDim;
bytecode.addOpcode(Opcode.GOTO);
int pc2 = bytecode.currentPc();
bytecode.addIndex(0);
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
expr.elseExpr().accept(this);
if (dim1 != arrayDim)
throw new CompileError("type mismatch in ?:");
bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
}
static final int[] binOp = {
'+', DADD, FADD, LADD, IADD,
'-', DSUB, FSUB, LSUB, ISUB,
'*', DMUL, FMUL, LMUL, IMUL,
'/', DDIV, FDIV, LDIV, IDIV,
'%', DREM, FREM, LREM, IREM,
'|', NOP, NOP, LOR, IOR,
'^', NOP, NOP, LXOR, IXOR,
'&', NOP, NOP, LAND, IAND,
LSHIFT, NOP, NOP, LSHL, ISHL,
RSHIFT, NOP, NOP, LSHR, ISHR,
ARSHIFT, NOP, NOP, LUSHR, IUSHR };
static int lookupBinOp(int token) {
int[] code = binOp;
int s = code.length;
for (int k = 0; k < s; k = k + 5)
if (code[k] == token)
return k;
return -1;
}
public void atBinExpr(BinExpr expr) throws CompileError {
int token = expr.getOperator();
/* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
*/
int k = lookupBinOp(token);
if (k >= 0) {
expr.oprand1().accept(this);
ASTree right = expr.oprand2();
if (right == null)
return; // see TypeChecker.atBinExpr().
int type1 = exprType;
int dim1 = arrayDim;
String cname1 = className;
right.accept(this);
if (dim1 != arrayDim)
throw new CompileError("incompatible array types");
if (token == '+' && dim1 == 0
&& (type1 == CLASS || exprType == CLASS))
atStringConcatExpr(expr, type1, dim1, cname1);
else
atArithBinExpr(expr, token, k, type1);
}
else {
/* equation: &&, ||, ==, !=, <=, >=, <, >
*/
booleanExpr(true, expr);
bytecode.addIndex(7);
bytecode.addIconst(0); // false
bytecode.addOpcode(Opcode.GOTO);
bytecode.addIndex(4);
bytecode.addIconst(1); // true
}
}
/* arrayDim values of the two oprands must be equal.
* If an oprand type is not a numeric type, this method
* throws an exception.
*/
private void atArithBinExpr(Expr expr, int token,
int index, int type1) throws CompileError
{
if (arrayDim != 0)
badTypes(expr);
int type2 = exprType;
if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
if (type2 == INT || type2 == SHORT
|| type2 == CHAR || type2 == BYTE)
exprType = type1;
else
badTypes(expr);
else
convertOprandTypes(type1, type2, expr);
int p = typePrecedence(exprType);
if (p >= 0) {
int op = binOp[index + p + 1];
if (op != NOP) {
if (p == P_INT && exprType != BOOLEAN)
exprType = INT; // type1 may be BYTE, ...
bytecode.addOpcode(op);
return;
}
}
badTypes(expr);
}
private void atStringConcatExpr(Expr expr, int type1, int dim1,
String cname1) throws CompileError
{
int type2 = exprType;
int dim2 = arrayDim;
boolean type2Is2 = is2word(type2, dim2);
boolean type2IsString
= (type2 == CLASS && jvmJavaLangString.equals(className));
if (type2Is2)
convToString(type2, dim2);
if (is2word(type1, dim1)) {
bytecode.addOpcode(DUP_X2);
bytecode.addOpcode(POP);
}
else
bytecode.addOpcode(SWAP);
// even if type1 is String, the left operand might be null.
convToString(type1, dim1);
bytecode.addOpcode(SWAP);
if (!type2Is2 && !type2IsString)
convToString(type2, dim2);
bytecode.addInvokevirtual(javaLangString, "concat",
"(Ljava/lang/String;)Ljava/lang/String;");
exprType = CLASS;
arrayDim = 0;
className = jvmJavaLangString;
}
private void convToString(int type, int dim) throws CompileError {
final String method = "valueOf";
if (isRefType(type) || dim > 0)
bytecode.addInvokestatic(javaLangString, method,
"(Ljava/lang/Object;)Ljava/lang/String;");
else if (type == DOUBLE)
bytecode.addInvokestatic(javaLangString, method,
"(D)Ljava/lang/String;");
else if (type == FLOAT)
bytecode.addInvokestatic(javaLangString, method,
"(F)Ljava/lang/String;");
else if (type == LONG)
bytecode.addInvokestatic(javaLangString, method,
"(J)Ljava/lang/String;");
else if (type == BOOLEAN)
bytecode.addInvokestatic(javaLangString, method,
"(Z)Ljava/lang/String;");
else if (type == CHAR)
bytecode.addInvokestatic(javaLangString, method,
"(C)Ljava/lang/String;");
else if (type == VOID)
throw new CompileError("void type expression");
else /* INT, BYTE, SHORT */
bytecode.addInvokestatic(javaLangString, method,
"(I)Ljava/lang/String;");
}
/* Produces the opcode to branch if the condition is true.
* The oprand is not produced.
*
* @return true if the compiled code is GOTO (always branch).
*/
private boolean booleanExpr(boolean branchIf, ASTree expr)
throws CompileError
{
boolean isAndAnd;
int op = getCompOperator(expr);
if (op == EQ) { // ==, !=, ...
BinExpr bexpr = (BinExpr)expr;
int type1 = compileOprands(bexpr);
// here, arrayDim might represent the array dim. of the left oprand
// if the right oprand is NULL.
compareExpr(branchIf, bexpr.getOperator(), type1, bexpr);
}
else if (op == '!')
booleanExpr(!branchIf, ((Expr)expr).oprand1());
else if ((isAndAnd = (op == ANDAND)) || op == OROR) {
BinExpr bexpr = (BinExpr)expr;
booleanExpr(!isAndAnd, bexpr.oprand1());
int pc = bytecode.currentPc();
bytecode.addIndex(0); // correct later
booleanExpr(isAndAnd, bexpr.oprand2());
bytecode.write16bit(pc, bytecode.currentPc() - pc + 3);
if (branchIf != isAndAnd) {
bytecode.addIndex(6); // skip GOTO instruction
bytecode.addOpcode(Opcode.GOTO);
}
}
else if (isAlwaysBranch(expr, branchIf)) {
bytecode.addOpcode(Opcode.GOTO);
return true; // always branch
}
else { // others
expr.accept(this);
if (exprType != BOOLEAN || arrayDim != 0)
throw new CompileError("boolean expr is required");
bytecode.addOpcode(branchIf ? IFNE : IFEQ);
}
exprType = BOOLEAN;
arrayDim = 0;
return false;
}
private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) {
if (expr instanceof Keyword) {
int t = ((Keyword)expr).get();
return branchIf ? t == TRUE : t == FALSE;
}
return false;
}
static int getCompOperator(ASTree expr) throws CompileError {
if (expr instanceof Expr) {
Expr bexpr = (Expr)expr;
int token = bexpr.getOperator();
if (token == '!')
return '!';
else if ((bexpr instanceof BinExpr)
&& token != OROR && token != ANDAND
&& token != '&' && token != '|')
return EQ; // ==, !=, ...
else
return token;
}
return ' '; // others
}
private int compileOprands(BinExpr expr) throws CompileError {
expr.oprand1().accept(this);
int type1 = exprType;
int dim1 = arrayDim;
expr.oprand2().accept(this);
if (dim1 != arrayDim)
if (type1 != NULL && exprType != NULL)
throw new CompileError("incompatible array types");
else if (exprType == NULL)
arrayDim = dim1;
if (type1 == NULL)
return exprType;
else
return type1;
}
private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE,
NEQ, IF_ICMPNE, IF_ICMPEQ,
LE, IF_ICMPLE, IF_ICMPGT,
GE, IF_ICMPGE, IF_ICMPLT,
'<', IF_ICMPLT, IF_ICMPGE,
'>', IF_ICMPGT, IF_ICMPLE };
private static final int ifOp2[] = { EQ, IFEQ, IFNE,
NEQ, IFNE, IFEQ,
LE, IFLE, IFGT,
GE, IFGE, IFLT,
'<', IFLT, IFGE,
'>', IFGT, IFLE };
/* Produces the opcode to branch if the condition is true.
* The oprands are not produced.
*
* Parameter expr - compare expression ==, !=, <=, >=, <, >
*/
private void compareExpr(boolean branchIf,
int token, int type1, BinExpr expr)
throws CompileError
{
if (arrayDim == 0)
convertOprandTypes(type1, exprType, expr);
int p = typePrecedence(exprType);
if (p == P_OTHER || arrayDim > 0)
if (token == EQ)
bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE);
else if (token == NEQ)
bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ);
else
badTypes(expr);
else
if (p == P_INT) {
int op[] = ifOp;
for (int i = 0; i < op.length; i += 3)
if (op[i] == token) {
bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
return;
}
badTypes(expr);
}
else {
if (p == P_DOUBLE)
if (token == '<' || token == LE)
bytecode.addOpcode(DCMPG);
else
bytecode.addOpcode(DCMPL);
else if (p == P_FLOAT)
if (token == '<' || token == LE)
bytecode.addOpcode(FCMPG);
else
bytecode.addOpcode(FCMPL);
else if (p == P_LONG)
bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: <
else
fatal();
int[] op = ifOp2;
for (int i = 0; i < op.length; i += 3)
if (op[i] == token) {
bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
return;
}
badTypes(expr);
}
}
protected static void badTypes(Expr expr) throws CompileError {
throw new CompileError("invalid types for " + expr.getName());
}
private static final int P_DOUBLE = 0;
private static final int P_FLOAT = 1;
private static final int P_LONG = 2;
private static final int P_INT = 3;
private static final int P_OTHER = -1;
protected static boolean isRefType(int type) {
return type == CLASS || type == NULL;
}
private static int typePrecedence(int type) {
if (type == DOUBLE)
return P_DOUBLE;
else if (type == FLOAT)
return P_FLOAT;
else if (type == LONG)
return P_LONG;
else if (isRefType(type))
return P_OTHER;
else if (type == VOID)
return P_OTHER; // this is wrong, but ...
else
return P_INT; // BOOLEAN, BYTE, CHAR, SHORT, INT
}
// used in TypeChecker.
static boolean isP_INT(int type) {
return typePrecedence(type) == P_INT;
}
// used in TypeChecker.
static boolean rightIsStrong(int type1, int type2) {
int type1_p = typePrecedence(type1);
int type2_p = typePrecedence(type2);
return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p;
}
private static final int[] castOp = {
/* D F L I */
/* double */ NOP, D2F, D2L, D2I,
/* float */ F2D, NOP, F2L, F2I,
/* long */ L2D, L2F, NOP, L2I,
/* other */ I2D, I2F, I2L, NOP };
/* do implicit type conversion.
* arrayDim values of the two oprands must be zero.
*/
private void convertOprandTypes(int type1, int type2, Expr expr)
throws CompileError
{
boolean rightStrong;
int type1_p = typePrecedence(type1);
int type2_p = typePrecedence(type2);
if (type2_p < 0 && type1_p < 0) // not primitive types
return;
if (type2_p < 0 || type1_p < 0) // either is not a primitive type
badTypes(expr);
int op, result_type;
if (type1_p <= type2_p) {
rightStrong = false;
exprType = type1;
op = castOp[type2_p * 4 + type1_p];
result_type = type1_p;
}
else {
rightStrong = true;
op = castOp[type1_p * 4 + type2_p];
result_type = type2_p;
}
if (rightStrong) {
if (result_type == P_DOUBLE || result_type == P_LONG) {
if (type1_p == P_DOUBLE || type1_p == P_LONG)
bytecode.addOpcode(DUP2_X2);
else
bytecode.addOpcode(DUP2_X1);
bytecode.addOpcode(POP2);
bytecode.addOpcode(op);
bytecode.addOpcode(DUP2_X2);
bytecode.addOpcode(POP2);
}
else if (result_type == P_FLOAT) {
if (type1_p == P_LONG) {
bytecode.addOpcode(DUP_X2);
bytecode.addOpcode(POP);
}
else
bytecode.addOpcode(SWAP);
bytecode.addOpcode(op);
bytecode.addOpcode(SWAP);
}
else
fatal();
}
else if (op != NOP)
bytecode.addOpcode(op);
}
public void atCastExpr(CastExpr expr) throws CompileError {
String cname = resolveClassName(expr.getClassName());
String toClass = checkCastExpr(expr, cname);
int srcType = exprType;
exprType = expr.getType();
arrayDim = expr.getArrayDim();
className = cname;
if (toClass == null)
atNumCastExpr(srcType, exprType); // built-in type
else
bytecode.addCheckcast(toClass);
}
public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
String cname = resolveClassName(expr.getClassName());
String toClass = checkCastExpr(expr, cname);
bytecode.addInstanceof(toClass);
exprType = BOOLEAN;
arrayDim = 0;
}
private String checkCastExpr(CastExpr expr, String name)
throws CompileError
{
final String msg = "invalid cast";
ASTree oprand = expr.getOprand();
int dim = expr.getArrayDim();
int type = expr.getType();
oprand.accept(this);
int srcType = exprType;
if (invalidDim(srcType, arrayDim, className, type, dim, name, true)
|| srcType == VOID || type == VOID)
throw new CompileError(msg);
if (type == CLASS) {
if (!isRefType(srcType))
throw new CompileError(msg);
return toJvmArrayName(name, dim);
}
else
if (dim > 0)
return toJvmTypeName(type, dim);
else
return null; // built-in type
}
void atNumCastExpr(int srcType, int destType)
throws CompileError
{
if (srcType == destType)
return;
int op, op2;
int stype = typePrecedence(srcType);
int dtype = typePrecedence(destType);
if (0 <= stype && stype < 3)
op = castOp[stype * 4 + dtype];
else
op = NOP;
if (destType == DOUBLE)
op2 = I2D;
else if (destType == FLOAT)
op2 = I2F;
else if (destType == LONG)
op2 = I2L;
else if (destType == SHORT)
op2 = I2S;
else if (destType == CHAR)
op2 = I2C;
else if (destType == BYTE)
op2 = I2B;
else
op2 = NOP;
if (op != NOP)
bytecode.addOpcode(op);
if (op == NOP || op == L2I || op == F2I || op == D2I)
if (op2 != NOP)
bytecode.addOpcode(op2);
}
public void atExpr(Expr expr) throws CompileError {
// array access, member access,
// (unary) +, (unary) -, ++, --, !, ~
int token = expr.getOperator();
ASTree oprand = expr.oprand1();
if (token == '.') {
String member = ((Symbol)expr.oprand2()).get();
if (member.equals("class"))
atClassObject(expr); // .class
else
atFieldRead(expr);
}
else if (token == MEMBER) { // field read
/* MEMBER ('#') is an extension by Javassist.
* The compiler internally uses # for compiling .class
* expressions such as "int.class".
*/
atFieldRead(expr);
}
else if (token == ARRAY)
atArrayRead(oprand, expr.oprand2());
else if (token == PLUSPLUS || token == MINUSMINUS)
atPlusPlus(token, oprand, expr, true);
else if (token == '!') {
booleanExpr(false, expr);
bytecode.addIndex(7);
bytecode.addIconst(1);
bytecode.addOpcode(Opcode.GOTO);
bytecode.addIndex(4);
bytecode.addIconst(0);
}
else if (token == CALL) // method call
fatal();
else {
expr.oprand1().accept(this);
int type = typePrecedence(exprType);
if (arrayDim > 0)
badType(expr);
if (token == '-') {
if (type == P_DOUBLE)
bytecode.addOpcode(DNEG);
else if (type == P_FLOAT)
bytecode.addOpcode(FNEG);
else if (type == P_LONG)
bytecode.addOpcode(LNEG);
else if (type == P_INT) {
bytecode.addOpcode(INEG);
exprType = INT; // type may be BYTE, ...
}
else
badType(expr);
}
else if (token == '~') {
if (type == P_INT) {
bytecode.addIconst(-1);
bytecode.addOpcode(IXOR);
exprType = INT; // type may be BYTE. ...
}
else if (type == P_LONG) {
bytecode.addLconst(-1);
bytecode.addOpcode(LXOR);
}
else
badType(expr);
}
else if (token == '+') {
if (type == P_OTHER)
badType(expr);
// do nothing. ignore.
}
else
fatal();
}
}
protected static void badType(Expr expr) throws CompileError {
throw new CompileError("invalid type for " + expr.getName());
}
public abstract void atCallExpr(CallExpr expr) throws CompileError;
protected abstract void atFieldRead(ASTree expr) throws CompileError;
public void atClassObject(Expr expr) throws CompileError {
ASTree op1 = expr.oprand1();
if (!(op1 instanceof Symbol))
throw new CompileError("fatal error: badly parsed .class expr");
String cname = ((Symbol)op1).get();
if (cname.startsWith("[")) {
int i = cname.indexOf("[L");
if (i >= 0) {
String name = cname.substring(i + 2, cname.length() - 1);
String name2 = resolveClassName(name);
if (!name.equals(name2)) {
/* For example, to obtain String[].class,
* "[Ljava.lang.String;" (not "[Ljava/lang/String"!)
* must be passed to Class.forName().
*/
name2 = MemberResolver.jvmToJavaName(name2);
StringBuffer sbuf = new StringBuffer();
while (i-- >= 0)
sbuf.append('[');
sbuf.append('L').append(name2).append(';');
cname = sbuf.toString();
}
}
}
else {
cname = resolveClassName(MemberResolver.javaToJvmName(cname));
cname = MemberResolver.jvmToJavaName(cname);
}
atClassObject2(cname);
exprType = CLASS;
arrayDim = 0;
className = "java/lang/Class";
}
/* MemberCodeGen overrides this method.
*/
protected void atClassObject2(String cname) throws CompileError {
int start = bytecode.currentPc();
bytecode.addLdc(cname);
bytecode.addInvokestatic("java.lang.Class", "forName",
"(Ljava/lang/String;)Ljava/lang/Class;");
int end = bytecode.currentPc();
bytecode.addOpcode(Opcode.GOTO);
int pc = bytecode.currentPc();
bytecode.addIndex(0); // correct later
bytecode.addExceptionHandler(start, end, bytecode.currentPc(),
"java.lang.ClassNotFoundException");
/* -- the following code is for inlining a call to DotClass.fail().
int var = getMaxLocals();
incMaxLocals(1);
bytecode.growStack(1);
bytecode.addAstore(var);
bytecode.addNew("java.lang.NoClassDefFoundError");
bytecode.addOpcode(DUP);
bytecode.addAload(var);
bytecode.addInvokevirtual("java.lang.ClassNotFoundException",
"getMessage", "()Ljava/lang/String;");
bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "",
"(Ljava/lang/String;)V");
*/
bytecode.growStack(1);
bytecode.addInvokestatic("javassist.runtime.DotClass", "fail",
"(Ljava/lang/ClassNotFoundException;)"
+ "Ljava/lang/NoClassDefFoundError;");
bytecode.addOpcode(ATHROW);
bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
}
public void atArrayRead(ASTree array, ASTree index)
throws CompileError
{
arrayAccess(array, index);
bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
}
protected void arrayAccess(ASTree array, ASTree index)
throws CompileError
{
array.accept(this);
int type = exprType;
int dim = arrayDim;
if (dim == 0)
throw new CompileError("bad array access");
String cname = className;
index.accept(this);
if (typePrecedence(exprType) != P_INT || arrayDim > 0)
throw new CompileError("bad array index");
exprType = type;
arrayDim = dim - 1;
className = cname;
}
protected static int getArrayReadOp(int type, int dim) {
if (dim > 0)
return AALOAD;
switch (type) {
case DOUBLE :
return DALOAD;
case FLOAT :
return FALOAD;
case LONG :
return LALOAD;
case INT :
return IALOAD;
case SHORT :
return SALOAD;
case CHAR :
return CALOAD;
case BYTE :
case BOOLEAN :
return BALOAD;
default :
return AALOAD;
}
}
protected static int getArrayWriteOp(int type, int dim) {
if (dim > 0)
return AASTORE;
switch (type) {
case DOUBLE :
return DASTORE;
case FLOAT :
return FASTORE;
case LONG :
return LASTORE;
case INT :
return IASTORE;
case SHORT :
return SASTORE;
case CHAR :
return CASTORE;
case BYTE :
case BOOLEAN :
return BASTORE;
default :
return AASTORE;
}
}
private void atPlusPlus(int token, ASTree oprand, Expr expr,
boolean doDup) throws CompileError
{
boolean isPost = oprand == null; // ++i or i++?
if (isPost)
oprand = expr.oprand2();
if (oprand instanceof Variable) {
Declarator d = ((Variable)oprand).getDeclarator();
int t = exprType = d.getType();
arrayDim = d.getArrayDim();
int var = getLocalVar(d);
if (arrayDim > 0)
badType(expr);
if (t == DOUBLE) {
bytecode.addDload(var);
if (doDup && isPost)
bytecode.addOpcode(DUP2);
bytecode.addDconst(1.0);
bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
if (doDup && !isPost)
bytecode.addOpcode(DUP2);
bytecode.addDstore(var);
}
else if (t == LONG) {
bytecode.addLload(var);
if (doDup && isPost)
bytecode.addOpcode(DUP2);
bytecode.addLconst((long)1);
bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
if (doDup && !isPost)
bytecode.addOpcode(DUP2);
bytecode.addLstore(var);
}
else if (t == FLOAT) {
bytecode.addFload(var);
if (doDup && isPost)
bytecode.addOpcode(DUP);
bytecode.addFconst(1.0f);
bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
if (doDup && !isPost)
bytecode.addOpcode(DUP);
bytecode.addFstore(var);
}
else if (t == BYTE || t == CHAR || t == SHORT || t == INT) {
if (doDup && isPost)
bytecode.addIload(var);
int delta = token == PLUSPLUS ? 1 : -1;
if (var > 0xff) {
bytecode.addOpcode(WIDE);
bytecode.addOpcode(IINC);
bytecode.addIndex(var);
bytecode.addIndex(delta);
}
else {
bytecode.addOpcode(IINC);
bytecode.add(var);
bytecode.add(delta);
}
if (doDup && !isPost)
bytecode.addIload(var);
}
else
badType(expr);
}
else {
if (oprand instanceof Expr) {
Expr e = (Expr)oprand;
if (e.getOperator() == ARRAY) {
atArrayPlusPlus(token, isPost, e, doDup);
return;
}
}
atFieldPlusPlus(token, isPost, oprand, expr, doDup);
}
}
public void atArrayPlusPlus(int token, boolean isPost,
Expr expr, boolean doDup) throws CompileError
{
arrayAccess(expr.oprand1(), expr.oprand2());
int t = exprType;
int dim = arrayDim;
if (dim > 0)
badType(expr);
bytecode.addOpcode(DUP2);
bytecode.addOpcode(getArrayReadOp(t, arrayDim));
int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2;
atPlusPlusCore(dup_code, doDup, token, isPost, expr);
bytecode.addOpcode(getArrayWriteOp(t, dim));
}
protected void atPlusPlusCore(int dup_code, boolean doDup,
int token, boolean isPost,
Expr expr) throws CompileError
{
int t = exprType;
if (doDup && isPost)
bytecode.addOpcode(dup_code);
if (t == INT || t == BYTE || t == CHAR || t == SHORT) {
bytecode.addIconst(1);
bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB);
exprType = INT;
}
else if (t == LONG) {
bytecode.addLconst((long)1);
bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
}
else if (t == FLOAT) {
bytecode.addFconst(1.0f);
bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
}
else if (t == DOUBLE) {
bytecode.addDconst(1.0);
bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
}
else
badType(expr);
if (doDup && !isPost)
bytecode.addOpcode(dup_code);
}
protected abstract void atFieldPlusPlus(int token, boolean isPost,
ASTree oprand, Expr expr, boolean doDup) throws CompileError;
public abstract void atMember(Member n) throws CompileError;
public void atVariable(Variable v) throws CompileError {
Declarator d = v.getDeclarator();
exprType = d.getType();
arrayDim = d.getArrayDim();
className = d.getClassName();
int var = getLocalVar(d);
if (arrayDim > 0)
bytecode.addAload(var);
else
switch (exprType) {
case CLASS :
bytecode.addAload(var);
break;
case LONG :
bytecode.addLload(var);
break;
case FLOAT :
bytecode.addFload(var);
break;
case DOUBLE :
bytecode.addDload(var);
break;
default : // BOOLEAN, BYTE, CHAR, SHORT, INT
bytecode.addIload(var);
break;
}
}
public void atKeyword(Keyword k) throws CompileError {
arrayDim = 0;
int token = k.get();
switch (token) {
case TRUE :
bytecode.addIconst(1);
exprType = BOOLEAN;
break;
case FALSE :
bytecode.addIconst(0);
exprType = BOOLEAN;
break;
case NULL :
bytecode.addOpcode(ACONST_NULL);
exprType = NULL;
break;
case THIS :
case SUPER :
if (inStaticMethod)
throw new CompileError("not-available: "
+ (token == THIS ? "this" : "super"));
bytecode.addAload(0);
exprType = CLASS;
if (token == THIS)
className = getThisName();
else
className = getSuperName();
break;
default :
fatal();
}
}
public void atStringL(StringL s) throws CompileError {
exprType = CLASS;
arrayDim = 0;
className = jvmJavaLangString;
bytecode.addLdc(s.get());
}
public void atIntConst(IntConst i) throws CompileError {
arrayDim = 0;
long value = i.get();
int type = i.getType();
if (type == IntConstant || type == CharConstant) {
exprType = (type == IntConstant ? INT : CHAR);
bytecode.addIconst((int)value);
}
else {
exprType = LONG;
bytecode.addLconst(value);
}
}
public void atDoubleConst(DoubleConst d) throws CompileError {
arrayDim = 0;
if (d.getType() == DoubleConstant) {
exprType = DOUBLE;
bytecode.addDconst(d.get());
}
else {
exprType = FLOAT;
bytecode.addFconst((float)d.get());
}
}
}