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 javassist.*;
0019: import javassist.bytecode.*;
0020: import javassist.compiler.ast.*;
0021:
0022: import java.util.ArrayList;
0023:
0024: /* Code generator methods depending on javassist.* classes.
0025: */
0026: public class MemberCodeGen extends CodeGen {
0027: protected MemberResolver resolver;
0028: protected CtClass this Class;
0029: protected MethodInfo this Method;
0030:
0031: protected boolean resultStatic;
0032:
0033: public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) {
0034: super (b);
0035: resolver = new MemberResolver(cp);
0036: this Class = cc;
0037: this Method = null;
0038: }
0039:
0040: /**
0041: * Records the currently compiled method.
0042: */
0043: public void setThisMethod(CtMethod m) {
0044: this Method = m.getMethodInfo2();
0045: if (typeChecker != null)
0046: typeChecker.setThisMethod(this Method);
0047: }
0048:
0049: public CtClass getThisClass() {
0050: return this Class;
0051: }
0052:
0053: /**
0054: * Returns the JVM-internal representation of this class name.
0055: */
0056: protected String getThisName() {
0057: return MemberResolver.javaToJvmName(this Class.getName());
0058: }
0059:
0060: /**
0061: * Returns the JVM-internal representation of this super class name.
0062: */
0063: protected String getSuperName() throws CompileError {
0064: return MemberResolver.javaToJvmName(MemberResolver
0065: .getSuperclass(this Class).getName());
0066: }
0067:
0068: protected void insertDefaultSuperCall() throws CompileError {
0069: bytecode.addAload(0);
0070: bytecode.addInvokespecial(MemberResolver
0071: .getSuperclass(this Class), "<init>", "()V");
0072: }
0073:
0074: static class JsrHook extends ReturnHook {
0075: ArrayList jsrList;
0076: int var;
0077:
0078: JsrHook(CodeGen gen) {
0079: super (gen);
0080: jsrList = new ArrayList();
0081: var = gen.getMaxLocals();
0082: gen.incMaxLocals(1);
0083: }
0084:
0085: private void jsrJmp(Bytecode b) {
0086: b.addOpcode(JSR);
0087: jsrList.add(new Integer(b.currentPc()));
0088: b.addIndex(0);
0089: }
0090:
0091: protected void doit(Bytecode b, int opcode) {
0092: switch (opcode) {
0093: case Opcode.RETURN:
0094: jsrJmp(b);
0095: break;
0096: case ARETURN:
0097: b.addAstore(var);
0098: jsrJmp(b);
0099: b.addAload(var);
0100: break;
0101: case IRETURN:
0102: b.addIstore(var);
0103: jsrJmp(b);
0104: b.addIload(var);
0105: break;
0106: case LRETURN:
0107: b.addLstore(var);
0108: jsrJmp(b);
0109: b.addLload(var);
0110: break;
0111: case DRETURN:
0112: b.addDstore(var);
0113: jsrJmp(b);
0114: b.addDload(var);
0115: break;
0116: case FRETURN:
0117: b.addFstore(var);
0118: jsrJmp(b);
0119: b.addFload(var);
0120: break;
0121: default:
0122: throw new RuntimeException("fatal");
0123: }
0124: }
0125: }
0126:
0127: protected void atTryStmnt(Stmnt st) throws CompileError {
0128: Bytecode bc = bytecode;
0129: Stmnt body = (Stmnt) st.getLeft();
0130: if (body == null)
0131: return;
0132:
0133: ASTList catchList = (ASTList) st.getRight().getLeft();
0134: Stmnt finallyBlock = (Stmnt) st.getRight().getRight().getLeft();
0135: ArrayList gotoList = new ArrayList();
0136:
0137: JsrHook jsrHook = null;
0138: if (finallyBlock != null)
0139: jsrHook = new JsrHook(this );
0140:
0141: int start = bc.currentPc();
0142: body.accept(this );
0143: int end = bc.currentPc();
0144: if (start == end)
0145: throw new CompileError("empty try block");
0146:
0147: boolean tryNotReturn = !hasReturned;
0148: if (tryNotReturn) {
0149: bc.addOpcode(Opcode.GOTO);
0150: gotoList.add(new Integer(bc.currentPc()));
0151: bc.addIndex(0); // correct later
0152: }
0153:
0154: int var = getMaxLocals();
0155: incMaxLocals(1);
0156: while (catchList != null) {
0157: // catch clause
0158: Pair p = (Pair) catchList.head();
0159: catchList = catchList.tail();
0160: Declarator decl = (Declarator) p.getLeft();
0161: Stmnt block = (Stmnt) p.getRight();
0162:
0163: decl.setLocalVar(var);
0164:
0165: CtClass type = resolver.lookupClassByJvmName(decl
0166: .getClassName());
0167: decl.setClassName(MemberResolver.javaToJvmName(type
0168: .getName()));
0169: bc.addExceptionHandler(start, end, bc.currentPc(), type);
0170: bc.growStack(1);
0171: bc.addAstore(var);
0172: hasReturned = false;
0173: if (block != null)
0174: block.accept(this );
0175:
0176: if (!hasReturned) {
0177: bc.addOpcode(Opcode.GOTO);
0178: gotoList.add(new Integer(bc.currentPc()));
0179: bc.addIndex(0); // correct later
0180: tryNotReturn = true;
0181: }
0182: }
0183:
0184: int pcFinally = -1;
0185: if (finallyBlock != null) {
0186: jsrHook.remove(this );
0187: // catch (any) clause
0188: int pcAnyCatch = bc.currentPc();
0189: bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0);
0190: bc.growStack(1);
0191: bc.addAstore(var);
0192: bc.addOpcode(JSR);
0193: int pcJsrIndex = bc.currentPc();
0194: bc.addIndex(0); // correct later
0195: bc.addAload(var);
0196: bc.addOpcode(ATHROW);
0197:
0198: // finally clause
0199: pcFinally = bc.currentPc();
0200: bc.write16bit(pcJsrIndex, pcFinally - pcJsrIndex + 1);
0201: int retAddr = getMaxLocals();
0202: incMaxLocals(1);
0203: bc.growStack(1); // return address
0204: bc.addAstore(retAddr);
0205: hasReturned = false;
0206: finallyBlock.accept(this );
0207: if (!hasReturned) {
0208: bc.addOpcode(RET);
0209: bc.add(retAddr);
0210: }
0211: }
0212:
0213: int pcEnd = bc.currentPc();
0214: patchGoto(gotoList, pcEnd);
0215: if (finallyBlock != null) {
0216: patchGoto(jsrHook.jsrList, pcFinally);
0217: if (tryNotReturn) {
0218: bc.addOpcode(JSR);
0219: bc.addIndex(pcFinally - pcEnd);
0220: }
0221: }
0222:
0223: hasReturned = !tryNotReturn;
0224: }
0225:
0226: public void atNewExpr(NewExpr expr) throws CompileError {
0227: if (expr.isArray())
0228: atNewArrayExpr(expr);
0229: else {
0230: CtClass clazz = resolver.lookupClassByName(expr
0231: .getClassName());
0232: String cname = clazz.getName();
0233: ASTList args = expr.getArguments();
0234: bytecode.addNew(cname);
0235: bytecode.addOpcode(DUP);
0236:
0237: atMethodCallCore(clazz, MethodInfo.nameInit, args, false,
0238: true, -1, null);
0239:
0240: exprType = CLASS;
0241: arrayDim = 0;
0242: className = MemberResolver.javaToJvmName(cname);
0243: }
0244: }
0245:
0246: public void atNewArrayExpr(NewExpr expr) throws CompileError {
0247: int type = expr.getArrayType();
0248: ASTList size = expr.getArraySize();
0249: ASTList classname = expr.getClassName();
0250: ArrayInit init = expr.getInitializer();
0251: if (size.length() > 1) {
0252: if (init != null)
0253: throw new CompileError(
0254: "sorry, multi-dimensional array initializer "
0255: + "for new is not supported");
0256:
0257: atMultiNewArray(type, classname, size);
0258: return;
0259: }
0260:
0261: ASTree sizeExpr = size.head();
0262: atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(
0263: classname, '/'), init);
0264: }
0265:
0266: private void atNewArrayExpr2(int type, ASTree sizeExpr,
0267: String jvmClassname, ArrayInit init) throws CompileError {
0268: if (init == null)
0269: if (sizeExpr == null)
0270: throw new CompileError("no array size");
0271: else
0272: sizeExpr.accept(this );
0273: else if (sizeExpr == null) {
0274: int s = init.length();
0275: bytecode.addIconst(s);
0276: } else
0277: throw new CompileError(
0278: "unnecessary array size specified for new");
0279:
0280: String elementClass;
0281: if (type == CLASS) {
0282: elementClass = resolveClassName(jvmClassname);
0283: bytecode.addAnewarray(MemberResolver
0284: .jvmToJavaName(elementClass));
0285: } else {
0286: elementClass = null;
0287: int atype = 0;
0288: switch (type) {
0289: case BOOLEAN:
0290: atype = T_BOOLEAN;
0291: break;
0292: case CHAR:
0293: atype = T_CHAR;
0294: break;
0295: case FLOAT:
0296: atype = T_FLOAT;
0297: break;
0298: case DOUBLE:
0299: atype = T_DOUBLE;
0300: break;
0301: case BYTE:
0302: atype = T_BYTE;
0303: break;
0304: case SHORT:
0305: atype = T_SHORT;
0306: break;
0307: case INT:
0308: atype = T_INT;
0309: break;
0310: case LONG:
0311: atype = T_LONG;
0312: break;
0313: default:
0314: badNewExpr();
0315: break;
0316: }
0317:
0318: bytecode.addOpcode(NEWARRAY);
0319: bytecode.add(atype);
0320: }
0321:
0322: if (init != null) {
0323: int s = init.length();
0324: ASTList list = init;
0325: for (int i = 0; i < s; i++) {
0326: bytecode.addOpcode(DUP);
0327: bytecode.addIconst(i);
0328: list.head().accept(this );
0329: if (!isRefType(type))
0330: atNumCastExpr(exprType, type);
0331:
0332: bytecode.addOpcode(getArrayWriteOp(type, 0));
0333: list = list.tail();
0334: }
0335: }
0336:
0337: exprType = type;
0338: arrayDim = 1;
0339: className = elementClass;
0340: }
0341:
0342: private static void badNewExpr() throws CompileError {
0343: throw new CompileError("bad new expression");
0344: }
0345:
0346: protected void atArrayVariableAssign(ArrayInit init, int varType,
0347: int varArray, String varClass) throws CompileError {
0348: atNewArrayExpr2(varType, null, varClass, init);
0349: }
0350:
0351: public void atArrayInit(ArrayInit init) throws CompileError {
0352: throw new CompileError("array initializer is not supported");
0353: }
0354:
0355: protected void atMultiNewArray(int type, ASTList classname,
0356: ASTList size) throws CompileError {
0357: int count, dim;
0358: dim = size.length();
0359: for (count = 0; size != null; size = size.tail()) {
0360: ASTree s = size.head();
0361: if (s == null)
0362: break; // int[][][] a = new int[3][4][];
0363:
0364: ++count;
0365: s.accept(this );
0366: if (exprType != INT)
0367: throw new CompileError("bad type for array size");
0368: }
0369:
0370: String desc;
0371: exprType = type;
0372: arrayDim = dim;
0373: if (type == CLASS) {
0374: className = resolveClassName(classname);
0375: desc = toJvmArrayName(className, dim);
0376: } else
0377: desc = toJvmTypeName(type, dim);
0378:
0379: bytecode.addMultiNewarray(desc, count);
0380: }
0381:
0382: public void atCallExpr(CallExpr expr) throws CompileError {
0383: String mname = null;
0384: CtClass targetClass = null;
0385: ASTree method = expr.oprand1();
0386: ASTList args = (ASTList) expr.oprand2();
0387: boolean isStatic = false;
0388: boolean isSpecial = false;
0389: int aload0pos = -1;
0390:
0391: MemberResolver.Method cached = expr.getMethod();
0392: if (method instanceof Member) {
0393: mname = ((Member) method).get();
0394: targetClass = this Class;
0395: if (inStaticMethod || (cached != null && cached.isStatic()))
0396: isStatic = true; // should be static
0397: else {
0398: aload0pos = bytecode.currentPc();
0399: bytecode.addAload(0); // this
0400: }
0401: } else if (method instanceof Keyword) { // constructor
0402: isSpecial = true;
0403: mname = MethodInfo.nameInit; // <init>
0404: targetClass = this Class;
0405: if (inStaticMethod)
0406: throw new CompileError("a constructor cannot be static");
0407: else
0408: bytecode.addAload(0); // this
0409:
0410: if (((Keyword) method).get() == SUPER)
0411: targetClass = MemberResolver.getSuperclass(targetClass);
0412: } else if (method instanceof Expr) {
0413: Expr e = (Expr) method;
0414: mname = ((Symbol) e.oprand2()).get();
0415: int op = e.getOperator();
0416: if (op == MEMBER) { // static method
0417: targetClass = resolver.lookupClass(((Symbol) e
0418: .oprand1()).get(), false);
0419: isStatic = true;
0420: } else if (op == '.') {
0421: ASTree target = e.oprand1();
0422: if (target instanceof Keyword)
0423: if (((Keyword) target).get() == SUPER)
0424: isSpecial = true;
0425:
0426: try {
0427: target.accept(this );
0428: } catch (NoFieldException nfe) {
0429: if (nfe.getExpr() != target)
0430: throw nfe;
0431:
0432: // it should be a static method.
0433: exprType = CLASS;
0434: arrayDim = 0;
0435: className = nfe.getField(); // JVM-internal
0436: resolver.recordPackage(className);
0437: isStatic = true;
0438: }
0439:
0440: if (arrayDim > 0)
0441: targetClass = resolver.lookupClass(javaLangObject,
0442: true);
0443: else if (exprType == CLASS /* && arrayDim == 0 */)
0444: targetClass = resolver
0445: .lookupClassByJvmName(className);
0446: else
0447: badMethod();
0448: } else
0449: badMethod();
0450: } else
0451: fatal();
0452:
0453: atMethodCallCore(targetClass, mname, args, isStatic, isSpecial,
0454: aload0pos, cached);
0455: }
0456:
0457: private static void badMethod() throws CompileError {
0458: throw new CompileError("bad method");
0459: }
0460:
0461: /*
0462: * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew
0463: *
0464: * @param targetClass the class at which method lookup starts.
0465: * @param found not null if the method look has been already done.
0466: */
0467: public void atMethodCallCore(CtClass targetClass, String mname,
0468: ASTList args, boolean isStatic, boolean isSpecial,
0469: int aload0pos, MemberResolver.Method found)
0470: throws CompileError {
0471: int nargs = getMethodArgsLength(args);
0472: int[] types = new int[nargs];
0473: int[] dims = new int[nargs];
0474: String[] cnames = new String[nargs];
0475:
0476: if (!isStatic && found != null && found.isStatic()) {
0477: bytecode.addOpcode(POP);
0478: isStatic = true;
0479: }
0480:
0481: int stack = bytecode.getStackDepth();
0482:
0483: // generate code for evaluating arguments.
0484: atMethodArgs(args, types, dims, cnames);
0485:
0486: // used by invokeinterface
0487: int count = bytecode.getStackDepth() - stack + 1;
0488:
0489: if (found == null)
0490: found = resolver.lookupMethod(targetClass, this Class,
0491: this Method, mname, types, dims, cnames);
0492:
0493: if (found == null) {
0494: String msg;
0495: if (mname.equals(MethodInfo.nameInit))
0496: msg = "constructor not found";
0497: else
0498: msg = "Method " + mname + " not found in "
0499: + targetClass.getName();
0500:
0501: throw new CompileError(msg);
0502: }
0503:
0504: atMethodCallCore2(targetClass, mname, isStatic, isSpecial,
0505: aload0pos, count, found);
0506: }
0507:
0508: private void atMethodCallCore2(CtClass targetClass, String mname,
0509: boolean isStatic, boolean isSpecial, int aload0pos,
0510: int count, MemberResolver.Method found) throws CompileError {
0511: CtClass declClass = found.declaring;
0512: MethodInfo minfo = found.info;
0513: String desc = minfo.getDescriptor();
0514: int acc = minfo.getAccessFlags();
0515:
0516: if (mname.equals(MethodInfo.nameInit)) {
0517: isSpecial = true;
0518: if (declClass != targetClass)
0519: throw new CompileError("no such a constructor");
0520:
0521: if (declClass != this Class && AccessFlag.isPrivate(acc)) {
0522: desc = getAccessibleConstructor(desc, declClass, minfo);
0523: bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter
0524: }
0525: } else if (AccessFlag.isPrivate(acc))
0526: if (declClass == this Class)
0527: isSpecial = true;
0528: else {
0529: isSpecial = false;
0530: isStatic = true;
0531: String origDesc = desc;
0532: if ((acc & AccessFlag.STATIC) == 0)
0533: desc = Descriptor.insertParameter(declClass
0534: .getName(), origDesc);
0535:
0536: acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC;
0537: mname = getAccessiblePrivate(mname, origDesc, desc,
0538: minfo, declClass);
0539: }
0540:
0541: boolean popTarget = false;
0542: if ((acc & AccessFlag.STATIC) != 0) {
0543: if (!isStatic) {
0544: /* this method is static but the target object is
0545: on stack. It must be popped out. If aload0pos >= 0,
0546: then the target object was pushed by aload_0. It is
0547: overwritten by NOP.
0548: */
0549: isStatic = true;
0550: if (aload0pos >= 0)
0551: bytecode.write(aload0pos, NOP);
0552: else
0553: popTarget = true;
0554: }
0555:
0556: bytecode.addInvokestatic(declClass, mname, desc);
0557: } else if (isSpecial) // if (isSpecial && notStatic(acc))
0558: bytecode.addInvokespecial(declClass, mname, desc);
0559: else if (declClass.isInterface())
0560: bytecode.addInvokeinterface(declClass, mname, desc, count);
0561: else if (isStatic)
0562: throw new CompileError(mname + " is not static");
0563: else
0564: bytecode.addInvokevirtual(declClass, mname, desc);
0565:
0566: setReturnType(desc, isStatic, popTarget);
0567: }
0568:
0569: /*
0570: * Finds (or adds if necessary) a hidden accessor if the method
0571: * is in an enclosing class.
0572: *
0573: * @param desc the descriptor of the method.
0574: * @param declClass the class declaring the method.
0575: */
0576: protected String getAccessiblePrivate(String methodName,
0577: String desc, String newDesc, MethodInfo minfo,
0578: CtClass declClass) throws CompileError {
0579: if (isEnclosing(declClass, this Class)) {
0580: AccessorMaker maker = declClass.getAccessorMaker();
0581: if (maker != null)
0582: return maker.getMethodAccessor(methodName, desc,
0583: newDesc, minfo);
0584: }
0585:
0586: throw new CompileError("Method " + methodName + " is private");
0587: }
0588:
0589: /*
0590: * Finds (or adds if necessary) a hidden constructor if the given
0591: * constructor is in an enclosing class.
0592: *
0593: * @param desc the descriptor of the constructor.
0594: * @param declClass the class declaring the constructor.
0595: * @param minfo the method info of the constructor.
0596: * @return the descriptor of the hidden constructor.
0597: */
0598: protected String getAccessibleConstructor(String desc,
0599: CtClass declClass, MethodInfo minfo) throws CompileError {
0600: if (isEnclosing(declClass, this Class)) {
0601: AccessorMaker maker = declClass.getAccessorMaker();
0602: if (maker != null)
0603: return maker.getConstructor(declClass, desc, minfo);
0604: }
0605:
0606: throw new CompileError("the called constructor is private in "
0607: + declClass.getName());
0608: }
0609:
0610: private boolean isEnclosing(CtClass outer, CtClass inner) {
0611: try {
0612: while (inner != null) {
0613: inner = inner.getDeclaringClass();
0614: if (inner == outer)
0615: return true;
0616: }
0617: } catch (NotFoundException e) {
0618: }
0619: return false;
0620: }
0621:
0622: public int getMethodArgsLength(ASTList args) {
0623: return ASTList.length(args);
0624: }
0625:
0626: public void atMethodArgs(ASTList args, int[] types, int[] dims,
0627: String[] cnames) throws CompileError {
0628: int i = 0;
0629: while (args != null) {
0630: ASTree a = args.head();
0631: a.accept(this );
0632: types[i] = exprType;
0633: dims[i] = arrayDim;
0634: cnames[i] = className;
0635: ++i;
0636: args = args.tail();
0637: }
0638: }
0639:
0640: void setReturnType(String desc, boolean isStatic, boolean popTarget)
0641: throws CompileError {
0642: int i = desc.indexOf(')');
0643: if (i < 0)
0644: badMethod();
0645:
0646: char c = desc.charAt(++i);
0647: int dim = 0;
0648: while (c == '[') {
0649: ++dim;
0650: c = desc.charAt(++i);
0651: }
0652:
0653: arrayDim = dim;
0654: if (c == 'L') {
0655: int j = desc.indexOf(';', i + 1);
0656: if (j < 0)
0657: badMethod();
0658:
0659: exprType = CLASS;
0660: className = desc.substring(i + 1, j);
0661: } else {
0662: exprType = MemberResolver.descToType(c);
0663: className = null;
0664: }
0665:
0666: int etype = exprType;
0667: if (isStatic) {
0668: if (popTarget) {
0669: if (is2word(etype, dim)) {
0670: bytecode.addOpcode(DUP2_X1);
0671: bytecode.addOpcode(POP2);
0672: bytecode.addOpcode(POP);
0673: } else if (etype == VOID)
0674: bytecode.addOpcode(POP);
0675: else {
0676: bytecode.addOpcode(SWAP);
0677: bytecode.addOpcode(POP);
0678: }
0679: }
0680: }
0681: }
0682:
0683: protected void atFieldAssign(Expr expr, int op, ASTree left,
0684: ASTree right, boolean doDup) throws CompileError {
0685: CtField f = fieldAccess(left, false);
0686: boolean is_static = resultStatic;
0687: if (op != '=' && !is_static)
0688: bytecode.addOpcode(DUP);
0689:
0690: int fi;
0691: if (op == '=') {
0692: FieldInfo finfo = f.getFieldInfo2();
0693: setFieldType(finfo);
0694: AccessorMaker maker = isAccessibleField(f, finfo);
0695: if (maker == null)
0696: fi = addFieldrefInfo(f, finfo);
0697: else
0698: fi = 0;
0699: } else
0700: fi = atFieldRead(f, is_static);
0701:
0702: int fType = exprType;
0703: int fDim = arrayDim;
0704: String cname = className;
0705:
0706: atAssignCore(expr, op, right, fType, fDim, cname);
0707:
0708: boolean is2w = is2word(fType, fDim);
0709: if (doDup) {
0710: int dup_code;
0711: if (is_static)
0712: dup_code = (is2w ? DUP2 : DUP);
0713: else
0714: dup_code = (is2w ? DUP2_X1 : DUP_X1);
0715:
0716: bytecode.addOpcode(dup_code);
0717: }
0718:
0719: atFieldAssignCore(f, is_static, fi, is2w);
0720:
0721: exprType = fType;
0722: arrayDim = fDim;
0723: className = cname;
0724: }
0725:
0726: /* If fi == 0, the field must be a private field in an enclosing class.
0727: */
0728: private void atFieldAssignCore(CtField f, boolean is_static,
0729: int fi, boolean is2byte) throws CompileError {
0730: if (fi != 0) {
0731: if (is_static) {
0732: bytecode.add(PUTSTATIC);
0733: bytecode.growStack(is2byte ? -2 : -1);
0734: } else {
0735: bytecode.add(PUTFIELD);
0736: bytecode.growStack(is2byte ? -3 : -2);
0737: }
0738:
0739: bytecode.addIndex(fi);
0740: } else {
0741: CtClass declClass = f.getDeclaringClass();
0742: AccessorMaker maker = declClass.getAccessorMaker();
0743: // make should be non null.
0744: FieldInfo finfo = f.getFieldInfo2();
0745: MethodInfo minfo = maker.getFieldSetter(finfo, is_static);
0746: bytecode.addInvokestatic(declClass, minfo.getName(), minfo
0747: .getDescriptor());
0748: }
0749: }
0750:
0751: /* overwritten in JvstCodeGen.
0752: */
0753: public void atMember(Member mem) throws CompileError {
0754: atFieldRead(mem);
0755: }
0756:
0757: protected void atFieldRead(ASTree expr) throws CompileError {
0758: CtField f = fieldAccess(expr, true);
0759: if (f == null) {
0760: atArrayLength(expr);
0761: return;
0762: }
0763:
0764: boolean is_static = resultStatic;
0765: ASTree cexpr = TypeChecker.getConstantFieldValue(f);
0766: if (cexpr == null)
0767: atFieldRead(f, is_static);
0768: else {
0769: cexpr.accept(this );
0770: setFieldType(f.getFieldInfo2());
0771: }
0772: }
0773:
0774: private void atArrayLength(ASTree expr) throws CompileError {
0775: if (arrayDim == 0)
0776: throw new CompileError(".length applied to a non array");
0777:
0778: bytecode.addOpcode(ARRAYLENGTH);
0779: exprType = INT;
0780: arrayDim = 0;
0781: }
0782:
0783: /**
0784: * Generates bytecode for reading a field value.
0785: * It returns a fieldref_info index or zero if the field is a private
0786: * one declared in an enclosing class.
0787: */
0788: private int atFieldRead(CtField f, boolean isStatic)
0789: throws CompileError {
0790: FieldInfo finfo = f.getFieldInfo2();
0791: boolean is2byte = setFieldType(finfo);
0792: AccessorMaker maker = isAccessibleField(f, finfo);
0793: if (maker != null) {
0794: MethodInfo minfo = maker.getFieldGetter(finfo, isStatic);
0795: bytecode.addInvokestatic(f.getDeclaringClass(), minfo
0796: .getName(), minfo.getDescriptor());
0797: return 0;
0798: } else {
0799: int fi = addFieldrefInfo(f, finfo);
0800: if (isStatic) {
0801: bytecode.add(GETSTATIC);
0802: bytecode.growStack(is2byte ? 2 : 1);
0803: } else {
0804: bytecode.add(GETFIELD);
0805: bytecode.growStack(is2byte ? 1 : 0);
0806: }
0807:
0808: bytecode.addIndex(fi);
0809: return fi;
0810: }
0811: }
0812:
0813: /**
0814: * Returns null if the field is accessible. Otherwise, it throws
0815: * an exception or it returns AccessorMaker if the field is a private
0816: * one declared in an enclosing class.
0817: */
0818: private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo)
0819: throws CompileError {
0820: if (AccessFlag.isPrivate(finfo.getAccessFlags())
0821: && f.getDeclaringClass() != this Class) {
0822: CtClass declClass = f.getDeclaringClass();
0823: if (isEnclosing(declClass, this Class)) {
0824: AccessorMaker maker = declClass.getAccessorMaker();
0825: if (maker != null)
0826: return maker;
0827: else
0828: throw new CompileError("fatal error. bug?");
0829: } else
0830: throw new CompileError("Field " + f.getName() + " in "
0831: + declClass.getName() + " is private.");
0832: }
0833:
0834: return null; // accessible field
0835: }
0836:
0837: /**
0838: * Sets exprType, arrayDim, and className.
0839: *
0840: * @return true if the field type is long or double.
0841: */
0842: private boolean setFieldType(FieldInfo finfo) throws CompileError {
0843: String type = finfo.getDescriptor();
0844:
0845: int i = 0;
0846: int dim = 0;
0847: char c = type.charAt(i);
0848: while (c == '[') {
0849: ++dim;
0850: c = type.charAt(++i);
0851: }
0852:
0853: arrayDim = dim;
0854: exprType = MemberResolver.descToType(c);
0855:
0856: if (c == 'L')
0857: className = type.substring(i + 1, type.indexOf(';', i + 1));
0858: else
0859: className = null;
0860:
0861: boolean is2byte = (c == 'J' || c == 'D');
0862: return is2byte;
0863: }
0864:
0865: private int addFieldrefInfo(CtField f, FieldInfo finfo) {
0866: ConstPool cp = bytecode.getConstPool();
0867: String cname = f.getDeclaringClass().getName();
0868: int ci = cp.addClassInfo(cname);
0869: String name = finfo.getName();
0870: String type = finfo.getDescriptor();
0871: return cp.addFieldrefInfo(ci, name, type);
0872: }
0873:
0874: protected void atFieldPlusPlus(int token, boolean isPost,
0875: ASTree oprand, Expr expr, boolean doDup)
0876: throws CompileError {
0877: CtField f = fieldAccess(oprand, false);
0878: boolean is_static = resultStatic;
0879: if (!is_static)
0880: bytecode.addOpcode(DUP);
0881:
0882: int fi = atFieldRead(f, is_static);
0883: int t = exprType;
0884: boolean is2w = is2word(t, arrayDim);
0885:
0886: int dup_code;
0887: if (is_static)
0888: dup_code = (is2w ? DUP2 : DUP);
0889: else
0890: dup_code = (is2w ? DUP2_X1 : DUP_X1);
0891:
0892: atPlusPlusCore(dup_code, doDup, token, isPost, expr);
0893: atFieldAssignCore(f, is_static, fi, is2w);
0894: }
0895:
0896: /* This method also returns a value in resultStatic.
0897: *
0898: * @param acceptLength true if array length is acceptable
0899: */
0900: protected CtField fieldAccess(ASTree expr, boolean acceptLength)
0901: throws CompileError {
0902: if (expr instanceof Member) {
0903: String name = ((Member) expr).get();
0904: CtField f = null;
0905: try {
0906: f = this Class.getField(name);
0907: } catch (NotFoundException e) {
0908: // EXPR might be part of a static member access?
0909: throw new NoFieldException(name, expr);
0910: }
0911:
0912: boolean is_static = Modifier.isStatic(f.getModifiers());
0913: if (!is_static)
0914: if (inStaticMethod)
0915: throw new CompileError(
0916: "not available in a static method: " + name);
0917: else
0918: bytecode.addAload(0); // this
0919:
0920: resultStatic = is_static;
0921: return f;
0922: } else if (expr instanceof Expr) {
0923: Expr e = (Expr) expr;
0924: int op = e.getOperator();
0925: if (op == MEMBER) {
0926: /* static member by # (extension by Javassist)
0927: * For example, if int.class is parsed, the resulting tree
0928: * is (# "java.lang.Integer" "TYPE").
0929: */
0930: CtField f = resolver.lookupField(((Symbol) e.oprand1())
0931: .get(), (Symbol) e.oprand2());
0932: resultStatic = true;
0933: return f;
0934: } else if (op == '.') {
0935: CtField f = null;
0936: try {
0937: e.oprand1().accept(this );
0938: /* Don't call lookupFieldByJvmName2().
0939: * The left operand of . is not a class name but
0940: * a normal expression.
0941: */
0942: if (exprType == CLASS && arrayDim == 0)
0943: f = resolver.lookupFieldByJvmName(className,
0944: (Symbol) e.oprand2());
0945: else if (acceptLength
0946: && arrayDim > 0
0947: && ((Symbol) e.oprand2()).get().equals(
0948: "length"))
0949: return null; // expr is an array length.
0950: else
0951: badLvalue();
0952:
0953: boolean is_static = Modifier.isStatic(f
0954: .getModifiers());
0955: if (is_static)
0956: bytecode.addOpcode(POP);
0957:
0958: resultStatic = is_static;
0959: return f;
0960: } catch (NoFieldException nfe) {
0961: if (nfe.getExpr() != e.oprand1())
0962: throw nfe;
0963:
0964: /* EXPR should be a static field.
0965: * If EXPR might be part of a qualified class name,
0966: * lookupFieldByJvmName2() throws NoFieldException.
0967: */
0968: Symbol fname = (Symbol) e.oprand2();
0969: String cname = nfe.getField();
0970: f = resolver.lookupFieldByJvmName2(cname, fname,
0971: expr);
0972: resolver.recordPackage(cname);
0973: resultStatic = true;
0974: return f;
0975: }
0976: } else
0977: badLvalue();
0978: } else
0979: badLvalue();
0980:
0981: resultStatic = false;
0982: return null; // never reach
0983: }
0984:
0985: private static void badLvalue() throws CompileError {
0986: throw new CompileError("bad l-value");
0987: }
0988:
0989: public CtClass[] makeParamList(MethodDecl md) throws CompileError {
0990: CtClass[] params;
0991: ASTList plist = md.getParams();
0992: if (plist == null)
0993: params = new CtClass[0];
0994: else {
0995: int i = 0;
0996: params = new CtClass[plist.length()];
0997: while (plist != null) {
0998: params[i++] = resolver.lookupClass((Declarator) plist
0999: .head());
1000: plist = plist.tail();
1001: }
1002: }
1003:
1004: return params;
1005: }
1006:
1007: public CtClass[] makeThrowsList(MethodDecl md) throws CompileError {
1008: CtClass[] clist;
1009: ASTList list = md.getThrows();
1010: if (list == null)
1011: return null;
1012: else {
1013: int i = 0;
1014: clist = new CtClass[list.length()];
1015: while (list != null) {
1016: clist[i++] = resolver.lookupClassByName((ASTList) list
1017: .head());
1018: list = list.tail();
1019: }
1020:
1021: return clist;
1022: }
1023: }
1024:
1025: /* Converts a class name into a JVM-internal representation.
1026: *
1027: * It may also expand a simple class name to java.lang.*.
1028: * For example, this converts Object into java/lang/Object.
1029: */
1030: protected String resolveClassName(ASTList name) throws CompileError {
1031: return resolver.resolveClassName(name);
1032: }
1033:
1034: /* Expands a simple class name to java.lang.*.
1035: * For example, this converts Object into java/lang/Object.
1036: */
1037: protected String resolveClassName(String jvmName)
1038: throws CompileError {
1039: return resolver.resolveJvmClassName(jvmName);
1040: }
1041: }
|