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