• 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 java.util.List;
19 import java.util.Iterator;
20 import javassist.*;
21 import javassist.bytecode.*;
22 import javassist.compiler.ast.*;
23 
24 /* Code generator methods depending on javassist.* classes.
25  */
26 public class MemberResolver implements TokenId {
27     private ClassPool classPool;
28 
MemberResolver(ClassPool cp)29     public MemberResolver(ClassPool cp) {
30         classPool = cp;
31     }
32 
getClassPool()33     public ClassPool getClassPool() { return classPool; }
34 
fatal()35     private static void fatal() throws CompileError {
36         throw new CompileError("fatal");
37     }
38 
39     /**
40      * @param jvmClassName      a class name.  Not a package name.
41      */
recordPackage(String jvmClassName)42     public void recordPackage(String jvmClassName) {
43         String classname = jvmToJavaName(jvmClassName);
44         for (;;) {
45             int i = classname.lastIndexOf('.');
46             if (i > 0) {
47                 classname = classname.substring(0, i);
48                 classPool.recordInvalidClassName(classname);
49             }
50             else
51                 break;
52         }
53     }
54 
55     public static class Method {
56         public CtClass declaring;
57         public MethodInfo info;
58         public int notmatch;
59 
Method(CtClass c, MethodInfo i, int n)60         public Method(CtClass c, MethodInfo i, int n) {
61             declaring = c;
62             info = i;
63             notmatch = n;
64         }
65 
66         /**
67          * Returns true if the invoked method is static.
68          */
isStatic()69         public boolean isStatic() {
70             int acc = info.getAccessFlags();
71             return (acc & AccessFlag.STATIC) != 0;
72         }
73     }
74 
lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current, String methodName, int[] argTypes, int[] argDims, String[] argClassNames)75     public Method lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current,
76                                 String methodName,
77                                 int[] argTypes, int[] argDims,
78                                 String[] argClassNames)
79         throws CompileError
80     {
81         Method maybe = null;
82         // to enable the creation of a recursively called method
83         if (current != null && clazz == currentClass)
84             if (current.getName().equals(methodName)) {
85                 int res = compareSignature(current.getDescriptor(),
86                                            argTypes, argDims, argClassNames);
87                 if (res != NO) {
88                     Method r = new Method(clazz, current, res);
89                     if (res == YES)
90                         return r;
91                     else
92                         maybe = r;
93                 }
94             }
95 
96         Method m = lookupMethod(clazz, methodName, argTypes, argDims,
97                                 argClassNames, maybe != null);
98         if (m != null)
99             return m;
100         else
101             return maybe;
102     }
103 
lookupMethod(CtClass clazz, String methodName, int[] argTypes, int[] argDims, String[] argClassNames, boolean onlyExact)104     private Method lookupMethod(CtClass clazz, String methodName,
105                                int[] argTypes, int[] argDims,
106                                String[] argClassNames, boolean onlyExact)
107         throws CompileError
108     {
109         Method maybe = null;
110         ClassFile cf = clazz.getClassFile2();
111         // If the class is an array type, the class file is null.
112         // If so, search the super class java.lang.Object for clone() etc.
113         if (cf != null) {
114             List list = cf.getMethods();
115             int n = list.size();
116             for (int i = 0; i < n; ++i) {
117                 MethodInfo minfo = (MethodInfo)list.get(i);
118                 if (minfo.getName().equals(methodName)) {
119                     int res = compareSignature(minfo.getDescriptor(),
120                                            argTypes, argDims, argClassNames);
121                     if (res != NO) {
122                         Method r = new Method(clazz, minfo, res);
123                         if (res == YES)
124                             return r;
125                         else if (maybe == null || maybe.notmatch > res)
126                             maybe = r;
127                     }
128                 }
129             }
130         }
131 
132         if (onlyExact)
133             maybe = null;
134         else
135             onlyExact = maybe != null;
136 
137         int mod = clazz.getModifiers();
138         boolean isIntf = Modifier.isInterface(mod);
139         try {
140             // skip searching java.lang.Object if clazz is an interface type.
141             if (!isIntf) {
142                 CtClass pclazz = clazz.getSuperclass();
143                 if (pclazz != null) {
144                     Method r = lookupMethod(pclazz, methodName, argTypes,
145                                             argDims, argClassNames, onlyExact);
146                     if (r != null)
147                         return r;
148                 }
149             }
150         }
151         catch (NotFoundException e) {}
152 
153         if (isIntf || Modifier.isAbstract(mod))
154             try {
155                 CtClass[] ifs = clazz.getInterfaces();
156                 int size = ifs.length;
157                 for (int i = 0; i < size; ++i) {
158                     Method r = lookupMethod(ifs[i], methodName,
159                                             argTypes, argDims, argClassNames,
160                                             onlyExact);
161                     if (r != null)
162                         return r;
163                 }
164 
165                 if (isIntf) {
166                     // finally search java.lang.Object.
167                     CtClass pclazz = clazz.getSuperclass();
168                     if (pclazz != null) {
169                         Method r = lookupMethod(pclazz, methodName, argTypes,
170                                                 argDims, argClassNames, onlyExact);
171                         if (r != null)
172                             return r;
173                     }
174                 }
175             }
176             catch (NotFoundException e) {}
177 
178         return maybe;
179     }
180 
181     private static final int YES = 0;
182     private static final int NO = -1;
183 
184     /*
185      * Returns YES if actual parameter types matches the given signature.
186      *
187      * argTypes, argDims, and argClassNames represent actual parameters.
188      *
189      * This method does not correctly implement the Java method dispatch
190      * algorithm.
191      *
192      * If some of the parameter types exactly match but others are subtypes of
193      * the corresponding type in the signature, this method returns the number
194      * of parameter types that do not exactly match.
195      */
compareSignature(String desc, int[] argTypes, int[] argDims, String[] argClassNames)196     private int compareSignature(String desc, int[] argTypes,
197                                  int[] argDims, String[] argClassNames)
198         throws CompileError
199     {
200         int result = YES;
201         int i = 1;
202         int nArgs = argTypes.length;
203         if (nArgs != Descriptor.numOfParameters(desc))
204             return NO;
205 
206         int len = desc.length();
207         for (int n = 0; i < len; ++n) {
208             char c = desc.charAt(i++);
209             if (c == ')')
210                 return (n == nArgs ? result : NO);
211             else if (n >= nArgs)
212                 return NO;
213 
214             int dim = 0;
215             while (c == '[') {
216                 ++dim;
217                 c = desc.charAt(i++);
218             }
219 
220             if (argTypes[n] == NULL) {
221                 if (dim == 0 && c != 'L')
222                     return NO;
223 
224                 if (c == 'L')
225                     i = desc.indexOf(';', i) + 1;
226             }
227             else if (argDims[n] != dim) {
228                 if (!(dim == 0 && c == 'L'
229                       && desc.startsWith("java/lang/Object;", i)))
230                     return NO;
231 
232                 // if the thread reaches here, c must be 'L'.
233                 i = desc.indexOf(';', i) + 1;
234                 result++;
235                 if (i <= 0)
236                     return NO;  // invalid descriptor?
237             }
238             else if (c == 'L') {        // not compare
239                 int j = desc.indexOf(';', i);
240                 if (j < 0 || argTypes[n] != CLASS)
241                     return NO;
242 
243                 String cname = desc.substring(i, j);
244                 if (!cname.equals(argClassNames[n])) {
245                     CtClass clazz = lookupClassByJvmName(argClassNames[n]);
246                     try {
247                         if (clazz.subtypeOf(lookupClassByJvmName(cname)))
248                             result++;
249                         else
250                             return NO;
251                     }
252                     catch (NotFoundException e) {
253                         result++; // should be NO?
254                     }
255                 }
256 
257                 i = j + 1;
258             }
259             else {
260                 int t = descToType(c);
261                 int at = argTypes[n];
262                 if (t != at)
263                     if (t == INT
264                         && (at == SHORT || at == BYTE || at == CHAR))
265                         result++;
266                     else
267                         return NO;
268             }
269         }
270 
271         return NO;
272     }
273 
274     /**
275      * Only used by fieldAccess() in MemberCodeGen and TypeChecker.
276      *
277      * @param jvmClassName  a JVM class name.  e.g. java/lang/String
278      */
lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, ASTree expr)279     public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym,
280                                          ASTree expr) throws NoFieldException
281     {
282         String field = fieldSym.get();
283         CtClass cc = null;
284         try {
285             cc = lookupClass(jvmToJavaName(jvmClassName), true);
286         }
287         catch (CompileError e) {
288             // EXPR might be part of a qualified class name.
289             throw new NoFieldException(jvmClassName + "/" + field, expr);
290         }
291 
292         try {
293             return cc.getField(field);
294         }
295         catch (NotFoundException e) {
296             // maybe an inner class.
297             jvmClassName = javaToJvmName(cc.getName());
298             throw new NoFieldException(jvmClassName + "$" + field, expr);
299         }
300     }
301 
302     /**
303      * @param jvmClassName  a JVM class name.  e.g. java/lang/String
304      */
lookupFieldByJvmName(String jvmClassName, Symbol fieldName)305     public CtField lookupFieldByJvmName(String jvmClassName, Symbol fieldName)
306         throws CompileError
307     {
308         return lookupField(jvmToJavaName(jvmClassName), fieldName);
309     }
310 
311     /**
312      * @param name      a qualified class name. e.g. java.lang.String
313      */
lookupField(String className, Symbol fieldName)314     public CtField lookupField(String className, Symbol fieldName)
315         throws CompileError
316     {
317         CtClass cc = lookupClass(className, false);
318         try {
319             return cc.getField(fieldName.get());
320         }
321         catch (NotFoundException e) {}
322         throw new CompileError("no such field: " + fieldName.get());
323     }
324 
lookupClassByName(ASTList name)325     public CtClass lookupClassByName(ASTList name) throws CompileError {
326         return lookupClass(Declarator.astToClassName(name, '.'), false);
327     }
328 
lookupClassByJvmName(String jvmName)329     public CtClass lookupClassByJvmName(String jvmName) throws CompileError {
330         return lookupClass(jvmToJavaName(jvmName), false);
331     }
332 
lookupClass(Declarator decl)333     public CtClass lookupClass(Declarator decl) throws CompileError {
334         return lookupClass(decl.getType(), decl.getArrayDim(),
335                            decl.getClassName());
336     }
337 
338     /**
339      * @parma classname         jvm class name.
340      */
lookupClass(int type, int dim, String classname)341     public CtClass lookupClass(int type, int dim, String classname)
342         throws CompileError
343     {
344         String cname = "";
345         CtClass clazz;
346         if (type == CLASS) {
347             clazz = lookupClassByJvmName(classname);
348             if (dim > 0)
349                 cname = clazz.getName();
350             else
351                 return clazz;
352         }
353         else
354             cname = getTypeName(type);
355 
356         while (dim-- > 0)
357             cname += "[]";
358 
359         return lookupClass(cname, false);
360     }
361 
362     /*
363      * type cannot be CLASS
364      */
getTypeName(int type)365     static String getTypeName(int type) throws CompileError {
366         String cname = "";
367         switch (type) {
368         case BOOLEAN :
369             cname = "boolean";
370             break;
371         case CHAR :
372             cname = "char";
373             break;
374         case BYTE :
375             cname = "byte";
376             break;
377         case SHORT :
378             cname = "short";
379             break;
380         case INT :
381             cname = "int";
382             break;
383         case LONG :
384             cname = "long";
385             break;
386         case FLOAT :
387             cname = "float";
388             break;
389         case DOUBLE :
390             cname = "double";
391             break;
392         case VOID :
393             cname = "void";
394             break;
395         default :
396             fatal();
397         }
398 
399         return cname;
400     }
401 
402     /**
403      * @param name      a qualified class name. e.g. java.lang.String
404      */
lookupClass(String name, boolean notCheckInner)405     public CtClass lookupClass(String name, boolean notCheckInner)
406         throws CompileError
407     {
408         try {
409             return lookupClass0(name, notCheckInner);
410         }
411         catch (NotFoundException e) {
412             return searchImports(name);
413         }
414     }
415 
searchImports(String orgName)416     private CtClass searchImports(String orgName)
417         throws CompileError
418     {
419         if (orgName.indexOf('.') < 0) {
420             Iterator it = classPool.getImportedPackages();
421             while (it.hasNext()) {
422                 String pac = (String)it.next();
423                 String fqName = pac + '.' + orgName;
424                 try {
425                     CtClass cc = classPool.get(fqName);
426                     // if the class is found,
427                     classPool.recordInvalidClassName(orgName);
428                     return cc;
429                 }
430                 catch (NotFoundException e) {
431                     classPool.recordInvalidClassName(fqName);
432                     try {
433                         if (pac.endsWith("." + orgName)) {
434                             CtClass cc = classPool.get(pac);
435                             // if the class is found,
436                             classPool.recordInvalidClassName(orgName);
437                             return cc;
438                         }
439                     }
440                     catch (NotFoundException e2) {
441                         classPool.recordInvalidClassName(pac);
442                     }
443                 }
444             }
445         }
446 
447         throw new CompileError("no such class: " + orgName);
448     }
449 
lookupClass0(String classname, boolean notCheckInner)450     private CtClass lookupClass0(String classname, boolean notCheckInner)
451         throws NotFoundException
452     {
453         CtClass cc = null;
454         do {
455             try {
456                 cc = classPool.get(classname);
457             }
458             catch (NotFoundException e) {
459                 int i = classname.lastIndexOf('.');
460                 if (notCheckInner || i < 0)
461                     throw e;
462                 else {
463                     StringBuffer sbuf = new StringBuffer(classname);
464                     sbuf.setCharAt(i, '$');
465                     classname = sbuf.toString();
466                 }
467             }
468         } while (cc == null);
469         return cc;
470     }
471 
472     /* Converts a class name into a JVM-internal representation.
473      *
474      * It may also expand a simple class name to java.lang.*.
475      * For example, this converts Object into java/lang/Object.
476      */
resolveClassName(ASTList name)477     public String resolveClassName(ASTList name) throws CompileError {
478         if (name == null)
479             return null;
480         else
481             return javaToJvmName(lookupClassByName(name).getName());
482     }
483 
484     /* Expands a simple class name to java.lang.*.
485      * For example, this converts Object into java/lang/Object.
486      */
resolveJvmClassName(String jvmName)487     public String resolveJvmClassName(String jvmName) throws CompileError {
488         if (jvmName == null)
489             return null;
490         else
491             return javaToJvmName(lookupClassByJvmName(jvmName).getName());
492     }
493 
getSuperclass(CtClass c)494     public static CtClass getSuperclass(CtClass c) throws CompileError {
495         try {
496             CtClass sc = c.getSuperclass();
497             if (sc != null)
498                 return sc;
499         }
500         catch (NotFoundException e) {}
501         throw new CompileError("cannot find the super class of "
502                                + c.getName());
503     }
504 
javaToJvmName(String classname)505     public static String javaToJvmName(String classname) {
506         return classname.replace('.', '/');
507     }
508 
jvmToJavaName(String classname)509     public static String jvmToJavaName(String classname) {
510         return classname.replace('/', '.');
511     }
512 
descToType(char c)513     public static int descToType(char c) throws CompileError {
514         switch (c) {
515         case 'Z' :
516             return BOOLEAN;
517         case 'C' :
518             return CHAR;
519         case 'B' :
520             return  BYTE;
521         case 'S' :
522             return SHORT;
523         case 'I' :
524             return INT;
525         case 'J' :
526             return LONG;
527         case 'F' :
528             return FLOAT;
529         case 'D' :
530             return DOUBLE;
531         case 'V' :
532             return VOID;
533         case 'L' :
534         case '[' :
535             return CLASS;
536         default :
537             fatal();
538             return VOID;    // never reach here
539         }
540     }
541 
getModifiers(ASTList mods)542     public static int getModifiers(ASTList mods) {
543         int m = 0;
544         while (mods != null) {
545             Keyword k = (Keyword)mods.head();
546             mods = mods.tail();
547             switch (k.get()) {
548             case STATIC :
549                 m |= Modifier.STATIC;
550                 break;
551             case FINAL :
552                 m |= Modifier.FINAL;
553                 break;
554             case SYNCHRONIZED :
555                 m |= Modifier.SYNCHRONIZED;
556                 break;
557             case ABSTRACT :
558                 m |= Modifier.ABSTRACT;
559                 break;
560             case PUBLIC :
561                 m |= Modifier.PUBLIC;
562                 break;
563             case PROTECTED :
564                 m |= Modifier.PROTECTED;
565                 break;
566             case PRIVATE :
567                 m |= Modifier.PRIVATE;
568                 break;
569             case VOLATILE :
570                 m |= Modifier.VOLATILE;
571                 break;
572             case TRANSIENT :
573                 m |= Modifier.TRANSIENT;
574                 break;
575             case STRICT :
576                 m |= Modifier.STRICT;
577                 break;
578             }
579         }
580 
581         return m;
582     }
583 }
584