Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
javassist.compiler.CodeGen Maven / Gradle / Ivy
/*
* 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 java.util.List;
import javassist.bytecode.Bytecode;
import javassist.bytecode.Opcode;
import javassist.compiler.ast.ASTList;
import javassist.compiler.ast.ASTree;
import javassist.compiler.ast.ArrayInit;
import javassist.compiler.ast.AssignExpr;
import javassist.compiler.ast.BinExpr;
import javassist.compiler.ast.CallExpr;
import javassist.compiler.ast.CastExpr;
import javassist.compiler.ast.CondExpr;
import javassist.compiler.ast.Declarator;
import javassist.compiler.ast.DoubleConst;
import javassist.compiler.ast.Expr;
import javassist.compiler.ast.FieldDecl;
import javassist.compiler.ast.InstanceOfExpr;
import javassist.compiler.ast.IntConst;
import javassist.compiler.ast.Keyword;
import javassist.compiler.ast.Member;
import javassist.compiler.ast.MethodDecl;
import javassist.compiler.ast.NewExpr;
import javassist.compiler.ast.Pair;
import javassist.compiler.ast.Stmnt;
import javassist.compiler.ast.StringL;
import javassist.compiler.ast.Symbol;
import javassist.compiler.ast.Variable;
import javassist.compiler.ast.Visitor;
/* The code generator is implemented 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 List 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;
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);
}
@Override
public void atASTList(ASTList n) throws CompileError { fatal(); }
@Override
public void atPair(Pair n) throws CompileError { fatal(); }
@Override
public void atSymbol(Symbol n) throws CompileError { fatal(); }
@Override
public void atFieldDecl(FieldDecl field) throws CompileError {
field.getInit().accept(this);
}
@Override
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;
@Override
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();
if (compileBooleanExpr(false, expr)) {
hasReturned = false;
if (elsep != null)
elsep.accept(this);
return;
}
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 {
List prevBreakList = breakList;
List 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);
if (alwaysBranch) {
bytecode.addOpcode(Opcode.GOTO);
alwaysBranch = breakList.size() == 0;
}
bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
patchGoto(breakList, bytecode.currentPc());
patchGoto(continueList, pc3);
continueList = prevContList;
breakList = prevBreakList;
hasReturned = alwaysBranch;
}
protected void patchGoto(List list, int targetPc) {
for (int pc:list)
bytecode.write16bit(pc, targetPc - pc + 1);
}
private void atForStmnt(Stmnt st) throws CompileError {
List prevBreakList = breakList;
List 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) {
if (compileBooleanExpr(false, expr)) {
// in case of "for (...; false; ...)"
continueList = prevContList;
breakList = prevBreakList;
hasReturned = false;
return;
}
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 {
boolean isString = false;
if (typeChecker != null) {
doTypeCheck(st.head());
isString = typeChecker.exprType == TypeChecker.CLASS
&& typeChecker.arrayDim == 0
&& TypeChecker.jvmJavaLangString.equals(typeChecker.className);
}
compileExpr(st.head());
int tmpVar = -1;
if (isString) {
tmpVar = getMaxLocals();
incMaxLocals(1);
bytecode.addAstore(tmpVar);
bytecode.addAload(tmpVar);
bytecode.addInvokevirtual(TypeChecker.jvmJavaLangString, "hashCode", "()I");
}
List 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];
ArrayList gotoDefaults = new ArrayList();
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 {
int curPos = bytecode.currentPc();
long caseLabel;
if (isString) {
// computeStringLabel() also adds bytecode as its side-effects.
caseLabel = (long)computeStringLabel(label.head(), tmpVar, gotoDefaults);
}
else
caseLabel = (long)computeLabel(label.head());
pairs[ipairs++]
= (caseLabel << 32) +
((long)(curPos - 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);
for (int addr: gotoDefaults)
bytecode.write16bit(addr, defaultPc - addr + 1);
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();
throw new CompileError("bad case label");
}
private int computeStringLabel(ASTree expr, int tmpVar, List gotoDefaults)
throws CompileError
{
doTypeCheck(expr);
expr = TypeChecker.stripPlusExpr(expr);
if (expr instanceof StringL) {
String label = ((StringL)expr).get();
bytecode.addAload(tmpVar);
bytecode.addLdc(label);
bytecode.addInvokevirtual(TypeChecker.jvmJavaLangString, "equals",
"(Ljava/lang/Object;)Z");
bytecode.addOpcode(IFEQ);
Integer pc = Integer.valueOf(bytecode.currentPc());
bytecode.addIndex(0);
gotoDefaults.add(pc);
return (int)label.hashCode();
}
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 = Integer.valueOf(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) {
@Override
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(List 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;
}
@Override
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);
}
}
@Override
public abstract void atNewExpr(NewExpr n) throws CompileError;
@Override
public abstract void atArrayInit(ArrayInit init) throws CompileError;
@Override
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;
}
@Override
public void atCondExpr(CondExpr expr) throws CompileError {
if (booleanExpr(false, expr.condExpr()))
expr.elseExpr().accept(this);
else {
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;
}
@Override
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: &&, ||, ==, !=, <=, >=, <, >
*/
if (!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 (branch offset) is not produced.
*
* @return true if the compiled code is GOTO (always branch).
* GOTO is not produced.
*/
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 == '!')
return booleanExpr(!branchIf, ((Expr)expr).oprand1());
else if ((isAndAnd = (op == ANDAND)) || op == OROR) {
BinExpr bexpr = (BinExpr)expr;
if (booleanExpr(!isAndAnd, bexpr.oprand1())) {
exprType = BOOLEAN;
arrayDim = 0;
return true;
}
int pc = bytecode.currentPc();
bytecode.addIndex(0); // correct later
if (booleanExpr(isAndAnd, bexpr.oprand2()))
bytecode.addOpcode(Opcode.GOTO);
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)) {
// Opcode.GOTO is not added here. The caller must add it.
exprType = BOOLEAN;
arrayDim = 0;
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;
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);
}
@Override
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);
}
@Override
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;
int srcDim = arrayDim;
if (invalidDim(srcType, arrayDim, className, type, dim, name, true)
|| srcType == VOID || type == VOID)
throw new CompileError(msg);
if (type == CLASS) {
if (!isRefType(srcType) && srcDim == 0)
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);
}
@Override
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 == '!') {
if (!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());
}
@Override
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(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(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;
@Override
public abstract void atMember(Member n) throws CompileError;
@Override
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;
}
}
@Override
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();
}
}
@Override
public void atStringL(StringL s) throws CompileError {
exprType = CLASS;
arrayDim = 0;
className = jvmJavaLangString;
bytecode.addLdc(s.get());
}
@Override
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);
}
}
@Override
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());
}
}
}