Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist.compiler;
     17 
     18 import javassist.CtClass;
     19 import javassist.CtField;
     20 import javassist.ClassPool;
     21 import javassist.Modifier;
     22 import javassist.NotFoundException;
     23 import javassist.compiler.ast.*;
     24 import javassist.bytecode.*;
     25 
     26 public class TypeChecker extends Visitor implements Opcode, TokenId {
     27     static final String javaLangObject = "java.lang.Object";
     28     static final String jvmJavaLangObject = "java/lang/Object";
     29     static final String jvmJavaLangString = "java/lang/String";
     30     static final String jvmJavaLangClass = "java/lang/Class";
     31 
     32     /* The following fields are used by atXXX() methods
     33      * for returning the type of the compiled expression.
     34      */
     35     protected int exprType;     // VOID, NULL, CLASS, BOOLEAN, INT, ...
     36     protected int arrayDim;
     37     protected String className; // JVM-internal representation
     38 
     39     protected MemberResolver resolver;
     40     protected CtClass   thisClass;
     41     protected MethodInfo thisMethod;
     42 
     43     public TypeChecker(CtClass cc, ClassPool cp) {
     44         resolver = new MemberResolver(cp);
     45         thisClass = cc;
     46         thisMethod = null;
     47     }
     48 
     49     /*
     50      * Converts an array of tuples of exprType, arrayDim, and className
     51      * into a String object.
     52      */
     53     protected static String argTypesToString(int[] types, int[] dims,
     54                                              String[] cnames) {
     55         StringBuffer sbuf = new StringBuffer();
     56         sbuf.append('(');
     57         int n = types.length;
     58         if (n > 0) {
     59             int i = 0;
     60             while (true) {
     61                 typeToString(sbuf, types[i], dims[i], cnames[i]);
     62                 if (++i < n)
     63                     sbuf.append(',');
     64                 else
     65                     break;
     66             }
     67         }
     68 
     69         sbuf.append(')');
     70         return sbuf.toString();
     71     }
     72 
     73     /*
     74      * Converts a tuple of exprType, arrayDim, and className
     75      * into a String object.
     76      */
     77     protected static StringBuffer typeToString(StringBuffer sbuf,
     78                                         int type, int dim, String cname) {
     79         String s;
     80         if (type == CLASS)
     81             s = MemberResolver.jvmToJavaName(cname);
     82         else if (type == NULL)
     83             s = "Object";
     84         else
     85             try {
     86                 s = MemberResolver.getTypeName(type);
     87             }
     88             catch (CompileError e) {
     89                 s = "?";
     90             }
     91 
     92         sbuf.append(s);
     93         while (dim-- > 0)
     94             sbuf.append("[]");
     95 
     96         return sbuf;
     97     }
     98 
     99     /**
    100      * Records the currently compiled method.
    101      */
    102     public void setThisMethod(MethodInfo m) {
    103         thisMethod = m;
    104     }
    105 
    106     protected static void fatal() throws CompileError {
    107         throw new CompileError("fatal");
    108     }
    109 
    110     /**
    111      * Returns the JVM-internal representation of this class name.
    112      */
    113     protected String getThisName() {
    114         return MemberResolver.javaToJvmName(thisClass.getName());
    115     }
    116 
    117     /**
    118      * Returns the JVM-internal representation of this super class name.
    119      */
    120     protected String getSuperName() throws CompileError {
    121         return MemberResolver.javaToJvmName(
    122                         MemberResolver.getSuperclass(thisClass).getName());
    123     }
    124 
    125     /* Converts a class name into a JVM-internal representation.
    126      *
    127      * It may also expand a simple class name to java.lang.*.
    128      * For example, this converts Object into java/lang/Object.
    129      */
    130     protected String resolveClassName(ASTList name) throws CompileError {
    131         return resolver.resolveClassName(name);
    132     }
    133 
    134     /* Expands a simple class name to java.lang.*.
    135      * For example, this converts Object into java/lang/Object.
    136      */
    137     protected String resolveClassName(String jvmName) throws CompileError {
    138         return resolver.resolveJvmClassName(jvmName);
    139     }
    140 
    141     public void atNewExpr(NewExpr expr) throws CompileError {
    142         if (expr.isArray())
    143             atNewArrayExpr(expr);
    144         else {
    145             CtClass clazz = resolver.lookupClassByName(expr.getClassName());
    146             String cname = clazz.getName();
    147             ASTList args = expr.getArguments();
    148             atMethodCallCore(clazz, MethodInfo.nameInit, args);
    149             exprType = CLASS;
    150             arrayDim = 0;
    151             className = MemberResolver.javaToJvmName(cname);
    152         }
    153     }
    154 
    155     public void atNewArrayExpr(NewExpr expr) throws CompileError {
    156         int type = expr.getArrayType();
    157         ASTList size = expr.getArraySize();
    158         ASTList classname = expr.getClassName();
    159         ASTree init = expr.getInitializer();
    160         if (init != null)
    161             init.accept(this);
    162 
    163         if (size.length() > 1)
    164             atMultiNewArray(type, classname, size);
    165         else {
    166             ASTree sizeExpr = size.head();
    167             if (sizeExpr != null)
    168                 sizeExpr.accept(this);
    169 
    170             exprType = type;
    171             arrayDim = 1;
    172             if (type == CLASS)
    173                 className = resolveClassName(classname);
    174             else
    175                 className = null;
    176         }
    177     }
    178 
    179     public void atArrayInit(ArrayInit init) throws CompileError {
    180         ASTList list = init;
    181         while (list != null) {
    182             ASTree h = list.head();
    183             list = list.tail();
    184             if (h != null)
    185                 h.accept(this);
    186         }
    187     }
    188 
    189     protected void atMultiNewArray(int type, ASTList classname, ASTList size)
    190         throws CompileError
    191     {
    192         int count, dim;
    193         dim = size.length();
    194         for (count = 0; size != null; size = size.tail()) {
    195             ASTree s = size.head();
    196             if (s == null)
    197                 break;          // int[][][] a = new int[3][4][];
    198 
    199             ++count;
    200             s.accept(this);
    201         }
    202 
    203         exprType = type;
    204         arrayDim = dim;
    205         if (type == CLASS)
    206             className = resolveClassName(classname);
    207         else
    208             className = null;
    209     }
    210 
    211     public void atAssignExpr(AssignExpr expr) throws CompileError {
    212         // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>=
    213         int op = expr.getOperator();
    214         ASTree left = expr.oprand1();
    215         ASTree right = expr.oprand2();
    216         if (left instanceof Variable)
    217             atVariableAssign(expr, op, (Variable)left,
    218                              ((Variable)left).getDeclarator(),
    219                              right);
    220         else {
    221             if (left instanceof Expr) {
    222                 Expr e = (Expr)left;
    223                 if (e.getOperator() == ARRAY) {
    224                     atArrayAssign(expr, op, (Expr)left, right);
    225                     return;
    226                 }
    227             }
    228 
    229             atFieldAssign(expr, op, left, right);
    230         }
    231     }
    232 
    233     /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
    234      *
    235      * expr and var can be null.
    236      */
    237     private void atVariableAssign(Expr expr, int op, Variable var,
    238                                   Declarator d, ASTree right)
    239         throws CompileError
    240     {
    241         int varType = d.getType();
    242         int varArray = d.getArrayDim();
    243         String varClass = d.getClassName();
    244 
    245         if (op != '=')
    246             atVariable(var);
    247 
    248         right.accept(this);
    249         exprType = varType;
    250         arrayDim = varArray;
    251         className = varClass;
    252     }
    253 
    254     private void atArrayAssign(Expr expr, int op, Expr array,
    255                         ASTree right) throws CompileError
    256     {
    257         atArrayRead(array.oprand1(), array.oprand2());
    258         int aType = exprType;
    259         int aDim = arrayDim;
    260         String cname = className;
    261         right.accept(this);
    262         exprType = aType;
    263         arrayDim = aDim;
    264         className = cname;
    265     }
    266 
    267     protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right)
    268         throws CompileError
    269     {
    270         CtField f = fieldAccess(left);
    271         atFieldRead(f);
    272         int fType = exprType;
    273         int fDim = arrayDim;
    274         String cname = className;
    275         right.accept(this);
    276         exprType = fType;
    277         arrayDim = fDim;
    278         className = cname;
    279     }
    280 
    281     public void atCondExpr(CondExpr expr) throws CompileError {
    282         booleanExpr(expr.condExpr());
    283         expr.thenExpr().accept(this);
    284         int type1 = exprType;
    285         int dim1 = arrayDim;
    286         String cname1 = className;
    287         expr.elseExpr().accept(this);
    288 
    289         if (dim1 == 0 && dim1 == arrayDim)
    290             if (CodeGen.rightIsStrong(type1, exprType))
    291                 expr.setThen(new CastExpr(exprType, 0, expr.thenExpr()));
    292             else if (CodeGen.rightIsStrong(exprType, type1)) {
    293                 expr.setElse(new CastExpr(type1, 0, expr.elseExpr()));
    294                 exprType = type1;
    295             }
    296     }
    297 
    298     /*
    299      * If atBinExpr() substitutes a new expression for the original
    300      * binary-operator expression, it changes the operator name to '+'
    301      * (if the original is not '+') and sets the new expression to the
    302      * left-hand-side expression and null to the right-hand-side expression.
    303      */
    304     public void atBinExpr(BinExpr expr) throws CompileError {
    305         int token = expr.getOperator();
    306         int k = CodeGen.lookupBinOp(token);
    307         if (k >= 0) {
    308             /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
    309              */
    310             if (token == '+') {
    311                 Expr e = atPlusExpr(expr);
    312                 if (e != null) {
    313                     /* String concatenation has been translated into
    314                      * an expression using StringBuffer.
    315                      */
    316                     e = CallExpr.makeCall(Expr.make('.', e,
    317                                             new Member("toString")), null);
    318                     expr.setOprand1(e);
    319                     expr.setOprand2(null);    // <---- look at this!
    320                     className = jvmJavaLangString;
    321                 }
    322             }
    323             else {
    324                 ASTree left = expr.oprand1();
    325                 ASTree right = expr.oprand2();
    326                 left.accept(this);
    327                 int type1 = exprType;
    328                 right.accept(this);
    329                 if (!isConstant(expr, token, left, right))
    330                     computeBinExprType(expr, token, type1);
    331             }
    332         }
    333         else {
    334             /* equation: &&, ||, ==, !=, <=, >=, <, >
    335             */
    336             booleanExpr(expr);
    337         }
    338     }
    339 
    340     /* EXPR must be a + expression.
    341      * atPlusExpr() returns non-null if the given expression is string
    342      * concatenation.  The returned value is "new StringBuffer().append..".
    343      */
    344     private Expr atPlusExpr(BinExpr expr) throws CompileError {
    345         ASTree left = expr.oprand1();
    346         ASTree right = expr.oprand2();
    347         if (right == null) {
    348             // this expression has been already type-checked.
    349             // see atBinExpr() above.
    350             left.accept(this);
    351             return null;
    352         }
    353 
    354         if (isPlusExpr(left)) {
    355             Expr newExpr = atPlusExpr((BinExpr)left);
    356             if (newExpr != null) {
    357                 right.accept(this);
    358                 exprType = CLASS;
    359                 arrayDim = 0;
    360                 className = "java/lang/StringBuffer";
    361                 return makeAppendCall(newExpr, right);
    362             }
    363         }
    364         else
    365             left.accept(this);
    366 
    367         int type1 = exprType;
    368         int dim1 = arrayDim;
    369         String cname = className;
    370         right.accept(this);
    371 
    372         if (isConstant(expr, '+', left, right))
    373             return null;
    374 
    375         if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname))
    376             || (exprType == CLASS && arrayDim == 0
    377                 && jvmJavaLangString.equals(className))) {
    378             ASTList sbufClass = ASTList.make(new Symbol("java"),
    379                             new Symbol("lang"), new Symbol("StringBuffer"));
    380             ASTree e = new NewExpr(sbufClass, null);
    381             exprType = CLASS;
    382             arrayDim = 0;
    383             className = "java/lang/StringBuffer";
    384             return makeAppendCall(makeAppendCall(e, left), right);
    385         }
    386         else {
    387             computeBinExprType(expr, '+', type1);
    388             return null;
    389         }
    390     }
    391 
    392     private boolean isConstant(BinExpr expr, int op, ASTree left,
    393                                ASTree right) throws CompileError
    394     {
    395         left = stripPlusExpr(left);
    396         right = stripPlusExpr(right);
    397         ASTree newExpr = null;
    398         if (left instanceof StringL && right instanceof StringL && op == '+')
    399             newExpr = new StringL(((StringL)left).get()
    400                                   + ((StringL)right).get());
    401         else if (left instanceof IntConst)
    402             newExpr = ((IntConst)left).compute(op, right);
    403         else if (left instanceof DoubleConst)
    404             newExpr = ((DoubleConst)left).compute(op, right);
    405 
    406         if (newExpr == null)
    407             return false;       // not a constant expression
    408         else {
    409             expr.setOperator('+');
    410             expr.setOprand1(newExpr);
    411             expr.setOprand2(null);
    412             newExpr.accept(this);   // for setting exprType, arrayDim, ...
    413             return true;
    414         }
    415     }
    416 
    417     /* CodeGen.atSwitchStmnt() also calls stripPlusExpr().
    418      */
    419     static ASTree stripPlusExpr(ASTree expr) {
    420         if (expr instanceof BinExpr) {
    421             BinExpr e = (BinExpr)expr;
    422             if (e.getOperator() == '+' && e.oprand2() == null)
    423                 return e.getLeft();
    424         }
    425         else if (expr instanceof Expr) {    // note: BinExpr extends Expr.
    426             Expr e = (Expr)expr;
    427             int op = e.getOperator();
    428             if (op == MEMBER) {
    429                 ASTree cexpr = getConstantFieldValue((Member)e.oprand2());
    430                 if (cexpr != null)
    431                     return cexpr;
    432             }
    433             else if (op == '+' && e.getRight() == null)
    434                 return e.getLeft();
    435         }
    436         else if (expr instanceof Member) {
    437             ASTree cexpr = getConstantFieldValue((Member)expr);
    438             if (cexpr != null)
    439                 return cexpr;
    440         }
    441 
    442         return expr;
    443     }
    444 
    445     /**
    446      * If MEM is a static final field, this method returns a constant
    447      * expression representing the value of that field.
    448      */
    449     private static ASTree getConstantFieldValue(Member mem) {
    450         return getConstantFieldValue(mem.getField());
    451     }
    452 
    453     public static ASTree getConstantFieldValue(CtField f) {
    454         if (f == null)
    455             return null;
    456 
    457         Object value = f.getConstantValue();
    458         if (value == null)
    459             return null;
    460 
    461         if (value instanceof String)
    462             return new StringL((String)value);
    463         else if (value instanceof Double || value instanceof Float) {
    464             int token = (value instanceof Double)
    465                         ? DoubleConstant : FloatConstant;
    466             return new DoubleConst(((Number)value).doubleValue(), token);
    467         }
    468         else if (value instanceof Number) {
    469             int token = (value instanceof Long) ? LongConstant : IntConstant;
    470             return new IntConst(((Number)value).longValue(), token);
    471         }
    472         else if (value instanceof Boolean)
    473             return new Keyword(((Boolean)value).booleanValue()
    474                                ? TokenId.TRUE : TokenId.FALSE);
    475         else
    476             return null;
    477     }
    478 
    479     private static boolean isPlusExpr(ASTree expr) {
    480         if (expr instanceof BinExpr) {
    481             BinExpr bexpr = (BinExpr)expr;
    482             int token = bexpr.getOperator();
    483             return token == '+';
    484         }
    485 
    486         return false;
    487     }
    488 
    489     private static Expr makeAppendCall(ASTree target, ASTree arg) {
    490         return CallExpr.makeCall(Expr.make('.', target, new Member("append")),
    491                                  new ASTList(arg));
    492     }
    493 
    494     private void computeBinExprType(BinExpr expr, int token, int type1)
    495         throws CompileError
    496     {
    497         // arrayDim should be 0.
    498         int type2 = exprType;
    499         if (token == LSHIFT || token == RSHIFT || token == ARSHIFT)
    500             exprType = type1;
    501         else
    502             insertCast(expr, type1, type2);
    503 
    504         if (CodeGen.isP_INT(exprType))
    505             exprType = INT;         // type1 may be BYTE, ...
    506     }
    507 
    508     private void booleanExpr(ASTree expr)
    509         throws CompileError
    510     {
    511         int op = CodeGen.getCompOperator(expr);
    512         if (op == EQ) {         // ==, !=, ...
    513             BinExpr bexpr = (BinExpr)expr;
    514             bexpr.oprand1().accept(this);
    515             int type1 = exprType;
    516             int dim1 = arrayDim;
    517             bexpr.oprand2().accept(this);
    518             if (dim1 == 0 && arrayDim == 0)
    519                 insertCast(bexpr, type1, exprType);
    520         }
    521         else if (op == '!')
    522             ((Expr)expr).oprand1().accept(this);
    523         else if (op == ANDAND || op == OROR) {
    524             BinExpr bexpr = (BinExpr)expr;
    525             bexpr.oprand1().accept(this);
    526             bexpr.oprand2().accept(this);
    527         }
    528         else                // others
    529             expr.accept(this);
    530 
    531         exprType = BOOLEAN;
    532         arrayDim = 0;
    533     }
    534 
    535     private void insertCast(BinExpr expr, int type1, int type2)
    536         throws CompileError
    537     {
    538         if (CodeGen.rightIsStrong(type1, type2))
    539             expr.setLeft(new CastExpr(type2, 0, expr.oprand1()));
    540         else
    541             exprType = type1;
    542     }
    543 
    544     public void atCastExpr(CastExpr expr) throws CompileError {
    545         String cname = resolveClassName(expr.getClassName());
    546         expr.getOprand().accept(this);
    547         exprType = expr.getType();
    548         arrayDim = expr.getArrayDim();
    549         className = cname;
    550     }
    551 
    552     public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
    553         expr.getOprand().accept(this);
    554         exprType = BOOLEAN;
    555         arrayDim = 0;
    556     }
    557 
    558     public void atExpr(Expr expr) throws CompileError {
    559         // array access, member access,
    560         // (unary) +, (unary) -, ++, --, !, ~
    561 
    562         int token = expr.getOperator();
    563         ASTree oprand = expr.oprand1();
    564         if (token == '.') {
    565             String member = ((Symbol)expr.oprand2()).get();
    566             if (member.equals("length"))
    567                 atArrayLength(expr);
    568             else if (member.equals("class"))
    569                 atClassObject(expr);  // .class
    570             else
    571                 atFieldRead(expr);
    572         }
    573         else if (token == MEMBER) {     // field read
    574             String member = ((Symbol)expr.oprand2()).get();
    575             if (member.equals("class"))
    576                 atClassObject(expr);  // .class
    577             else
    578                 atFieldRead(expr);
    579         }
    580         else if (token == ARRAY)
    581             atArrayRead(oprand, expr.oprand2());
    582         else if (token == PLUSPLUS || token == MINUSMINUS)
    583             atPlusPlus(token, oprand, expr);
    584         else if (token == '!')
    585             booleanExpr(expr);
    586         else if (token == CALL)              // method call
    587             fatal();
    588         else {
    589             oprand.accept(this);
    590             if (!isConstant(expr, token, oprand))
    591                 if (token == '-' || token == '~')
    592                     if (CodeGen.isP_INT(exprType))
    593                         exprType = INT;         // type may be BYTE, ...
    594         }
    595     }
    596 
    597     private boolean isConstant(Expr expr, int op, ASTree oprand) {
    598         oprand = stripPlusExpr(oprand);
    599         if (oprand instanceof IntConst) {
    600             IntConst c = (IntConst)oprand;
    601             long v = c.get();
    602             if (op == '-')
    603                 v = -v;
    604             else if (op == '~')
    605                 v = ~v;
    606             else
    607                 return false;
    608 
    609             c.set(v);
    610         }
    611         else if (oprand instanceof DoubleConst) {
    612             DoubleConst c = (DoubleConst)oprand;
    613             if (op == '-')
    614                 c.set(-c.get());
    615             else
    616                 return false;
    617         }
    618         else
    619             return false;
    620 
    621         expr.setOperator('+');
    622         return true;
    623     }
    624 
    625     public void atCallExpr(CallExpr expr) throws CompileError {
    626         String mname = null;
    627         CtClass targetClass = null;
    628         ASTree method = expr.oprand1();
    629         ASTList args = (ASTList)expr.oprand2();
    630 
    631         if (method instanceof Member) {
    632             mname = ((Member)method).get();
    633             targetClass = thisClass;
    634         }
    635         else if (method instanceof Keyword) {   // constructor
    636             mname = MethodInfo.nameInit;        // <init>
    637             if (((Keyword)method).get() == SUPER)
    638                 targetClass = MemberResolver.getSuperclass(thisClass);
    639             else
    640                 targetClass = thisClass;
    641         }
    642         else if (method instanceof Expr) {
    643             Expr e = (Expr)method;
    644             mname = ((Symbol)e.oprand2()).get();
    645             int op = e.getOperator();
    646             if (op == MEMBER)                // static method
    647                 targetClass
    648                         = resolver.lookupClass(((Symbol)e.oprand1()).get(),
    649                                                false);
    650             else if (op == '.') {
    651                 ASTree target = e.oprand1();
    652                 try {
    653                     target.accept(this);
    654                 }
    655                 catch (NoFieldException nfe) {
    656                     if (nfe.getExpr() != target)
    657                         throw nfe;
    658 
    659                     // it should be a static method.
    660                     exprType = CLASS;
    661                     arrayDim = 0;
    662                     className = nfe.getField(); // JVM-internal
    663                     e.setOperator(MEMBER);
    664                     e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(
    665                                                             className)));
    666                 }
    667 
    668                 if (arrayDim > 0)
    669                     targetClass = resolver.lookupClass(javaLangObject, true);
    670                 else if (exprType == CLASS /* && arrayDim == 0 */)
    671                     targetClass = resolver.lookupClassByJvmName(className);
    672                 else
    673                     badMethod();
    674             }
    675             else
    676                 badMethod();
    677         }
    678         else
    679             fatal();
    680 
    681         MemberResolver.Method minfo
    682                 = atMethodCallCore(targetClass, mname, args);
    683         expr.setMethod(minfo);
    684     }
    685 
    686     private static void badMethod() throws CompileError {
    687         throw new CompileError("bad method");
    688     }
    689 
    690     /**
    691      * @return  a pair of the class declaring the invoked method
    692      *          and the MethodInfo of that method.  Never null.
    693      */
    694     public MemberResolver.Method atMethodCallCore(CtClass targetClass,
    695                                                   String mname, ASTList args)
    696         throws CompileError
    697     {
    698         int nargs = getMethodArgsLength(args);
    699         int[] types = new int[nargs];
    700         int[] dims = new int[nargs];
    701         String[] cnames = new String[nargs];
    702         atMethodArgs(args, types, dims, cnames);
    703 
    704         MemberResolver.Method found
    705             = resolver.lookupMethod(targetClass, thisClass, thisMethod,
    706                                     mname, types, dims, cnames);
    707         if (found == null) {
    708             String clazz = targetClass.getName();
    709             String signature = argTypesToString(types, dims, cnames);
    710             String msg;
    711             if (mname.equals(MethodInfo.nameInit))
    712                 msg = "cannot find constructor " + clazz + signature;
    713             else
    714                 msg = mname + signature +  " not found in " + clazz;
    715 
    716             throw new CompileError(msg);
    717         }
    718 
    719         String desc = found.info.getDescriptor();
    720         setReturnType(desc);
    721         return found;
    722     }
    723 
    724     public int getMethodArgsLength(ASTList args) {
    725         return ASTList.length(args);
    726     }
    727 
    728     public void atMethodArgs(ASTList args, int[] types, int[] dims,
    729                              String[] cnames) throws CompileError {
    730         int i = 0;
    731         while (args != null) {
    732             ASTree a = args.head();
    733             a.accept(this);
    734             types[i] = exprType;
    735             dims[i] = arrayDim;
    736             cnames[i] = className;
    737             ++i;
    738             args = args.tail();
    739         }
    740     }
    741 
    742     void setReturnType(String desc) throws CompileError {
    743         int i = desc.indexOf(')');
    744         if (i < 0)
    745             badMethod();
    746 
    747         char c = desc.charAt(++i);
    748         int dim = 0;
    749         while (c == '[') {
    750             ++dim;
    751             c = desc.charAt(++i);
    752         }
    753 
    754         arrayDim = dim;
    755         if (c == 'L') {
    756             int j = desc.indexOf(';', i + 1);
    757             if (j < 0)
    758                 badMethod();
    759 
    760             exprType = CLASS;
    761             className = desc.substring(i + 1, j);
    762         }
    763         else {
    764             exprType = MemberResolver.descToType(c);
    765             className = null;
    766         }
    767     }
    768 
    769     private void atFieldRead(ASTree expr) throws CompileError {
    770         atFieldRead(fieldAccess(expr));
    771     }
    772 
    773     private void atFieldRead(CtField f) throws CompileError {
    774         FieldInfo finfo = f.getFieldInfo2();
    775         String type = finfo.getDescriptor();
    776 
    777         int i = 0;
    778         int dim = 0;
    779         char c = type.charAt(i);
    780         while (c == '[') {
    781             ++dim;
    782             c = type.charAt(++i);
    783         }
    784 
    785         arrayDim = dim;
    786         exprType = MemberResolver.descToType(c);
    787 
    788         if (c == 'L')
    789             className = type.substring(i + 1, type.indexOf(';', i + 1));
    790         else
    791             className = null;
    792     }
    793 
    794     /* if EXPR is to access a static field, fieldAccess() translates EXPR
    795      * into an expression using '#' (MEMBER).  For example, it translates
    796      * java.lang.Integer.TYPE into java.lang.Integer#TYPE.  This translation
    797      * speeds up type resolution by MemberCodeGen.
    798      */
    799     protected CtField fieldAccess(ASTree expr) throws CompileError {
    800         if (expr instanceof Member) {
    801             Member mem = (Member)expr;
    802             String name = mem.get();
    803             try {
    804                 CtField f = thisClass.getField(name);
    805                 if (Modifier.isStatic(f.getModifiers()))
    806                     mem.setField(f);
    807 
    808                 return f;
    809             }
    810             catch (NotFoundException e) {
    811                 // EXPR might be part of a static member access?
    812                 throw new NoFieldException(name, expr);
    813             }
    814         }
    815         else if (expr instanceof Expr) {
    816             Expr e = (Expr)expr;
    817             int op = e.getOperator();
    818             if (op == MEMBER) {
    819                 Member mem = (Member)e.oprand2();
    820                 CtField f
    821                     = resolver.lookupField(((Symbol)e.oprand1()).get(), mem);
    822                 mem.setField(f);
    823                 return f;
    824             }
    825             else if (op == '.') {
    826                 try {
    827                     e.oprand1().accept(this);
    828                 }
    829                 catch (NoFieldException nfe) {
    830                     if (nfe.getExpr() != e.oprand1())
    831                         throw nfe;
    832 
    833                     /* EXPR should be a static field.
    834                      * If EXPR might be part of a qualified class name,
    835                      * lookupFieldByJvmName2() throws NoFieldException.
    836                      */
    837                     return fieldAccess2(e, nfe.getField());
    838                 }
    839 
    840                 CompileError err = null;
    841                 try {
    842                     if (exprType == CLASS && arrayDim == 0)
    843                         return resolver.lookupFieldByJvmName(className,
    844                                                     (Symbol)e.oprand2());
    845                 }
    846                 catch (CompileError ce) {
    847                     err = ce;
    848                 }
    849 
    850                 /* If a filed name is the same name as a package's,
    851                  * a static member of a class in that package is not
    852                  * visible.  For example,
    853                  *
    854                  * class Foo {
    855                  *   int javassist;
    856                  * }
    857                  *
    858                  * It is impossible to add the following method:
    859                  *
    860                  * String m() { return javassist.CtClass.intType.toString(); }
    861                  *
    862                  * because javassist is a field name.  However, this is
    863                  * often inconvenient, this compiler allows it.  The following
    864                  * code is for that.
    865                  */
    866                 ASTree oprnd1 = e.oprand1();
    867                 if (oprnd1 instanceof Symbol)
    868                     return fieldAccess2(e, ((Symbol)oprnd1).get());
    869 
    870                 if (err != null)
    871                     throw err;
    872             }
    873         }
    874 
    875         throw new CompileError("bad filed access");
    876     }
    877 
    878     private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError {
    879         Member fname = (Member)e.oprand2();
    880         CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e);
    881         e.setOperator(MEMBER);
    882         e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName)));
    883         fname.setField(f);
    884         return f;
    885     }
    886 
    887     public void atClassObject(Expr expr) throws CompileError {
    888         exprType = CLASS;
    889         arrayDim = 0;
    890         className =jvmJavaLangClass;
    891     }
    892 
    893     public void atArrayLength(Expr expr) throws CompileError {
    894         expr.oprand1().accept(this);
    895         exprType = INT;
    896         arrayDim = 0;
    897     }
    898 
    899     public void atArrayRead(ASTree array, ASTree index)
    900         throws CompileError
    901     {
    902         array.accept(this);
    903         int type = exprType;
    904         int dim = arrayDim;
    905         String cname = className;
    906         index.accept(this);
    907         exprType = type;
    908         arrayDim = dim - 1;
    909         className = cname;
    910     }
    911 
    912     private void atPlusPlus(int token, ASTree oprand, Expr expr)
    913         throws CompileError
    914     {
    915         boolean isPost = oprand == null;        // ++i or i++?
    916         if (isPost)
    917             oprand = expr.oprand2();
    918 
    919         if (oprand instanceof Variable) {
    920             Declarator d = ((Variable)oprand).getDeclarator();
    921             exprType = d.getType();
    922             arrayDim = d.getArrayDim();
    923         }
    924         else {
    925             if (oprand instanceof Expr) {
    926                 Expr e = (Expr)oprand;
    927                 if (e.getOperator() == ARRAY) {
    928                     atArrayRead(e.oprand1(), e.oprand2());
    929                     // arrayDim should be 0.
    930                     int t = exprType;
    931                     if (t == INT || t == BYTE || t == CHAR || t == SHORT)
    932                         exprType = INT;
    933 
    934                     return;
    935                 }
    936             }
    937 
    938             atFieldPlusPlus(oprand);
    939         }
    940     }
    941 
    942     protected void atFieldPlusPlus(ASTree oprand) throws CompileError
    943     {
    944         CtField f = fieldAccess(oprand);
    945         atFieldRead(f);
    946         int t = exprType;
    947         if (t == INT || t == BYTE || t == CHAR || t == SHORT)
    948             exprType = INT;
    949     }
    950 
    951     public void atMember(Member mem) throws CompileError {
    952         atFieldRead(mem);
    953     }
    954 
    955     public void atVariable(Variable v) throws CompileError {
    956         Declarator d = v.getDeclarator();
    957         exprType = d.getType();
    958         arrayDim = d.getArrayDim();
    959         className = d.getClassName();
    960     }
    961 
    962     public void atKeyword(Keyword k) throws CompileError {
    963         arrayDim = 0;
    964         int token = k.get();
    965         switch (token) {
    966         case TRUE :
    967         case FALSE :
    968             exprType = BOOLEAN;
    969             break;
    970         case NULL :
    971             exprType = NULL;
    972             break;
    973         case THIS :
    974         case SUPER :
    975             exprType = CLASS;
    976             if (token == THIS)
    977                 className = getThisName();
    978             else
    979                 className = getSuperName();
    980             break;
    981         default :
    982             fatal();
    983         }
    984     }
    985 
    986     public void atStringL(StringL s) throws CompileError {
    987         exprType = CLASS;
    988         arrayDim = 0;
    989         className = jvmJavaLangString;
    990     }
    991 
    992     public void atIntConst(IntConst i) throws CompileError {
    993         arrayDim = 0;
    994         int type = i.getType();
    995         if (type == IntConstant || type == CharConstant)
    996             exprType = (type == IntConstant ? INT : CHAR);
    997         else
    998             exprType = LONG;
    999     }
   1000 
   1001     public void atDoubleConst(DoubleConst d) throws CompileError {
   1002         arrayDim = 0;
   1003         if (d.getType() == DoubleConstant)
   1004             exprType = DOUBLE;
   1005         else
   1006             exprType = FLOAT;
   1007     }
   1008 }
   1009