• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TypeChecker(CtClass cc, ClassPool cp)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      */
argTypesToString(int[] types, int[] dims, String[] cnames)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      */
typeToString(StringBuffer sbuf, int type, int dim, String cname)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      */
setThisMethod(MethodInfo m)102     public void setThisMethod(MethodInfo m) {
103         thisMethod = m;
104     }
105 
fatal()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      */
getThisName()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      */
getSuperName()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      */
resolveClassName(ASTList name)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      */
resolveClassName(String jvmName)137     protected String resolveClassName(String jvmName) throws CompileError {
138         return resolver.resolveJvmClassName(jvmName);
139     }
140 
atNewExpr(NewExpr expr)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 
atNewArrayExpr(NewExpr expr)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 
atArrayInit(ArrayInit init)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 
atMultiNewArray(int type, ASTList classname, ASTList size)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 
atAssignExpr(AssignExpr expr)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      */
atVariableAssign(Expr expr, int op, Variable var, Declarator d, ASTree right)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 
atArrayAssign(Expr expr, int op, Expr array, ASTree right)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 
atFieldAssign(Expr expr, int op, ASTree left, ASTree right)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 
atCondExpr(CondExpr expr)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      */
atBinExpr(BinExpr expr)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      */
atPlusExpr(BinExpr expr)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 
isConstant(BinExpr expr, int op, ASTree left, ASTree right)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      */
stripPlusExpr(ASTree expr)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      */
getConstantFieldValue(Member mem)449     private static ASTree getConstantFieldValue(Member mem) {
450         return getConstantFieldValue(mem.getField());
451     }
452 
getConstantFieldValue(CtField f)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 
isPlusExpr(ASTree expr)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 
makeAppendCall(ASTree target, ASTree arg)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 
computeBinExprType(BinExpr expr, int token, int type1)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 
booleanExpr(ASTree expr)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 
insertCast(BinExpr expr, int type1, int type2)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 
atCastExpr(CastExpr expr)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 
atInstanceOfExpr(InstanceOfExpr expr)552     public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
553         expr.getOprand().accept(this);
554         exprType = BOOLEAN;
555         arrayDim = 0;
556     }
557 
atExpr(Expr expr)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 
isConstant(Expr expr, int op, ASTree oprand)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 
atCallExpr(CallExpr expr)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 
badMethod()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      */
atMethodCallCore(CtClass targetClass, String mname, ASTList args)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 
getMethodArgsLength(ASTList args)724     public int getMethodArgsLength(ASTList args) {
725         return ASTList.length(args);
726     }
727 
atMethodArgs(ASTList args, int[] types, int[] dims, String[] cnames)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 
setReturnType(String desc)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 
atFieldRead(ASTree expr)769     private void atFieldRead(ASTree expr) throws CompileError {
770         atFieldRead(fieldAccess(expr));
771     }
772 
atFieldRead(CtField f)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      */
fieldAccess(ASTree expr)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 
fieldAccess2(Expr e, String jvmClassName)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 
atClassObject(Expr expr)887     public void atClassObject(Expr expr) throws CompileError {
888         exprType = CLASS;
889         arrayDim = 0;
890         className =jvmJavaLangClass;
891     }
892 
atArrayLength(Expr expr)893     public void atArrayLength(Expr expr) throws CompileError {
894         expr.oprand1().accept(this);
895         exprType = INT;
896         arrayDim = 0;
897     }
898 
atArrayRead(ASTree array, ASTree index)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 
atPlusPlus(int token, ASTree oprand, Expr expr)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 
atFieldPlusPlus(ASTree oprand)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 
atMember(Member mem)951     public void atMember(Member mem) throws CompileError {
952         atFieldRead(mem);
953     }
954 
atVariable(Variable v)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 
atKeyword(Keyword k)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 
atStringL(StringL s)986     public void atStringL(StringL s) throws CompileError {
987         exprType = CLASS;
988         arrayDim = 0;
989         className = jvmJavaLangString;
990     }
991 
atIntConst(IntConst i)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 
atDoubleConst(DoubleConst d)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