/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999-2007 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.
*
* 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", "<init>",
"(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);
bytecode.
addOpcode(
IINC);
bytecode.
add(
var);
bytecode.
add(
token ==
PLUSPLUS ? 1 : -1);
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());
}
}
}