0001: /*
0002: * Javassist, a Java-bytecode translator toolkit.
0003: * Copyright (C) 1999-2006 Shigeru Chiba. All Rights Reserved.
0004: *
0005: * The contents of this file are subject to the Mozilla Public License Version
0006: * 1.1 (the "License"); you may not use this file except in compliance with
0007: * the License. Alternatively, the contents of this file may be used under
0008: * the terms of the GNU Lesser General Public License Version 2.1 or later.
0009: *
0010: * Software distributed under the License is distributed on an "AS IS" basis,
0011: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0012: * for the specific language governing rights and limitations under the
0013: * License.
0014: */
0015:
0016: package javassist.compiler;
0017:
0018: import java.util.ArrayList;
0019: import java.util.Arrays;
0020: import javassist.compiler.ast.*;
0021: import javassist.bytecode.*;
0022:
0023: /* The code generator is implemeted by three files:
0024: * CodeGen.java, MemberCodeGen.java, and JvstCodeGen.
0025: * I just wanted to split a big file into three smaller ones.
0026: */
0027:
0028: public abstract class CodeGen extends Visitor implements Opcode,
0029: TokenId {
0030: static final String javaLangObject = "java.lang.Object";
0031: static final String jvmJavaLangObject = "java/lang/Object";
0032:
0033: static final String javaLangString = "java.lang.String";
0034: static final String jvmJavaLangString = "java/lang/String";
0035:
0036: protected Bytecode bytecode;
0037: private int tempVar;
0038: TypeChecker typeChecker;
0039:
0040: /**
0041: * true if the last visited node is a return statement.
0042: */
0043: protected boolean hasReturned;
0044:
0045: /**
0046: * Must be true if compilation is for a static method.
0047: */
0048: public boolean inStaticMethod;
0049:
0050: protected ArrayList breakList, continueList;
0051:
0052: /**
0053: * doit() in ReturnHook is called from atReturn().
0054: */
0055: protected static abstract class ReturnHook {
0056: ReturnHook next;
0057:
0058: protected abstract void doit(Bytecode b, int opcode);
0059:
0060: protected ReturnHook(CodeGen gen) {
0061: next = gen.returnHooks;
0062: gen.returnHooks = this ;
0063: }
0064:
0065: protected void remove(CodeGen gen) {
0066: gen.returnHooks = next;
0067: }
0068: }
0069:
0070: protected ReturnHook returnHooks;
0071:
0072: /* The following fields are used by atXXX() methods
0073: * for returning the type of the compiled expression.
0074: */
0075: protected int exprType; // VOID, NULL, CLASS, BOOLEAN, INT, ...
0076: protected int arrayDim;
0077: protected String className; // JVM-internal representation
0078:
0079: public CodeGen(Bytecode b) {
0080: bytecode = b;
0081: tempVar = -1;
0082: typeChecker = null;
0083: hasReturned = false;
0084: inStaticMethod = false;
0085: breakList = null;
0086: continueList = null;
0087: returnHooks = null;
0088: }
0089:
0090: public void setTypeChecker(TypeChecker checker) {
0091: typeChecker = checker;
0092: }
0093:
0094: protected static void fatal() throws CompileError {
0095: throw new CompileError("fatal");
0096: }
0097:
0098: public static boolean is2word(int type, int dim) {
0099: return dim == 0 && (type == DOUBLE || type == LONG);
0100: }
0101:
0102: public int getMaxLocals() {
0103: return bytecode.getMaxLocals();
0104: }
0105:
0106: public void setMaxLocals(int n) {
0107: bytecode.setMaxLocals(n);
0108: }
0109:
0110: protected void incMaxLocals(int size) {
0111: bytecode.incMaxLocals(size);
0112: }
0113:
0114: /**
0115: * Returns a local variable that single or double words can be
0116: * stored in.
0117: */
0118: protected int getTempVar() {
0119: if (tempVar < 0) {
0120: tempVar = getMaxLocals();
0121: incMaxLocals(2);
0122: }
0123:
0124: return tempVar;
0125: }
0126:
0127: protected int getLocalVar(Declarator d) {
0128: int v = d.getLocalVar();
0129: if (v < 0) {
0130: v = getMaxLocals(); // delayed variable allocation.
0131: d.setLocalVar(v);
0132: incMaxLocals(1);
0133: }
0134:
0135: return v;
0136: }
0137:
0138: /**
0139: * Returns the JVM-internal representation of this class name.
0140: */
0141: protected abstract String getThisName();
0142:
0143: /**
0144: * Returns the JVM-internal representation of this super class name.
0145: */
0146: protected abstract String getSuperName() throws CompileError;
0147:
0148: /* Converts a class name into a JVM-internal representation.
0149: *
0150: * It may also expand a simple class name to java.lang.*.
0151: * For example, this converts Object into java/lang/Object.
0152: */
0153: protected abstract String resolveClassName(ASTList name)
0154: throws CompileError;
0155:
0156: /* Expands a simple class name to java.lang.*.
0157: * For example, this converts Object into java/lang/Object.
0158: */
0159: protected abstract String resolveClassName(String jvmClassName)
0160: throws CompileError;
0161:
0162: /**
0163: * @param name the JVM-internal representation.
0164: * name is not exapnded to java.lang.*.
0165: */
0166: protected static String toJvmArrayName(String name, int dim) {
0167: if (name == null)
0168: return null;
0169:
0170: if (dim == 0)
0171: return name;
0172: else {
0173: StringBuffer sbuf = new StringBuffer();
0174: int d = dim;
0175: while (d-- > 0)
0176: sbuf.append('[');
0177:
0178: sbuf.append('L');
0179: sbuf.append(name);
0180: sbuf.append(';');
0181:
0182: return sbuf.toString();
0183: }
0184: }
0185:
0186: protected static String toJvmTypeName(int type, int dim) {
0187: char c = 'I';
0188: switch (type) {
0189: case BOOLEAN:
0190: c = 'Z';
0191: break;
0192: case BYTE:
0193: c = 'B';
0194: break;
0195: case CHAR:
0196: c = 'C';
0197: break;
0198: case SHORT:
0199: c = 'S';
0200: break;
0201: case INT:
0202: c = 'I';
0203: break;
0204: case LONG:
0205: c = 'J';
0206: break;
0207: case FLOAT:
0208: c = 'F';
0209: break;
0210: case DOUBLE:
0211: c = 'D';
0212: break;
0213: case VOID:
0214: c = 'V';
0215: break;
0216: }
0217:
0218: StringBuffer sbuf = new StringBuffer();
0219: while (dim-- > 0)
0220: sbuf.append('[');
0221:
0222: sbuf.append(c);
0223: return sbuf.toString();
0224: }
0225:
0226: public void compileExpr(ASTree expr) throws CompileError {
0227: doTypeCheck(expr);
0228: expr.accept(this );
0229: }
0230:
0231: public boolean compileBooleanExpr(boolean branchIf, ASTree expr)
0232: throws CompileError {
0233: doTypeCheck(expr);
0234: return booleanExpr(branchIf, expr);
0235: }
0236:
0237: public void doTypeCheck(ASTree expr) throws CompileError {
0238: if (typeChecker != null)
0239: expr.accept(typeChecker);
0240: }
0241:
0242: public void atASTList(ASTList n) throws CompileError {
0243: fatal();
0244: }
0245:
0246: public void atPair(Pair n) throws CompileError {
0247: fatal();
0248: }
0249:
0250: public void atSymbol(Symbol n) throws CompileError {
0251: fatal();
0252: }
0253:
0254: public void atFieldDecl(FieldDecl field) throws CompileError {
0255: field.getInit().accept(this );
0256: }
0257:
0258: public void atMethodDecl(MethodDecl method) throws CompileError {
0259: ASTList mods = method.getModifiers();
0260: setMaxLocals(1);
0261: while (mods != null) {
0262: Keyword k = (Keyword) mods.head();
0263: mods = mods.tail();
0264: if (k.get() == STATIC) {
0265: setMaxLocals(0);
0266: inStaticMethod = true;
0267: }
0268: }
0269:
0270: ASTList params = method.getParams();
0271: while (params != null) {
0272: atDeclarator((Declarator) params.head());
0273: params = params.tail();
0274: }
0275:
0276: Stmnt s = method.getBody();
0277: atMethodBody(s, method.isConstructor(), method.getReturn()
0278: .getType() == VOID);
0279: }
0280:
0281: /**
0282: * @param isCons true if super() must be called.
0283: * false if the method is a class initializer.
0284: */
0285: public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid)
0286: throws CompileError {
0287: if (s == null)
0288: return;
0289:
0290: if (isCons && needsSuperCall(s))
0291: insertDefaultSuperCall();
0292:
0293: hasReturned = false;
0294: s.accept(this );
0295: if (!hasReturned)
0296: if (isVoid) {
0297: bytecode.addOpcode(Opcode.RETURN);
0298: hasReturned = true;
0299: } else
0300: throw new CompileError("no return statement");
0301: }
0302:
0303: private boolean needsSuperCall(Stmnt body) throws CompileError {
0304: if (body.getOperator() == BLOCK)
0305: body = (Stmnt) body.head();
0306:
0307: if (body != null && body.getOperator() == EXPR) {
0308: ASTree expr = body.head();
0309: if (expr != null && expr instanceof Expr
0310: && ((Expr) expr).getOperator() == CALL) {
0311: ASTree target = ((Expr) expr).head();
0312: if (target instanceof Keyword) {
0313: int token = ((Keyword) target).get();
0314: return token != THIS && token != SUPER;
0315: }
0316: }
0317: }
0318:
0319: return true;
0320: }
0321:
0322: protected abstract void insertDefaultSuperCall()
0323: throws CompileError;
0324:
0325: public void atStmnt(Stmnt st) throws CompileError {
0326: if (st == null)
0327: return; // empty
0328:
0329: int op = st.getOperator();
0330: if (op == EXPR) {
0331: ASTree expr = st.getLeft();
0332: doTypeCheck(expr);
0333: if (expr instanceof AssignExpr)
0334: atAssignExpr((AssignExpr) expr, false);
0335: else if (isPlusPlusExpr(expr)) {
0336: Expr e = (Expr) expr;
0337: atPlusPlus(e.getOperator(), e.oprand1(), e, false);
0338: } else {
0339: expr.accept(this );
0340: if (is2word(exprType, arrayDim))
0341: bytecode.addOpcode(POP2);
0342: else if (exprType != VOID)
0343: bytecode.addOpcode(POP);
0344: }
0345: } else if (op == DECL || op == BLOCK) {
0346: ASTList list = st;
0347: while (list != null) {
0348: ASTree h = list.head();
0349: list = list.tail();
0350: if (h != null)
0351: h.accept(this );
0352: }
0353: } else if (op == IF)
0354: atIfStmnt(st);
0355: else if (op == WHILE || op == DO)
0356: atWhileStmnt(st, op == WHILE);
0357: else if (op == FOR)
0358: atForStmnt(st);
0359: else if (op == BREAK || op == CONTINUE)
0360: atBreakStmnt(st, op == BREAK);
0361: else if (op == TokenId.RETURN)
0362: atReturnStmnt(st);
0363: else if (op == THROW)
0364: atThrowStmnt(st);
0365: else if (op == TRY)
0366: atTryStmnt(st);
0367: else if (op == SWITCH)
0368: atSwitchStmnt(st);
0369: else if (op == SYNCHRONIZED)
0370: atSyncStmnt(st);
0371: else {
0372: // LABEL, SWITCH label stament might be null?.
0373: hasReturned = false;
0374: throw new CompileError(
0375: "sorry, not supported statement: TokenId " + op);
0376: }
0377: }
0378:
0379: private void atIfStmnt(Stmnt st) throws CompileError {
0380: ASTree expr = st.head();
0381: Stmnt thenp = (Stmnt) st.tail().head();
0382: Stmnt elsep = (Stmnt) st.tail().tail().head();
0383: compileBooleanExpr(false, expr);
0384: int pc = bytecode.currentPc();
0385: int pc2 = 0;
0386: bytecode.addIndex(0); // correct later
0387:
0388: hasReturned = false;
0389: if (thenp != null)
0390: thenp.accept(this );
0391:
0392: boolean thenHasReturned = hasReturned;
0393: hasReturned = false;
0394:
0395: if (elsep != null && !thenHasReturned) {
0396: bytecode.addOpcode(Opcode.GOTO);
0397: pc2 = bytecode.currentPc();
0398: bytecode.addIndex(0);
0399: }
0400:
0401: bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
0402:
0403: if (elsep != null) {
0404: elsep.accept(this );
0405: if (!thenHasReturned)
0406: bytecode
0407: .write16bit(pc2, bytecode.currentPc() - pc2 + 1);
0408:
0409: hasReturned = thenHasReturned && hasReturned;
0410: }
0411: }
0412:
0413: private void atWhileStmnt(Stmnt st, boolean notDo)
0414: throws CompileError {
0415: ArrayList prevBreakList = breakList;
0416: ArrayList prevContList = continueList;
0417: breakList = new ArrayList();
0418: continueList = new ArrayList();
0419:
0420: ASTree expr = st.head();
0421: Stmnt body = (Stmnt) st.tail();
0422:
0423: int pc = 0;
0424: if (notDo) {
0425: bytecode.addOpcode(Opcode.GOTO);
0426: pc = bytecode.currentPc();
0427: bytecode.addIndex(0);
0428: }
0429:
0430: int pc2 = bytecode.currentPc();
0431: if (body != null)
0432: body.accept(this );
0433:
0434: int pc3 = bytecode.currentPc();
0435: if (notDo)
0436: bytecode.write16bit(pc, pc3 - pc + 1);
0437:
0438: boolean alwaysBranch = compileBooleanExpr(true, expr);
0439: bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
0440:
0441: patchGoto(breakList, bytecode.currentPc());
0442: patchGoto(continueList, pc3);
0443: continueList = prevContList;
0444: breakList = prevBreakList;
0445: hasReturned = alwaysBranch;
0446: }
0447:
0448: protected void patchGoto(ArrayList list, int targetPc) {
0449: int n = list.size();
0450: for (int i = 0; i < n; ++i) {
0451: int pc = ((Integer) list.get(i)).intValue();
0452: bytecode.write16bit(pc, targetPc - pc + 1);
0453: }
0454: }
0455:
0456: private void atForStmnt(Stmnt st) throws CompileError {
0457: ArrayList prevBreakList = breakList;
0458: ArrayList prevContList = continueList;
0459: breakList = new ArrayList();
0460: continueList = new ArrayList();
0461:
0462: Stmnt init = (Stmnt) st.head();
0463: ASTList p = st.tail();
0464: ASTree expr = p.head();
0465: p = p.tail();
0466: Stmnt update = (Stmnt) p.head();
0467: Stmnt body = (Stmnt) p.tail();
0468:
0469: if (init != null)
0470: init.accept(this );
0471:
0472: int pc = bytecode.currentPc();
0473: int pc2 = 0;
0474: if (expr != null) {
0475: compileBooleanExpr(false, expr);
0476: pc2 = bytecode.currentPc();
0477: bytecode.addIndex(0);
0478: }
0479:
0480: if (body != null)
0481: body.accept(this );
0482:
0483: int pc3 = bytecode.currentPc();
0484: if (update != null)
0485: update.accept(this );
0486:
0487: bytecode.addOpcode(Opcode.GOTO);
0488: bytecode.addIndex(pc - bytecode.currentPc() + 1);
0489:
0490: int pc4 = bytecode.currentPc();
0491: if (expr != null)
0492: bytecode.write16bit(pc2, pc4 - pc2 + 1);
0493:
0494: patchGoto(breakList, pc4);
0495: patchGoto(continueList, pc3);
0496: continueList = prevContList;
0497: breakList = prevBreakList;
0498: hasReturned = false;
0499: }
0500:
0501: private void atSwitchStmnt(Stmnt st) throws CompileError {
0502: compileExpr(st.head());
0503:
0504: ArrayList prevBreakList = breakList;
0505: breakList = new ArrayList();
0506: int opcodePc = bytecode.currentPc();
0507: bytecode.addOpcode(LOOKUPSWITCH);
0508: int npads = 3 - (opcodePc & 3);
0509: while (npads-- > 0)
0510: bytecode.add(0);
0511:
0512: Stmnt body = (Stmnt) st.tail();
0513: int npairs = 0;
0514: for (ASTList list = body; list != null; list = list.tail())
0515: if (((Stmnt) list.head()).getOperator() == CASE)
0516: ++npairs;
0517:
0518: // opcodePc2 is the position at which the default jump offset is.
0519: int opcodePc2 = bytecode.currentPc();
0520: bytecode.addGap(4);
0521: bytecode.add32bit(npairs);
0522: bytecode.addGap(npairs * 8);
0523:
0524: long[] pairs = new long[npairs];
0525: int ipairs = 0;
0526: int defaultPc = -1;
0527: for (ASTList list = body; list != null; list = list.tail()) {
0528: Stmnt label = (Stmnt) list.head();
0529: int op = label.getOperator();
0530: if (op == DEFAULT)
0531: defaultPc = bytecode.currentPc();
0532: else if (op != CASE)
0533: fatal();
0534: else {
0535: pairs[ipairs++] = ((long) computeLabel(label.head()) << 32)
0536: + ((long) (bytecode.currentPc() - opcodePc) & 0xffffffff);
0537: }
0538:
0539: hasReturned = false;
0540: ((Stmnt) label.tail()).accept(this );
0541: }
0542:
0543: Arrays.sort(pairs);
0544: int pc = opcodePc2 + 8;
0545: for (int i = 0; i < npairs; ++i) {
0546: bytecode.write32bit(pc, (int) (pairs[i] >>> 32));
0547: bytecode.write32bit(pc + 4, (int) pairs[i]);
0548: pc += 8;
0549: }
0550:
0551: if (defaultPc < 0 || breakList.size() > 0)
0552: hasReturned = false;
0553:
0554: int endPc = bytecode.currentPc();
0555: if (defaultPc < 0)
0556: defaultPc = endPc;
0557:
0558: bytecode.write32bit(opcodePc2, defaultPc - opcodePc);
0559:
0560: patchGoto(breakList, endPc);
0561: breakList = prevBreakList;
0562: }
0563:
0564: private int computeLabel(ASTree expr) throws CompileError {
0565: doTypeCheck(expr);
0566: expr = TypeChecker.stripPlusExpr(expr);
0567: if (expr instanceof IntConst)
0568: return (int) ((IntConst) expr).get();
0569: else
0570: throw new CompileError("bad case label");
0571: }
0572:
0573: private void atBreakStmnt(Stmnt st, boolean notCont)
0574: throws CompileError {
0575: if (st.head() != null)
0576: throw new CompileError(
0577: "sorry, not support labeled break or continue");
0578:
0579: bytecode.addOpcode(Opcode.GOTO);
0580: Integer pc = new Integer(bytecode.currentPc());
0581: bytecode.addIndex(0);
0582: if (notCont)
0583: breakList.add(pc);
0584: else
0585: continueList.add(pc);
0586: }
0587:
0588: protected void atReturnStmnt(Stmnt st) throws CompileError {
0589: atReturnStmnt2(st.getLeft());
0590: }
0591:
0592: protected final void atReturnStmnt2(ASTree result)
0593: throws CompileError {
0594: int op;
0595: if (result == null)
0596: op = Opcode.RETURN;
0597: else {
0598: compileExpr(result);
0599: if (arrayDim > 0)
0600: op = ARETURN;
0601: else {
0602: int type = exprType;
0603: if (type == DOUBLE)
0604: op = DRETURN;
0605: else if (type == FLOAT)
0606: op = FRETURN;
0607: else if (type == LONG)
0608: op = LRETURN;
0609: else if (isRefType(type))
0610: op = ARETURN;
0611: else
0612: op = IRETURN;
0613: }
0614: }
0615:
0616: for (ReturnHook har = returnHooks; har != null; har = har.next)
0617: har.doit(bytecode, op);
0618:
0619: bytecode.addOpcode(op);
0620: hasReturned = true;
0621: }
0622:
0623: private void atThrowStmnt(Stmnt st) throws CompileError {
0624: ASTree e = st.getLeft();
0625: compileExpr(e);
0626: if (exprType != CLASS || arrayDim > 0)
0627: throw new CompileError("bad throw statement");
0628:
0629: bytecode.addOpcode(ATHROW);
0630: hasReturned = true;
0631: }
0632:
0633: /* overridden in MemberCodeGen
0634: */
0635: protected void atTryStmnt(Stmnt st) throws CompileError {
0636: hasReturned = false;
0637: }
0638:
0639: private void atSyncStmnt(Stmnt st) throws CompileError {
0640: int nbreaks = getListSize(breakList);
0641: int ncontinues = getListSize(continueList);
0642:
0643: compileExpr(st.head());
0644: if (exprType != CLASS && arrayDim == 0)
0645: throw new CompileError(
0646: "bad type expr for synchronized block");
0647:
0648: Bytecode bc = bytecode;
0649: final int var = bc.getMaxLocals();
0650: bc.incMaxLocals(1);
0651: bc.addOpcode(DUP);
0652: bc.addAstore(var);
0653: bc.addOpcode(MONITORENTER);
0654:
0655: ReturnHook rh = new ReturnHook(this ) {
0656: protected void doit(Bytecode b, int opcode) {
0657: b.addAload(var);
0658: b.addOpcode(MONITOREXIT);
0659: }
0660: };
0661:
0662: int pc = bc.currentPc();
0663: Stmnt body = (Stmnt) st.tail();
0664: if (body != null)
0665: body.accept(this );
0666:
0667: int pc2 = bc.currentPc();
0668: int pc3 = 0;
0669: if (!hasReturned) {
0670: rh.doit(bc, 0); // the 2nd arg is ignored.
0671: bc.addOpcode(Opcode.GOTO);
0672: pc3 = bc.currentPc();
0673: bc.addIndex(0);
0674: }
0675:
0676: int pc4 = bc.currentPc();
0677: rh.doit(bc, 0); // the 2nd arg is ignored.
0678: bc.addOpcode(ATHROW);
0679: bc.addExceptionHandler(pc, pc2, pc4, 0);
0680: if (!hasReturned)
0681: bc.write16bit(pc3, bc.currentPc() - pc3 + 1);
0682:
0683: rh.remove(this );
0684:
0685: if (getListSize(breakList) != nbreaks
0686: || getListSize(continueList) != ncontinues)
0687: throw new CompileError(
0688: "sorry, cannot break/continue in synchronized block");
0689: }
0690:
0691: private static int getListSize(ArrayList list) {
0692: return list == null ? 0 : list.size();
0693: }
0694:
0695: private static boolean isPlusPlusExpr(ASTree expr) {
0696: if (expr instanceof Expr) {
0697: int op = ((Expr) expr).getOperator();
0698: return op == PLUSPLUS || op == MINUSMINUS;
0699: }
0700:
0701: return false;
0702: }
0703:
0704: public void atDeclarator(Declarator d) throws CompileError {
0705: d.setLocalVar(getMaxLocals());
0706: d.setClassName(resolveClassName(d.getClassName()));
0707:
0708: int size;
0709: if (is2word(d.getType(), d.getArrayDim()))
0710: size = 2;
0711: else
0712: size = 1;
0713:
0714: incMaxLocals(size);
0715:
0716: /* NOTE: Array initializers has not been supported.
0717: */
0718: ASTree init = d.getInitializer();
0719: if (init != null) {
0720: doTypeCheck(init);
0721: atVariableAssign(null, '=', null, d, init, false);
0722: }
0723: }
0724:
0725: public abstract void atNewExpr(NewExpr n) throws CompileError;
0726:
0727: public abstract void atArrayInit(ArrayInit init)
0728: throws CompileError;
0729:
0730: public void atAssignExpr(AssignExpr expr) throws CompileError {
0731: atAssignExpr(expr, true);
0732: }
0733:
0734: protected void atAssignExpr(AssignExpr expr, boolean doDup)
0735: throws CompileError {
0736: // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
0737: int op = expr.getOperator();
0738: ASTree left = expr.oprand1();
0739: ASTree right = expr.oprand2();
0740: if (left instanceof Variable)
0741: atVariableAssign(expr, op, (Variable) left,
0742: ((Variable) left).getDeclarator(), right, doDup);
0743: else {
0744: if (left instanceof Expr) {
0745: Expr e = (Expr) left;
0746: if (e.getOperator() == ARRAY) {
0747: atArrayAssign(expr, op, (Expr) left, right, doDup);
0748: return;
0749: }
0750: }
0751:
0752: atFieldAssign(expr, op, left, right, doDup);
0753: }
0754: }
0755:
0756: protected static void badAssign(Expr expr) throws CompileError {
0757: String msg;
0758: if (expr == null)
0759: msg = "incompatible type for assignment";
0760: else
0761: msg = "incompatible type for " + expr.getName();
0762:
0763: throw new CompileError(msg);
0764: }
0765:
0766: /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
0767: *
0768: * expr and var can be null.
0769: */
0770: private void atVariableAssign(Expr expr, int op, Variable var,
0771: Declarator d, ASTree right, boolean doDup)
0772: throws CompileError {
0773: int varType = d.getType();
0774: int varArray = d.getArrayDim();
0775: String varClass = d.getClassName();
0776: int varNo = getLocalVar(d);
0777:
0778: if (op != '=')
0779: atVariable(var);
0780:
0781: // expr is null if the caller is atDeclarator().
0782: if (expr == null && right instanceof ArrayInit)
0783: atArrayVariableAssign((ArrayInit) right, varType, varArray,
0784: varClass);
0785: else
0786: atAssignCore(expr, op, right, varType, varArray, varClass);
0787:
0788: if (doDup)
0789: if (is2word(varType, varArray))
0790: bytecode.addOpcode(DUP2);
0791: else
0792: bytecode.addOpcode(DUP);
0793:
0794: if (varArray > 0)
0795: bytecode.addAstore(varNo);
0796: else if (varType == DOUBLE)
0797: bytecode.addDstore(varNo);
0798: else if (varType == FLOAT)
0799: bytecode.addFstore(varNo);
0800: else if (varType == LONG)
0801: bytecode.addLstore(varNo);
0802: else if (isRefType(varType))
0803: bytecode.addAstore(varNo);
0804: else
0805: bytecode.addIstore(varNo);
0806:
0807: exprType = varType;
0808: arrayDim = varArray;
0809: className = varClass;
0810: }
0811:
0812: protected abstract void atArrayVariableAssign(ArrayInit init,
0813: int varType, int varArray, String varClass)
0814: throws CompileError;
0815:
0816: private void atArrayAssign(Expr expr, int op, Expr array,
0817: ASTree right, boolean doDup) throws CompileError {
0818: arrayAccess(array.oprand1(), array.oprand2());
0819:
0820: if (op != '=') {
0821: bytecode.addOpcode(DUP2);
0822: bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
0823: }
0824:
0825: int aType = exprType;
0826: int aDim = arrayDim;
0827: String cname = className;
0828:
0829: atAssignCore(expr, op, right, aType, aDim, cname);
0830:
0831: if (doDup)
0832: if (is2word(aType, aDim))
0833: bytecode.addOpcode(DUP2_X2);
0834: else
0835: bytecode.addOpcode(DUP_X2);
0836:
0837: bytecode.addOpcode(getArrayWriteOp(aType, aDim));
0838: exprType = aType;
0839: arrayDim = aDim;
0840: className = cname;
0841: }
0842:
0843: protected abstract void atFieldAssign(Expr expr, int op,
0844: ASTree left, ASTree right, boolean doDup)
0845: throws CompileError;
0846:
0847: protected void atAssignCore(Expr expr, int op, ASTree right,
0848: int type, int dim, String cname) throws CompileError {
0849: if (op == PLUS_E && dim == 0 && type == CLASS)
0850: atStringPlusEq(expr, type, dim, cname, right);
0851: else {
0852: right.accept(this );
0853: if (invalidDim(exprType, arrayDim, className, type, dim,
0854: cname, false)
0855: || (op != '=' && dim > 0))
0856: badAssign(expr);
0857:
0858: if (op != '=') {
0859: int token = assignOps[op - MOD_E];
0860: int k = lookupBinOp(token);
0861: if (k < 0)
0862: fatal();
0863:
0864: atArithBinExpr(expr, token, k, type);
0865: }
0866: }
0867:
0868: if (op != '=' || (dim == 0 && !isRefType(type)))
0869: atNumCastExpr(exprType, type);
0870:
0871: // type check should be done here.
0872: }
0873:
0874: private void atStringPlusEq(Expr expr, int type, int dim,
0875: String cname, ASTree right) throws CompileError {
0876: if (!jvmJavaLangString.equals(cname))
0877: badAssign(expr);
0878:
0879: convToString(type, dim); // the value might be null.
0880: right.accept(this );
0881: convToString(exprType, arrayDim);
0882: bytecode.addInvokevirtual(javaLangString, "concat",
0883: "(Ljava/lang/String;)Ljava/lang/String;");
0884: exprType = CLASS;
0885: arrayDim = 0;
0886: className = jvmJavaLangString;
0887: }
0888:
0889: private boolean invalidDim(int srcType, int srcDim,
0890: String srcClass, int destType, int destDim,
0891: String destClass, boolean isCast) {
0892: if (srcDim != destDim)
0893: if (srcType == NULL)
0894: return false;
0895: else if (destDim == 0 && destType == CLASS
0896: && jvmJavaLangObject.equals(destClass))
0897: return false;
0898: else if (isCast && srcDim == 0 && srcType == CLASS
0899: && jvmJavaLangObject.equals(srcClass))
0900: return false;
0901: else
0902: return true;
0903:
0904: return false;
0905: }
0906:
0907: public void atCondExpr(CondExpr expr) throws CompileError {
0908: booleanExpr(false, expr.condExpr());
0909: int pc = bytecode.currentPc();
0910: bytecode.addIndex(0); // correct later
0911: expr.thenExpr().accept(this );
0912: int dim1 = arrayDim;
0913: bytecode.addOpcode(Opcode.GOTO);
0914: int pc2 = bytecode.currentPc();
0915: bytecode.addIndex(0);
0916: bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
0917: expr.elseExpr().accept(this );
0918: if (dim1 != arrayDim)
0919: throw new CompileError("type mismatch in ?:");
0920:
0921: bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
0922: }
0923:
0924: static final int[] binOp = { '+', DADD, FADD, LADD, IADD, '-',
0925: DSUB, FSUB, LSUB, ISUB, '*', DMUL, FMUL, LMUL, IMUL, '/',
0926: DDIV, FDIV, LDIV, IDIV, '%', DREM, FREM, LREM, IREM, '|',
0927: NOP, NOP, LOR, IOR, '^', NOP, NOP, LXOR, IXOR, '&', NOP,
0928: NOP, LAND, IAND, LSHIFT, NOP, NOP, LSHL, ISHL, RSHIFT, NOP,
0929: NOP, LSHR, ISHR, ARSHIFT, NOP, NOP, LUSHR, IUSHR };
0930:
0931: static int lookupBinOp(int token) {
0932: int[] code = binOp;
0933: int s = code.length;
0934: for (int k = 0; k < s; k = k + 5)
0935: if (code[k] == token)
0936: return k;
0937:
0938: return -1;
0939: }
0940:
0941: public void atBinExpr(BinExpr expr) throws CompileError {
0942: int token = expr.getOperator();
0943:
0944: /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
0945: */
0946: int k = lookupBinOp(token);
0947: if (k >= 0) {
0948: expr.oprand1().accept(this );
0949: ASTree right = expr.oprand2();
0950: if (right == null)
0951: return; // see TypeChecker.atBinExpr().
0952:
0953: int type1 = exprType;
0954: int dim1 = arrayDim;
0955: String cname1 = className;
0956: right.accept(this );
0957: if (dim1 != arrayDim)
0958: throw new CompileError("incompatible array types");
0959:
0960: if (token == '+' && dim1 == 0
0961: && (type1 == CLASS || exprType == CLASS))
0962: atStringConcatExpr(expr, type1, dim1, cname1);
0963: else
0964: atArithBinExpr(expr, token, k, type1);
0965: } else {
0966: /* equation: &&, ||, ==, !=, <=, >=, <, >
0967: */
0968: booleanExpr(true, expr);
0969: bytecode.addIndex(7);
0970: bytecode.addIconst(0); // false
0971: bytecode.addOpcode(Opcode.GOTO);
0972: bytecode.addIndex(4);
0973: bytecode.addIconst(1); // true
0974: }
0975: }
0976:
0977: /* arrayDim values of the two oprands must be equal.
0978: * If an oprand type is not a numeric type, this method
0979: * throws an exception.
0980: */
0981: private void atArithBinExpr(Expr expr, int token, int index,
0982: int type1) throws CompileError {
0983: if (arrayDim != 0)
0984: badTypes(expr);
0985:
0986: int type2 = exprType;
0987: if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
0988: if (type2 == INT || type2 == SHORT || type2 == CHAR
0989: || type2 == BYTE)
0990: exprType = type1;
0991: else
0992: badTypes(expr);
0993: else
0994: convertOprandTypes(type1, type2, expr);
0995:
0996: int p = typePrecedence(exprType);
0997: if (p >= 0) {
0998: int op = binOp[index + p + 1];
0999: if (op != NOP) {
1000: if (p == P_INT && exprType != BOOLEAN)
1001: exprType = INT; // type1 may be BYTE, ...
1002:
1003: bytecode.addOpcode(op);
1004: return;
1005: }
1006: }
1007:
1008: badTypes(expr);
1009: }
1010:
1011: private void atStringConcatExpr(Expr expr, int type1, int dim1,
1012: String cname1) throws CompileError {
1013: int type2 = exprType;
1014: int dim2 = arrayDim;
1015: boolean type2Is2 = is2word(type2, dim2);
1016: boolean type2IsString = (type2 == CLASS && jvmJavaLangString
1017: .equals(className));
1018:
1019: if (type2Is2)
1020: convToString(type2, dim2);
1021:
1022: if (is2word(type1, dim1)) {
1023: bytecode.addOpcode(DUP_X2);
1024: bytecode.addOpcode(POP);
1025: } else
1026: bytecode.addOpcode(SWAP);
1027:
1028: // even if type1 is String, the left operand might be null.
1029: convToString(type1, dim1);
1030: bytecode.addOpcode(SWAP);
1031:
1032: if (!type2Is2 && !type2IsString)
1033: convToString(type2, dim2);
1034:
1035: bytecode.addInvokevirtual(javaLangString, "concat",
1036: "(Ljava/lang/String;)Ljava/lang/String;");
1037: exprType = CLASS;
1038: arrayDim = 0;
1039: className = jvmJavaLangString;
1040: }
1041:
1042: private void convToString(int type, int dim) throws CompileError {
1043: final String method = "valueOf";
1044:
1045: if (isRefType(type) || dim > 0)
1046: bytecode.addInvokestatic(javaLangString, method,
1047: "(Ljava/lang/Object;)Ljava/lang/String;");
1048: else if (type == DOUBLE)
1049: bytecode.addInvokestatic(javaLangString, method,
1050: "(D)Ljava/lang/String;");
1051: else if (type == FLOAT)
1052: bytecode.addInvokestatic(javaLangString, method,
1053: "(F)Ljava/lang/String;");
1054: else if (type == LONG)
1055: bytecode.addInvokestatic(javaLangString, method,
1056: "(J)Ljava/lang/String;");
1057: else if (type == BOOLEAN)
1058: bytecode.addInvokestatic(javaLangString, method,
1059: "(Z)Ljava/lang/String;");
1060: else if (type == CHAR)
1061: bytecode.addInvokestatic(javaLangString, method,
1062: "(C)Ljava/lang/String;");
1063: else if (type == VOID)
1064: throw new CompileError("void type expression");
1065: else
1066: /* INT, BYTE, SHORT */
1067: bytecode.addInvokestatic(javaLangString, method,
1068: "(I)Ljava/lang/String;");
1069: }
1070:
1071: /* Produces the opcode to branch if the condition is true.
1072: * The oprand is not produced.
1073: *
1074: * @return true if the compiled code is GOTO (always branch).
1075: */
1076: private boolean booleanExpr(boolean branchIf, ASTree expr)
1077: throws CompileError {
1078: boolean isAndAnd;
1079: int op = getCompOperator(expr);
1080: if (op == EQ) { // ==, !=, ...
1081: BinExpr bexpr = (BinExpr) expr;
1082: int type1 = compileOprands(bexpr);
1083: // here, arrayDim might represent the array dim. of the left oprand
1084: // if the right oprand is NULL.
1085: compareExpr(branchIf, bexpr.getOperator(), type1, bexpr);
1086: } else if (op == '!')
1087: booleanExpr(!branchIf, ((Expr) expr).oprand1());
1088: else if ((isAndAnd = (op == ANDAND)) || op == OROR) {
1089: BinExpr bexpr = (BinExpr) expr;
1090: booleanExpr(!isAndAnd, bexpr.oprand1());
1091: int pc = bytecode.currentPc();
1092: bytecode.addIndex(0); // correct later
1093:
1094: booleanExpr(isAndAnd, bexpr.oprand2());
1095: bytecode.write16bit(pc, bytecode.currentPc() - pc + 3);
1096: if (branchIf != isAndAnd) {
1097: bytecode.addIndex(6); // skip GOTO instruction
1098: bytecode.addOpcode(Opcode.GOTO);
1099: }
1100: } else if (isAlwaysBranch(expr, branchIf)) {
1101: bytecode.addOpcode(Opcode.GOTO);
1102: return true; // always branch
1103: } else { // others
1104: expr.accept(this );
1105: if (exprType != BOOLEAN || arrayDim != 0)
1106: throw new CompileError("boolean expr is required");
1107:
1108: bytecode.addOpcode(branchIf ? IFNE : IFEQ);
1109: }
1110:
1111: exprType = BOOLEAN;
1112: arrayDim = 0;
1113: return false;
1114: }
1115:
1116: private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) {
1117: if (expr instanceof Keyword) {
1118: int t = ((Keyword) expr).get();
1119: return branchIf ? t == TRUE : t == FALSE;
1120: }
1121:
1122: return false;
1123: }
1124:
1125: static int getCompOperator(ASTree expr) throws CompileError {
1126: if (expr instanceof Expr) {
1127: Expr bexpr = (Expr) expr;
1128: int token = bexpr.getOperator();
1129: if (token == '!')
1130: return '!';
1131: else if ((bexpr instanceof BinExpr) && token != OROR
1132: && token != ANDAND && token != '&' && token != '|')
1133: return EQ; // ==, !=, ...
1134: else
1135: return token;
1136: }
1137:
1138: return ' '; // others
1139: }
1140:
1141: private int compileOprands(BinExpr expr) throws CompileError {
1142: expr.oprand1().accept(this );
1143: int type1 = exprType;
1144: int dim1 = arrayDim;
1145: expr.oprand2().accept(this );
1146: if (dim1 != arrayDim)
1147: if (type1 != NULL && exprType != NULL)
1148: throw new CompileError("incompatible array types");
1149: else if (exprType == NULL)
1150: arrayDim = dim1;
1151:
1152: if (type1 == NULL)
1153: return exprType;
1154: else
1155: return type1;
1156: }
1157:
1158: private static final int ifOp[] = { EQ, IF_ICMPEQ, IF_ICMPNE, NEQ,
1159: IF_ICMPNE, IF_ICMPEQ, LE, IF_ICMPLE, IF_ICMPGT, GE,
1160: IF_ICMPGE, IF_ICMPLT, '<', IF_ICMPLT, IF_ICMPGE, '>',
1161: IF_ICMPGT, IF_ICMPLE };
1162:
1163: private static final int ifOp2[] = { EQ, IFEQ, IFNE, NEQ, IFNE,
1164: IFEQ, LE, IFLE, IFGT, GE, IFGE, IFLT, '<', IFLT, IFGE, '>',
1165: IFGT, IFLE };
1166:
1167: /* Produces the opcode to branch if the condition is true.
1168: * The oprands are not produced.
1169: *
1170: * Parameter expr - compare expression ==, !=, <=, >=, <, >
1171: */
1172: private void compareExpr(boolean branchIf, int token, int type1,
1173: BinExpr expr) throws CompileError {
1174: if (arrayDim == 0)
1175: convertOprandTypes(type1, exprType, expr);
1176:
1177: int p = typePrecedence(exprType);
1178: if (p == P_OTHER || arrayDim > 0)
1179: if (token == EQ)
1180: bytecode.addOpcode(branchIf ? IF_ACMPEQ : IF_ACMPNE);
1181: else if (token == NEQ)
1182: bytecode.addOpcode(branchIf ? IF_ACMPNE : IF_ACMPEQ);
1183: else
1184: badTypes(expr);
1185: else if (p == P_INT) {
1186: int op[] = ifOp;
1187: for (int i = 0; i < op.length; i += 3)
1188: if (op[i] == token) {
1189: bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
1190: return;
1191: }
1192:
1193: badTypes(expr);
1194: } else {
1195: if (p == P_DOUBLE)
1196: if (token == '<' || token == LE)
1197: bytecode.addOpcode(DCMPG);
1198: else
1199: bytecode.addOpcode(DCMPL);
1200: else if (p == P_FLOAT)
1201: if (token == '<' || token == LE)
1202: bytecode.addOpcode(FCMPG);
1203: else
1204: bytecode.addOpcode(FCMPL);
1205: else if (p == P_LONG)
1206: bytecode.addOpcode(LCMP); // 1: >, 0: =, -1: <
1207: else
1208: fatal();
1209:
1210: int[] op = ifOp2;
1211: for (int i = 0; i < op.length; i += 3)
1212: if (op[i] == token) {
1213: bytecode.addOpcode(op[i + (branchIf ? 1 : 2)]);
1214: return;
1215: }
1216:
1217: badTypes(expr);
1218: }
1219: }
1220:
1221: protected static void badTypes(Expr expr) throws CompileError {
1222: throw new CompileError("invalid types for " + expr.getName());
1223: }
1224:
1225: private static final int P_DOUBLE = 0;
1226: private static final int P_FLOAT = 1;
1227: private static final int P_LONG = 2;
1228: private static final int P_INT = 3;
1229: private static final int P_OTHER = -1;
1230:
1231: protected static boolean isRefType(int type) {
1232: return type == CLASS || type == NULL;
1233: }
1234:
1235: private static int typePrecedence(int type) {
1236: if (type == DOUBLE)
1237: return P_DOUBLE;
1238: else if (type == FLOAT)
1239: return P_FLOAT;
1240: else if (type == LONG)
1241: return P_LONG;
1242: else if (isRefType(type))
1243: return P_OTHER;
1244: else if (type == VOID)
1245: return P_OTHER; // this is wrong, but ...
1246: else
1247: return P_INT; // BOOLEAN, BYTE, CHAR, SHORT, INT
1248: }
1249:
1250: // used in TypeChecker.
1251: static boolean isP_INT(int type) {
1252: return typePrecedence(type) == P_INT;
1253: }
1254:
1255: // used in TypeChecker.
1256: static boolean rightIsStrong(int type1, int type2) {
1257: int type1_p = typePrecedence(type1);
1258: int type2_p = typePrecedence(type2);
1259: return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p;
1260: }
1261:
1262: private static final int[] castOp = {
1263: /* D F L I */
1264: /* double */NOP, D2F, D2L, D2I,
1265: /* float */F2D, NOP, F2L, F2I,
1266: /* long */L2D, L2F, NOP, L2I,
1267: /* other */I2D, I2F, I2L, NOP };
1268:
1269: /* do implicit type conversion.
1270: * arrayDim values of the two oprands must be zero.
1271: */
1272: private void convertOprandTypes(int type1, int type2, Expr expr)
1273: throws CompileError {
1274: boolean rightStrong;
1275: int type1_p = typePrecedence(type1);
1276: int type2_p = typePrecedence(type2);
1277:
1278: if (type2_p < 0 && type1_p < 0) // not primitive types
1279: return;
1280:
1281: if (type2_p < 0 || type1_p < 0) // either is not a primitive type
1282: badTypes(expr);
1283:
1284: int op, result_type;
1285: if (type1_p <= type2_p) {
1286: rightStrong = false;
1287: exprType = type1;
1288: op = castOp[type2_p * 4 + type1_p];
1289: result_type = type1_p;
1290: } else {
1291: rightStrong = true;
1292: op = castOp[type1_p * 4 + type2_p];
1293: result_type = type2_p;
1294: }
1295:
1296: if (rightStrong) {
1297: if (result_type == P_DOUBLE || result_type == P_LONG) {
1298: if (type1_p == P_DOUBLE || type1_p == P_LONG)
1299: bytecode.addOpcode(DUP2_X2);
1300: else
1301: bytecode.addOpcode(DUP2_X1);
1302:
1303: bytecode.addOpcode(POP2);
1304: bytecode.addOpcode(op);
1305: bytecode.addOpcode(DUP2_X2);
1306: bytecode.addOpcode(POP2);
1307: } else if (result_type == P_FLOAT) {
1308: if (type1_p == P_LONG) {
1309: bytecode.addOpcode(DUP_X2);
1310: bytecode.addOpcode(POP);
1311: } else
1312: bytecode.addOpcode(SWAP);
1313:
1314: bytecode.addOpcode(op);
1315: bytecode.addOpcode(SWAP);
1316: } else
1317: fatal();
1318: } else if (op != NOP)
1319: bytecode.addOpcode(op);
1320: }
1321:
1322: public void atCastExpr(CastExpr expr) throws CompileError {
1323: String cname = resolveClassName(expr.getClassName());
1324: String toClass = checkCastExpr(expr, cname);
1325: int srcType = exprType;
1326: exprType = expr.getType();
1327: arrayDim = expr.getArrayDim();
1328: className = cname;
1329: if (toClass == null)
1330: atNumCastExpr(srcType, exprType); // built-in type
1331: else
1332: bytecode.addCheckcast(toClass);
1333: }
1334:
1335: public void atInstanceOfExpr(InstanceOfExpr expr)
1336: throws CompileError {
1337: String cname = resolveClassName(expr.getClassName());
1338: String toClass = checkCastExpr(expr, cname);
1339: bytecode.addInstanceof(toClass);
1340: exprType = BOOLEAN;
1341: arrayDim = 0;
1342: }
1343:
1344: private String checkCastExpr(CastExpr expr, String name)
1345: throws CompileError {
1346: final String msg = "invalid cast";
1347: ASTree oprand = expr.getOprand();
1348: int dim = expr.getArrayDim();
1349: int type = expr.getType();
1350: oprand.accept(this );
1351: int srcType = exprType;
1352: if (invalidDim(srcType, arrayDim, className, type, dim, name,
1353: true)
1354: || srcType == VOID || type == VOID)
1355: throw new CompileError(msg);
1356:
1357: if (type == CLASS) {
1358: if (!isRefType(srcType))
1359: throw new CompileError(msg);
1360:
1361: return toJvmArrayName(name, dim);
1362: } else if (dim > 0)
1363: return toJvmTypeName(type, dim);
1364: else
1365: return null; // built-in type
1366: }
1367:
1368: void atNumCastExpr(int srcType, int destType) throws CompileError {
1369: if (srcType == destType)
1370: return;
1371:
1372: int op, op2;
1373: int stype = typePrecedence(srcType);
1374: int dtype = typePrecedence(destType);
1375: if (0 <= stype && stype < 3)
1376: op = castOp[stype * 4 + dtype];
1377: else
1378: op = NOP;
1379:
1380: if (destType == DOUBLE)
1381: op2 = I2D;
1382: else if (destType == FLOAT)
1383: op2 = I2F;
1384: else if (destType == LONG)
1385: op2 = I2L;
1386: else if (destType == SHORT)
1387: op2 = I2S;
1388: else if (destType == CHAR)
1389: op2 = I2C;
1390: else if (destType == BYTE)
1391: op2 = I2B;
1392: else
1393: op2 = NOP;
1394:
1395: if (op != NOP)
1396: bytecode.addOpcode(op);
1397:
1398: if (op == NOP || op == L2I || op == F2I || op == D2I)
1399: if (op2 != NOP)
1400: bytecode.addOpcode(op2);
1401: }
1402:
1403: public void atExpr(Expr expr) throws CompileError {
1404: // array access, member access,
1405: // (unary) +, (unary) -, ++, --, !, ~
1406:
1407: int token = expr.getOperator();
1408: ASTree oprand = expr.oprand1();
1409: if (token == '.') {
1410: String member = ((Symbol) expr.oprand2()).get();
1411: if (member.equals("class"))
1412: atClassObject(expr); // .class
1413: else
1414: atFieldRead(expr);
1415: } else if (token == MEMBER) { // field read
1416: /* MEMBER ('#') is an extension by Javassist.
1417: * The compiler internally uses # for compiling .class
1418: * expressions such as "int.class".
1419: */
1420: atFieldRead(expr);
1421: } else if (token == ARRAY)
1422: atArrayRead(oprand, expr.oprand2());
1423: else if (token == PLUSPLUS || token == MINUSMINUS)
1424: atPlusPlus(token, oprand, expr, true);
1425: else if (token == '!') {
1426: booleanExpr(false, expr);
1427: bytecode.addIndex(7);
1428: bytecode.addIconst(1);
1429: bytecode.addOpcode(Opcode.GOTO);
1430: bytecode.addIndex(4);
1431: bytecode.addIconst(0);
1432: } else if (token == CALL) // method call
1433: fatal();
1434: else {
1435: expr.oprand1().accept(this );
1436: int type = typePrecedence(exprType);
1437: if (arrayDim > 0)
1438: badType(expr);
1439:
1440: if (token == '-') {
1441: if (type == P_DOUBLE)
1442: bytecode.addOpcode(DNEG);
1443: else if (type == P_FLOAT)
1444: bytecode.addOpcode(FNEG);
1445: else if (type == P_LONG)
1446: bytecode.addOpcode(LNEG);
1447: else if (type == P_INT) {
1448: bytecode.addOpcode(INEG);
1449: exprType = INT; // type may be BYTE, ...
1450: } else
1451: badType(expr);
1452: } else if (token == '~') {
1453: if (type == P_INT) {
1454: bytecode.addIconst(-1);
1455: bytecode.addOpcode(IXOR);
1456: exprType = INT; // type may be BYTE. ...
1457: } else if (type == P_LONG) {
1458: bytecode.addLconst(-1);
1459: bytecode.addOpcode(LXOR);
1460: } else
1461: badType(expr);
1462:
1463: } else if (token == '+') {
1464: if (type == P_OTHER)
1465: badType(expr);
1466:
1467: // do nothing. ignore.
1468: } else
1469: fatal();
1470: }
1471: }
1472:
1473: protected static void badType(Expr expr) throws CompileError {
1474: throw new CompileError("invalid type for " + expr.getName());
1475: }
1476:
1477: public abstract void atCallExpr(CallExpr expr) throws CompileError;
1478:
1479: protected abstract void atFieldRead(ASTree expr)
1480: throws CompileError;
1481:
1482: public void atClassObject(Expr expr) throws CompileError {
1483: ASTree op1 = expr.oprand1();
1484: if (!(op1 instanceof Symbol))
1485: throw new CompileError(
1486: "fatal error: badly parsed .class expr");
1487:
1488: String cname = ((Symbol) op1).get();
1489: if (cname.startsWith("[")) {
1490: int i = cname.indexOf("[L");
1491: if (i >= 0) {
1492: String name = cname
1493: .substring(i + 2, cname.length() - 1);
1494: String name2 = resolveClassName(name);
1495: if (!name.equals(name2)) {
1496: /* For example, to obtain String[].class,
1497: * "[Ljava.lang.String;" (not "[Ljava/lang/String"!)
1498: * must be passed to Class.forName().
1499: */
1500: name2 = MemberResolver.jvmToJavaName(name2);
1501: StringBuffer sbuf = new StringBuffer();
1502: while (i-- >= 0)
1503: sbuf.append('[');
1504:
1505: sbuf.append('L').append(name2).append(';');
1506: cname = sbuf.toString();
1507: }
1508: }
1509: } else {
1510: cname = resolveClassName(MemberResolver
1511: .javaToJvmName(cname));
1512: cname = MemberResolver.jvmToJavaName(cname);
1513: }
1514:
1515: int start = bytecode.currentPc();
1516: bytecode.addLdc(cname);
1517: bytecode.addInvokestatic("java.lang.Class", "forName",
1518: "(Ljava/lang/String;)Ljava/lang/Class;");
1519: int end = bytecode.currentPc();
1520: bytecode.addOpcode(Opcode.GOTO);
1521: int pc = bytecode.currentPc();
1522: bytecode.addIndex(0); // correct later
1523:
1524: bytecode.addExceptionHandler(start, end, bytecode.currentPc(),
1525: "java.lang.ClassNotFoundException");
1526:
1527: /* -- the following code is for inlining a call to DotClass.fail().
1528:
1529: int var = getMaxLocals();
1530: incMaxLocals(1);
1531: bytecode.growStack(1);
1532: bytecode.addAstore(var);
1533:
1534: bytecode.addNew("java.lang.NoClassDefFoundError");
1535: bytecode.addOpcode(DUP);
1536: bytecode.addAload(var);
1537: bytecode.addInvokevirtual("java.lang.ClassNotFoundException",
1538: "getMessage", "()Ljava/lang/String;");
1539: bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "<init>",
1540: "(Ljava/lang/String;)V");
1541: */
1542:
1543: bytecode.growStack(1);
1544: bytecode.addInvokestatic("javassist.runtime.DotClass", "fail",
1545: "(Ljava/lang/ClassNotFoundException;)"
1546: + "Ljava/lang/NoClassDefFoundError;");
1547: bytecode.addOpcode(ATHROW);
1548: bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
1549:
1550: exprType = CLASS;
1551: arrayDim = 0;
1552: className = "java/lang/Class";
1553: }
1554:
1555: public void atArrayRead(ASTree array, ASTree index)
1556: throws CompileError {
1557: arrayAccess(array, index);
1558: bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
1559: }
1560:
1561: protected void arrayAccess(ASTree array, ASTree index)
1562: throws CompileError {
1563: array.accept(this );
1564: int type = exprType;
1565: int dim = arrayDim;
1566: if (dim == 0)
1567: throw new CompileError("bad array access");
1568:
1569: String cname = className;
1570:
1571: index.accept(this );
1572: if (typePrecedence(exprType) != P_INT || arrayDim > 0)
1573: throw new CompileError("bad array index");
1574:
1575: exprType = type;
1576: arrayDim = dim - 1;
1577: className = cname;
1578: }
1579:
1580: protected static int getArrayReadOp(int type, int dim) {
1581: if (dim > 0)
1582: return AALOAD;
1583:
1584: switch (type) {
1585: case DOUBLE:
1586: return DALOAD;
1587: case FLOAT:
1588: return FALOAD;
1589: case LONG:
1590: return LALOAD;
1591: case INT:
1592: return IALOAD;
1593: case SHORT:
1594: return SALOAD;
1595: case CHAR:
1596: return CALOAD;
1597: case BYTE:
1598: case BOOLEAN:
1599: return BALOAD;
1600: default:
1601: return AALOAD;
1602: }
1603: }
1604:
1605: protected static int getArrayWriteOp(int type, int dim) {
1606: if (dim > 0)
1607: return AASTORE;
1608:
1609: switch (type) {
1610: case DOUBLE:
1611: return DASTORE;
1612: case FLOAT:
1613: return FASTORE;
1614: case LONG:
1615: return LASTORE;
1616: case INT:
1617: return IASTORE;
1618: case SHORT:
1619: return SASTORE;
1620: case CHAR:
1621: return CASTORE;
1622: case BYTE:
1623: case BOOLEAN:
1624: return BASTORE;
1625: default:
1626: return AASTORE;
1627: }
1628: }
1629:
1630: private void atPlusPlus(int token, ASTree oprand, Expr expr,
1631: boolean doDup) throws CompileError {
1632: boolean isPost = oprand == null; // ++i or i++?
1633: if (isPost)
1634: oprand = expr.oprand2();
1635:
1636: if (oprand instanceof Variable) {
1637: Declarator d = ((Variable) oprand).getDeclarator();
1638: int t = exprType = d.getType();
1639: arrayDim = d.getArrayDim();
1640: int var = getLocalVar(d);
1641: if (arrayDim > 0)
1642: badType(expr);
1643:
1644: if (t == DOUBLE) {
1645: bytecode.addDload(var);
1646: if (doDup && isPost)
1647: bytecode.addOpcode(DUP2);
1648:
1649: bytecode.addDconst(1.0);
1650: bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
1651: if (doDup && !isPost)
1652: bytecode.addOpcode(DUP2);
1653:
1654: bytecode.addDstore(var);
1655: } else if (t == LONG) {
1656: bytecode.addLload(var);
1657: if (doDup && isPost)
1658: bytecode.addOpcode(DUP2);
1659:
1660: bytecode.addLconst((long) 1);
1661: bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
1662: if (doDup && !isPost)
1663: bytecode.addOpcode(DUP2);
1664:
1665: bytecode.addLstore(var);
1666: } else if (t == FLOAT) {
1667: bytecode.addFload(var);
1668: if (doDup && isPost)
1669: bytecode.addOpcode(DUP);
1670:
1671: bytecode.addFconst(1.0f);
1672: bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
1673: if (doDup && !isPost)
1674: bytecode.addOpcode(DUP);
1675:
1676: bytecode.addFstore(var);
1677: } else if (t == BYTE || t == CHAR || t == SHORT || t == INT) {
1678: if (doDup && isPost)
1679: bytecode.addIload(var);
1680:
1681: bytecode.addOpcode(IINC);
1682: bytecode.add(var);
1683: bytecode.add(token == PLUSPLUS ? 1 : -1);
1684:
1685: if (doDup && !isPost)
1686: bytecode.addIload(var);
1687: } else
1688: badType(expr);
1689: } else {
1690: if (oprand instanceof Expr) {
1691: Expr e = (Expr) oprand;
1692: if (e.getOperator() == ARRAY) {
1693: atArrayPlusPlus(token, isPost, e, doDup);
1694: return;
1695: }
1696: }
1697:
1698: atFieldPlusPlus(token, isPost, oprand, expr, doDup);
1699: }
1700: }
1701:
1702: public void atArrayPlusPlus(int token, boolean isPost, Expr expr,
1703: boolean doDup) throws CompileError {
1704: arrayAccess(expr.oprand1(), expr.oprand2());
1705: int t = exprType;
1706: int dim = arrayDim;
1707: if (dim > 0)
1708: badType(expr);
1709:
1710: bytecode.addOpcode(DUP2);
1711: bytecode.addOpcode(getArrayReadOp(t, arrayDim));
1712: int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2;
1713: atPlusPlusCore(dup_code, doDup, token, isPost, expr);
1714: bytecode.addOpcode(getArrayWriteOp(t, dim));
1715: }
1716:
1717: protected void atPlusPlusCore(int dup_code, boolean doDup,
1718: int token, boolean isPost, Expr expr) throws CompileError {
1719: int t = exprType;
1720:
1721: if (doDup && isPost)
1722: bytecode.addOpcode(dup_code);
1723:
1724: if (t == INT || t == BYTE || t == CHAR || t == SHORT) {
1725: bytecode.addIconst(1);
1726: bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB);
1727: exprType = INT;
1728: } else if (t == LONG) {
1729: bytecode.addLconst((long) 1);
1730: bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
1731: } else if (t == FLOAT) {
1732: bytecode.addFconst(1.0f);
1733: bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
1734: } else if (t == DOUBLE) {
1735: bytecode.addDconst(1.0);
1736: bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
1737: } else
1738: badType(expr);
1739:
1740: if (doDup && !isPost)
1741: bytecode.addOpcode(dup_code);
1742: }
1743:
1744: protected abstract void atFieldPlusPlus(int token, boolean isPost,
1745: ASTree oprand, Expr expr, boolean doDup)
1746: throws CompileError;
1747:
1748: public abstract void atMember(Member n) throws CompileError;
1749:
1750: public void atVariable(Variable v) throws CompileError {
1751: Declarator d = v.getDeclarator();
1752: exprType = d.getType();
1753: arrayDim = d.getArrayDim();
1754: className = d.getClassName();
1755: int var = getLocalVar(d);
1756:
1757: if (arrayDim > 0)
1758: bytecode.addAload(var);
1759: else
1760: switch (exprType) {
1761: case CLASS:
1762: bytecode.addAload(var);
1763: break;
1764: case LONG:
1765: bytecode.addLload(var);
1766: break;
1767: case FLOAT:
1768: bytecode.addFload(var);
1769: break;
1770: case DOUBLE:
1771: bytecode.addDload(var);
1772: break;
1773: default: // BOOLEAN, BYTE, CHAR, SHORT, INT
1774: bytecode.addIload(var);
1775: break;
1776: }
1777: }
1778:
1779: public void atKeyword(Keyword k) throws CompileError {
1780: arrayDim = 0;
1781: int token = k.get();
1782: switch (token) {
1783: case TRUE:
1784: bytecode.addIconst(1);
1785: exprType = BOOLEAN;
1786: break;
1787: case FALSE:
1788: bytecode.addIconst(0);
1789: exprType = BOOLEAN;
1790: break;
1791: case NULL:
1792: bytecode.addOpcode(ACONST_NULL);
1793: exprType = NULL;
1794: break;
1795: case THIS:
1796: case SUPER:
1797: if (inStaticMethod)
1798: throw new CompileError("not-available: "
1799: + (token == THIS ? "this" : "super"));
1800:
1801: bytecode.addAload(0);
1802: exprType = CLASS;
1803: if (token == THIS)
1804: className = getThisName();
1805: else
1806: className = getSuperName();
1807: break;
1808: default:
1809: fatal();
1810: }
1811: }
1812:
1813: public void atStringL(StringL s) throws CompileError {
1814: exprType = CLASS;
1815: arrayDim = 0;
1816: className = jvmJavaLangString;
1817: bytecode.addLdc(s.get());
1818: }
1819:
1820: public void atIntConst(IntConst i) throws CompileError {
1821: arrayDim = 0;
1822: long value = i.get();
1823: int type = i.getType();
1824: if (type == IntConstant || type == CharConstant) {
1825: exprType = (type == IntConstant ? INT : CHAR);
1826: bytecode.addIconst((int) value);
1827: } else {
1828: exprType = LONG;
1829: bytecode.addLconst(value);
1830: }
1831: }
1832:
1833: public void atDoubleConst(DoubleConst d) throws CompileError {
1834: arrayDim = 0;
1835: if (d.getType() == DoubleConstant) {
1836: exprType = DOUBLE;
1837: bytecode.addDconst(d.get());
1838: } else {
1839: exprType = FLOAT;
1840: bytecode.addFconst((float) d.get());
1841: }
1842: }
1843: }
|