Home | History | Annotate | Download | only in compiler
      1 /*
      2  * Javassist, a Java-bytecode translator toolkit.
      3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
      4  *
      5  * The contents of this file are subject to the Mozilla Public License Version
      6  * 1.1 (the "License"); you may not use this file except in compliance with
      7  * the License.  Alternatively, the contents of this file may be used under
      8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
      9  *
     10  * Software distributed under the License is distributed on an "AS IS" basis,
     11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     12  * for the specific language governing rights and limitations under the
     13  * License.
     14  */
     15 
     16 package javassist.compiler;
     17 
     18 import 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 
     29     public MemberResolver(ClassPool cp) {
     30         classPool = cp;
     31     }
     32 
     33     public ClassPool getClassPool() { return classPool; }
     34 
     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      */
     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 
     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          */
     69         public boolean isStatic() {
     70             int acc = info.getAccessFlags();
     71             return (acc & AccessFlag.STATIC) != 0;
     72         }
     73     }
     74 
     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 
    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      */
    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      */
    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      */
    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      */
    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 
    325     public CtClass lookupClassByName(ASTList name) throws CompileError {
    326         return lookupClass(Declarator.astToClassName(name, '.'), false);
    327     }
    328 
    329     public CtClass lookupClassByJvmName(String jvmName) throws CompileError {
    330         return lookupClass(jvmToJavaName(jvmName), false);
    331     }
    332 
    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      */
    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      */
    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      */
    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 
    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 
    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      */
    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      */
    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 
    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 
    505     public static String javaToJvmName(String classname) {
    506         return classname.replace('.', '/');
    507     }
    508 
    509     public static String jvmToJavaName(String classname) {
    510         return classname.replace('/', '.');
    511     }
    512 
    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 
    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