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