Home | History | Annotate | Download | only in Analysis
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib.Code.Analysis;
     30 
     31 import org.jf.dexlib.*;
     32 import org.jf.dexlib.Util.AccessFlags;
     33 import org.jf.dexlib.Util.ExceptionWithContext;
     34 import org.jf.dexlib.Util.SparseArray;
     35 
     36 import javax.annotation.Nonnull;
     37 import javax.annotation.Nullable;
     38 import java.io.File;
     39 import java.util.*;
     40 import java.util.regex.Matcher;
     41 import java.util.regex.Pattern;
     42 
     43 import static org.jf.dexlib.ClassDataItem.EncodedField;
     44 import static org.jf.dexlib.ClassDataItem.EncodedMethod;
     45 
     46 public class ClassPath {
     47     private static ClassPath theClassPath = null;
     48 
     49     /**
     50      * The current version of dalvik in master(AOSP) has a slight change to the way the
     51      * virtual tables are computed. This should be set to true to use the new logic.
     52      * TODO: set this based on api level, once it's present in a released version of Android
     53      */
     54     private boolean checkPackagePrivateAccess;
     55 
     56     private final HashMap<String, ClassDef> classDefs;
     57     protected ClassDef javaLangObjectClassDef; //cached ClassDef for Ljava/lang/Object;
     58 
     59     // Contains the classes that we haven't loaded yet
     60     private HashMap<String, UnresolvedClassInfo> unloadedClasses;
     61 
     62     private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
     63 
     64     /**
     65      * Initialize the class path using the dependencies from an odex file
     66      * @param classPathDirs The directories to search for boot class path files
     67      * @param extraBootClassPathEntries any extra entries that should be added after the entries that are read
     68      * from the odex file
     69      * @param dexFilePath The path of the dex file (used for error reporting purposes only)
     70      * @param dexFile The DexFile to load - it must represents an odex file
     71      */
     72     public static void InitializeClassPathFromOdex(String[] classPathDirs, String[] extraBootClassPathEntries,
     73                                                    String dexFilePath, DexFile dexFile,
     74                                                    boolean checkPackagePrivateAccess) {
     75         if (!dexFile.isOdex()) {
     76             throw new ExceptionWithContext("Cannot use InitialiazeClassPathFromOdex with a non-odex DexFile");
     77         }
     78 
     79         if (theClassPath != null) {
     80             throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
     81         }
     82 
     83         OdexDependencies odexDependencies = dexFile.getOdexDependencies();
     84 
     85         String[] bootClassPath = new String[odexDependencies.getDependencyCount()];
     86         for (int i=0; i<bootClassPath.length; i++) {
     87             String dependency = odexDependencies.getDependency(i);
     88 
     89             if (dependency.endsWith(".odex")) {
     90                 int slashIndex = dependency.lastIndexOf("/");
     91 
     92                 if (slashIndex != -1) {
     93                     dependency = dependency.substring(slashIndex+1);
     94                 }
     95             } else if (dependency.endsWith("@classes.dex")) {
     96                 Matcher m = dalvikCacheOdexPattern.matcher(dependency);
     97 
     98                 if (!m.find()) {
     99                     throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
    100                 }
    101 
    102                 dependency = m.group(1);
    103             } else {
    104                 throw new ExceptionWithContext(String.format("Cannot parse dependency value %s", dependency));
    105             }
    106 
    107             bootClassPath[i] = dependency;
    108         }
    109 
    110         theClassPath = new ClassPath();
    111         theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile,
    112                 checkPackagePrivateAccess);
    113     }
    114 
    115     /**
    116      * Initialize the class path using the given boot class path entries
    117      * @param classPathDirs The directories to search for boot class path files
    118      * @param bootClassPath A list of the boot class path entries to search for and load
    119      * @param dexFilePath The path of the dex file (used for error reporting purposes only)
    120      * @param dexFile the DexFile to load
    121      * classes
    122      */
    123     public static void InitializeClassPath(String[] classPathDirs, String[] bootClassPath,
    124                                            String[] extraBootClassPathEntries, String dexFilePath, DexFile dexFile,
    125                                            boolean checkPackagePrivateAccess) {
    126         if (theClassPath != null) {
    127             throw new ExceptionWithContext("Cannot initialize ClassPath multiple times");
    128         }
    129 
    130         theClassPath = new ClassPath();
    131         theClassPath.initClassPath(classPathDirs, bootClassPath, extraBootClassPathEntries, dexFilePath, dexFile,
    132                 checkPackagePrivateAccess);
    133     }
    134 
    135     private ClassPath() {
    136         classDefs = new HashMap<String, ClassDef>();
    137     }
    138 
    139     private void initClassPath(String[] classPathDirs, String[] bootClassPath, String[] extraBootClassPathEntries,
    140                                String dexFilePath, DexFile dexFile, boolean checkPackagePrivateAccess) {
    141         this.checkPackagePrivateAccess = checkPackagePrivateAccess;
    142         unloadedClasses = new LinkedHashMap<String, UnresolvedClassInfo>();
    143 
    144         if (bootClassPath != null) {
    145             for (String bootClassPathEntry: bootClassPath) {
    146                 loadBootClassPath(classPathDirs, bootClassPathEntry);
    147             }
    148         }
    149 
    150         if (extraBootClassPathEntries != null) {
    151             for (String bootClassPathEntry: extraBootClassPathEntries) {
    152                 loadBootClassPath(classPathDirs, bootClassPathEntry);
    153             }
    154         }
    155 
    156         if (dexFile != null) {
    157             loadDexFile(dexFilePath, dexFile);
    158         }
    159 
    160         javaLangObjectClassDef = getClassDef("Ljava/lang/Object;", false);
    161 
    162         for (String primitiveType: new String[]{"Z", "B", "S", "C", "I", "J", "F", "D"}) {
    163             ClassDef classDef = new PrimitiveClassDef(primitiveType);
    164             classDefs.put(primitiveType, classDef);
    165         }
    166     }
    167 
    168     private void loadBootClassPath(String[] classPathDirs, String bootClassPathEntry) {
    169         for (String classPathDir: classPathDirs) {
    170             File file = null;
    171             DexFile dexFile = null;
    172 
    173             int extIndex = bootClassPathEntry.lastIndexOf(".");
    174 
    175             String baseEntry;
    176             if (extIndex == -1) {
    177                 baseEntry = bootClassPathEntry;
    178             } else {
    179                 baseEntry = bootClassPathEntry.substring(0, extIndex);
    180             }
    181 
    182             for (String ext: new String[]{"", ".odex", ".jar", ".apk", ".zip"}) {
    183                 if (ext.length() == 0) {
    184                     file = new File(classPathDir, bootClassPathEntry);
    185                 } else {
    186                     file = new File(classPathDir, baseEntry + ext);
    187                 }
    188 
    189                 if (file.exists()) {
    190                     if (!file.canRead()) {
    191                         System.err.println(String.format("warning: cannot open %s for reading. Will continue " +
    192                                 "looking.", file.getPath()));
    193                         continue;
    194                     }
    195 
    196                     try {
    197                         dexFile = new DexFile(file, false, true);
    198                     } catch (DexFile.NoClassesDexException ex) {
    199                         continue;
    200                     } catch (Exception ex) {
    201                         throw ExceptionWithContext.withContext(ex, "Error while reading boot class path entry \"" +
    202                         bootClassPathEntry + "\".");
    203                     }
    204                 }
    205             }
    206             if (dexFile == null) {
    207                 continue;
    208             }
    209 
    210             try {
    211                 loadDexFile(file.getPath(), dexFile);
    212             } catch (Exception ex) {
    213                 throw ExceptionWithContext.withContext(ex,
    214                         String.format("Error while loading boot classpath entry %s", bootClassPathEntry));
    215             }
    216             return;
    217         }
    218         throw new ExceptionWithContext(String.format("Cannot locate boot class path file %s", bootClassPathEntry));
    219     }
    220 
    221     private void loadDexFile(String dexFilePath, DexFile dexFile) {
    222         for (ClassDefItem classDefItem: dexFile.ClassDefsSection.getItems()) {
    223             try {
    224                 UnresolvedClassInfo unresolvedClassInfo = new UnresolvedClassInfo(dexFilePath, classDefItem);
    225 
    226                 if (!unloadedClasses.containsKey(unresolvedClassInfo.classType)) {
    227                     unloadedClasses.put(unresolvedClassInfo.classType, unresolvedClassInfo);
    228                 }
    229             } catch (Exception ex) {
    230                 throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s",
    231                         classDefItem.getClassType().getTypeDescriptor()));
    232             }
    233         }
    234     }
    235 
    236     /**
    237      * This method loads the given class (and any dependent classes, as needed), removing them from unloadedClasses
    238      * @param classType the class to load
    239      * @return the newly loaded ClassDef object for the given class, or null if the class cannot be found
    240      */
    241     @Nullable
    242     private static ClassDef loadClassDef(String classType) {
    243         ClassDef classDef = null;
    244 
    245         UnresolvedClassInfo classInfo = theClassPath.unloadedClasses.get(classType);
    246         if (classInfo == null) {
    247             return null;
    248         }
    249 
    250         try {
    251             classDef = new ClassDef(classInfo);
    252             theClassPath.classDefs.put(classDef.classType, classDef);
    253         } catch (Exception ex) {
    254             throw ExceptionWithContext.withContext(ex, String.format("Error while loading class %s from file %s",
    255                     classInfo.classType, classInfo.dexFilePath));
    256         }
    257         theClassPath.unloadedClasses.remove(classType);
    258 
    259         return classDef;
    260     }
    261 
    262     @Nonnull
    263     public static ClassDef getClassDef(String classType, boolean createUnresolvedClassDef)  {
    264         ClassDef classDef = theClassPath.classDefs.get(classType);
    265         if (classDef == null) {
    266             //if it's an array class, try to create it
    267             if (classType.charAt(0) == '[') {
    268                 return theClassPath.createArrayClassDef(classType);
    269             } else {
    270                 try {
    271                     classDef = loadClassDef(classType);
    272                     if (classDef == null) {
    273                         throw new ExceptionWithContext(
    274                                 String.format("Could not find definition for class %s", classType));
    275                     }
    276                 } catch (Exception ex) {
    277                     RuntimeException exWithContext = ExceptionWithContext.withContext(ex,
    278                             String.format("Error while loading ClassPath class %s", classType));
    279                     if (createUnresolvedClassDef) {
    280                         //TODO: add warning message
    281                         return theClassPath.createUnresolvedClassDef(classType);
    282                     } else {
    283                         throw exWithContext;
    284                     }
    285                 }
    286             }
    287         }
    288         return classDef;
    289     }
    290 
    291     public static ClassDef getClassDef(String classType) {
    292         return getClassDef(classType, true);
    293     }
    294 
    295     public static ClassDef getClassDef(TypeIdItem classType) {
    296         return getClassDef(classType.getTypeDescriptor());
    297     }
    298 
    299     public static ClassDef getClassDef(TypeIdItem classType, boolean creatUnresolvedClassDef) {
    300         return getClassDef(classType.getTypeDescriptor(), creatUnresolvedClassDef);
    301     }
    302 
    303     //256 [ characters
    304     private static final String arrayPrefix = "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" +
    305         "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" +
    306         "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[";
    307     private static ClassDef getArrayClassDefByElementClassAndDimension(ClassDef classDef, int arrayDimension) {
    308         return getClassDef(arrayPrefix.substring(256 - arrayDimension) + classDef.classType);
    309     }
    310 
    311     private static ClassDef unresolvedObjectClassDef = null;
    312     public static ClassDef getUnresolvedObjectClassDef() {
    313         if (unresolvedObjectClassDef == null) {
    314             unresolvedObjectClassDef = new UnresolvedClassDef("Ljava/lang/Object;");
    315         }
    316         return unresolvedObjectClassDef;
    317     }
    318 
    319     private ClassDef createUnresolvedClassDef(String classType)  {
    320         assert classType.charAt(0) == 'L';
    321 
    322         UnresolvedClassDef unresolvedClassDef = new UnresolvedClassDef(classType);
    323         classDefs.put(classType, unresolvedClassDef);
    324         return unresolvedClassDef;
    325     }
    326 
    327     private ClassDef createArrayClassDef(String arrayClassName) {
    328         assert arrayClassName != null;
    329         assert arrayClassName.charAt(0) == '[';
    330 
    331         ArrayClassDef arrayClassDef = new ArrayClassDef(arrayClassName);
    332         if (arrayClassDef.elementClass == null) {
    333             return null;
    334         }
    335 
    336         classDefs.put(arrayClassName, arrayClassDef);
    337         return arrayClassDef;
    338     }
    339 
    340     public static ClassDef getCommonSuperclass(ClassDef class1, ClassDef class2) {
    341         if (class1 == class2) {
    342             return class1;
    343         }
    344 
    345         if (class1 == null) {
    346             return class2;
    347         }
    348 
    349         if (class2 == null) {
    350             return class1;
    351         }
    352 
    353         //TODO: do we want to handle primitive types here? I don't think so.. (if not, add assert)
    354 
    355         if (class2.isInterface) {
    356             if (class1.implementsInterface(class2)) {
    357                 return class2;
    358             }
    359             return theClassPath.javaLangObjectClassDef;
    360         }
    361 
    362         if (class1.isInterface) {
    363             if (class2.implementsInterface(class1)) {
    364                 return class1;
    365             }
    366             return theClassPath.javaLangObjectClassDef;
    367         }
    368 
    369         if (class1 instanceof ArrayClassDef && class2 instanceof ArrayClassDef) {
    370             return getCommonArraySuperclass((ArrayClassDef)class1, (ArrayClassDef)class2);
    371         }
    372 
    373         //we've got two non-array reference types. Find the class depth of each, and then move up the longer one
    374         //so that both classes are at the same class depth, and then move each class up until they match
    375 
    376         //we don't strictly need to keep track of the class depth separately, but it's probably slightly faster
    377         //to do so, rather than calling getClassDepth() many times
    378         int class1Depth = class1.getClassDepth();
    379         int class2Depth = class2.getClassDepth();
    380 
    381         while (class1Depth > class2Depth) {
    382             class1 = class1.superclass;
    383             class1Depth--;
    384         }
    385 
    386         while (class2Depth > class1Depth) {
    387             class2 = class2.superclass;
    388             class2Depth--;
    389         }
    390 
    391         while (class1Depth > 0) {
    392             if (class1 == class2) {
    393                 return class1;
    394             }
    395             class1 = class1.superclass;
    396             class1Depth--;
    397             class2 = class2.superclass;
    398             class2Depth--;
    399         }
    400 
    401         return class1;
    402     }
    403 
    404     private static ClassDef getCommonArraySuperclass(ArrayClassDef class1, ArrayClassDef class2) {
    405         assert class1 != class2;
    406 
    407         //If one of the arrays is a primitive array, then the only option is to return java.lang.Object
    408         //TODO: might it be possible to merge something like int[] and short[] into int[]? (I don't think so..)
    409         if (class1.elementClass instanceof PrimitiveClassDef || class2.elementClass instanceof PrimitiveClassDef) {
    410             return theClassPath.javaLangObjectClassDef;
    411         }
    412 
    413         //if the two arrays have the same number of dimensions, then we should return an array class with the
    414         //same number of dimensions, for the common superclass of the 2 element classes
    415         if (class1.arrayDimensions == class2.arrayDimensions) {
    416             ClassDef commonElementClass;
    417             if (class1.elementClass instanceof UnresolvedClassDef ||
    418                 class2.elementClass instanceof UnresolvedClassDef) {
    419                 commonElementClass = ClassPath.getUnresolvedObjectClassDef();
    420             } else {
    421                 commonElementClass = getCommonSuperclass(class1.elementClass, class2.elementClass);
    422             }
    423             return getArrayClassDefByElementClassAndDimension(commonElementClass, class1.arrayDimensions);
    424         }
    425 
    426         //something like String[][][] and String[][] should be merged to Object[][]
    427         //this also holds when the element classes aren't the same (but are both reference types)
    428         int dimensions = Math.min(class1.arrayDimensions, class2.arrayDimensions);
    429         return getArrayClassDefByElementClassAndDimension(theClassPath.javaLangObjectClassDef, dimensions);
    430     }
    431 
    432     public static class ArrayClassDef extends ClassDef {
    433         private final ClassDef elementClass;
    434         private final int arrayDimensions;
    435 
    436         protected ArrayClassDef(String arrayClassType) {
    437             super(arrayClassType, ClassDef.ArrayClassDef);
    438             assert arrayClassType.charAt(0) == '[';
    439 
    440             int i=0;
    441             while (arrayClassType.charAt(i) == '[') i++;
    442 
    443             String elementClassType = arrayClassType.substring(i);
    444 
    445             if (i>256) {
    446                 throw new ExceptionWithContext("Error while creating array class for element type " + elementClassType +
    447                         " with " + i + " dimensions. The maximum number of dimensions is 256");
    448             }
    449 
    450             try {
    451                 elementClass = ClassPath.getClassDef(arrayClassType.substring(i));
    452             } catch (Exception ex) {
    453                 throw ExceptionWithContext.withContext(ex, "Error while creating array class " + arrayClassType);
    454             }
    455             arrayDimensions = i;
    456         }
    457 
    458         /**
    459          * Returns the "base" element class of the array.
    460          *
    461          * For example, for a multi-dimensional array of strings ([[Ljava/lang/String;), this method would return
    462          * Ljava/lang/String;
    463          * @return the "base" element class of the array
    464          */
    465         public ClassDef getBaseElementClass() {
    466             return elementClass;
    467         }
    468 
    469         /**
    470          * Returns the "immediate" element class of the array.
    471          *
    472          * For example, for a multi-dimensional array of stings with 2 dimensions ([[Ljava/lang/String;), this method
    473          * would return [Ljava/lang/String;
    474          * @return the immediate element class of the array
    475          */
    476         public ClassDef getImmediateElementClass() {
    477             if (arrayDimensions == 1) {
    478                 return elementClass;
    479             }
    480             return getArrayClassDefByElementClassAndDimension(elementClass, arrayDimensions - 1);
    481         }
    482 
    483         public int getArrayDimensions() {
    484             return arrayDimensions;
    485         }
    486 
    487         @Override
    488         public boolean extendsClass(ClassDef superclassDef) {
    489             if (!(superclassDef instanceof ArrayClassDef)) {
    490                 if (superclassDef == ClassPath.theClassPath.javaLangObjectClassDef) {
    491                     return true;
    492                 } else if (superclassDef.isInterface) {
    493                     return this.implementsInterface(superclassDef);
    494                 }
    495                 return false;
    496             }
    497 
    498             ArrayClassDef arraySuperclassDef = (ArrayClassDef)superclassDef;
    499             if (this.arrayDimensions == arraySuperclassDef.arrayDimensions) {
    500                 ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
    501 
    502                 if (baseElementClass.isInterface) {
    503                     return true;
    504                 }
    505 
    506                 return baseElementClass.extendsClass(arraySuperclassDef.getBaseElementClass());
    507             } else if (this.arrayDimensions > arraySuperclassDef.arrayDimensions) {
    508                 ClassDef baseElementClass = arraySuperclassDef.getBaseElementClass();
    509                 if (baseElementClass.isInterface) {
    510                     return true;
    511                 }
    512 
    513                 if (baseElementClass == ClassPath.theClassPath.javaLangObjectClassDef) {
    514                     return true;
    515                 }
    516                 return false;
    517             }
    518             return false;
    519         }
    520     }
    521 
    522     public static class PrimitiveClassDef extends ClassDef {
    523         protected PrimitiveClassDef(String primitiveClassType) {
    524             super(primitiveClassType, ClassDef.PrimitiveClassDef);
    525             assert primitiveClassType.charAt(0) != 'L' && primitiveClassType.charAt(0) != '[';
    526         }
    527     }
    528 
    529     public static class UnresolvedClassDef extends ClassDef {
    530         protected UnresolvedClassDef(String unresolvedClassDef) {
    531             super(unresolvedClassDef, ClassDef.UnresolvedClassDef);
    532             assert unresolvedClassDef.charAt(0) == 'L';
    533         }
    534 
    535         protected ValidationException unresolvedValidationException() {
    536             return new ValidationException(String.format("class %s cannot be resolved.", this.getClassType()));
    537         }
    538 
    539         public ClassDef getSuperclass() {
    540             return theClassPath.javaLangObjectClassDef;
    541         }
    542 
    543         public int getClassDepth() {
    544             throw unresolvedValidationException();
    545         }
    546 
    547         public boolean isInterface() {
    548             throw unresolvedValidationException();
    549         }
    550 
    551          public boolean extendsClass(ClassDef superclassDef) {
    552             if (superclassDef != theClassPath.javaLangObjectClassDef && superclassDef != this) {
    553                 throw unresolvedValidationException();
    554             }
    555             return true;
    556         }
    557 
    558         public boolean implementsInterface(ClassDef interfaceDef) {
    559             throw unresolvedValidationException();
    560         }
    561 
    562         public boolean hasVirtualMethod(String method) {
    563             if (!super.hasVirtualMethod(method)) {
    564                 throw unresolvedValidationException();
    565             }
    566             return true;
    567         }
    568     }
    569 
    570     public static class FieldDef {
    571         public final String definingClass;
    572         public final String name;
    573         public final String type;
    574 
    575         public FieldDef(String definingClass, String name, String type) {
    576             this.definingClass = definingClass;
    577             this.name = name;
    578             this.type = type;
    579         }
    580     }
    581 
    582     public static class ClassDef implements Comparable<ClassDef> {
    583         private final String classType;
    584         private final ClassDef superclass;
    585         /**
    586          * This is a list of all of the interfaces that a class implements, either directly or indirectly. It includes
    587          * all interfaces implemented by the superclass, and all super-interfaces of any implemented interface. The
    588          * intention is to make it easier to determine whether the class implements a given interface or not.
    589          */
    590         private final TreeSet<ClassDef> implementedInterfaces;
    591 
    592         private final boolean isInterface;
    593 
    594         private final int classDepth;
    595 
    596         // classes can only be public or package-private. Internally, any private/protected inner class is actually
    597         // package-private.
    598         private final boolean isPublic;
    599 
    600         private final VirtualMethod[] vtable;
    601 
    602         //this maps a method name of the form method(III)Ljava/lang/String; to an integer
    603         //If the value is non-negative, it is a vtable index
    604         //If it is -1, it is a non-static direct method,
    605         //If it is -2, it is a static method
    606         private final HashMap<String, Integer> methodLookup;
    607 
    608         private final SparseArray<FieldDef> instanceFields;
    609 
    610         public final static int ArrayClassDef = 0;
    611         public final static int PrimitiveClassDef = 1;
    612         public final static int UnresolvedClassDef = 2;
    613 
    614         private final static int DirectMethod = -1;
    615         private final static int StaticMethod = -2;
    616 
    617         /**
    618          * The following fields are used only during the initial loading of classes, and are set to null afterwards
    619          * TODO: free these
    620          */
    621 
    622         //This is only the virtual methods that this class declares itself.
    623         private VirtualMethod[] virtualMethods;
    624         //this is a list of all the interfaces that the class implements directory, or any super interfaces of those
    625         //interfaces. It is generated in such a way that it is ordered in the same way as dalvik's ClassObject.iftable,
    626         private LinkedHashMap<String, ClassDef> interfaceTable;
    627 
    628         /**
    629          * This constructor is used for the ArrayClassDef, PrimitiveClassDef and UnresolvedClassDef subclasses
    630          * @param classType the class type
    631          * @param classFlavor one of ArrayClassDef, PrimitiveClassDef or UnresolvedClassDef
    632          */
    633         protected ClassDef(String classType, int classFlavor) {
    634             if (classFlavor == ArrayClassDef) {
    635                 assert classType.charAt(0) == '[';
    636                 this.classType = classType;
    637                 this.superclass = ClassPath.theClassPath.javaLangObjectClassDef;
    638                 implementedInterfaces = new TreeSet<ClassDef>();
    639                 implementedInterfaces.add(ClassPath.getClassDef("Ljava/lang/Cloneable;"));
    640                 implementedInterfaces.add(ClassPath.getClassDef("Ljava/io/Serializable;"));
    641                 isInterface = false;
    642                 isPublic = true;
    643 
    644                 vtable = superclass.vtable;
    645                 methodLookup = superclass.methodLookup;
    646 
    647                 instanceFields = superclass.instanceFields;
    648                 classDepth = 1; //1 off from java.lang.Object
    649 
    650                 virtualMethods = null;
    651                 interfaceTable = null;
    652             } else if (classFlavor == PrimitiveClassDef) {
    653                 //primitive type
    654                 assert classType.charAt(0) != '[' && classType.charAt(0) != 'L';
    655 
    656                 this.classType = classType;
    657                 this.superclass = null;
    658                 implementedInterfaces = null;
    659                 isInterface = false;
    660                 isPublic = true;
    661                 vtable = null;
    662                 methodLookup = null;
    663                 instanceFields = null;
    664                 classDepth = 0; //TODO: maybe use -1 to indicate not applicable?
    665 
    666                 virtualMethods = null;
    667                 interfaceTable = null;
    668             } else /*if (classFlavor == UnresolvedClassDef)*/ {
    669                 assert classType.charAt(0) == 'L';
    670                 this.classType = classType;
    671                 this.superclass = ClassPath.getClassDef("Ljava/lang/Object;");
    672                 implementedInterfaces = new TreeSet<ClassDef>();
    673                 isInterface = false;
    674                 isPublic = true;
    675 
    676                 vtable = superclass.vtable;
    677                 methodLookup = superclass.methodLookup;
    678 
    679                 instanceFields = superclass.instanceFields;
    680                 classDepth = 1; //1 off from java.lang.Object
    681 
    682                 virtualMethods = null;
    683                 interfaceTable = null;
    684             }
    685         }
    686 
    687         protected ClassDef(UnresolvedClassInfo classInfo)  {
    688             classType = classInfo.classType;
    689             isPublic = classInfo.isPublic;
    690             isInterface = classInfo.isInterface;
    691 
    692             superclass = loadSuperclass(classInfo);
    693             if (superclass == null) {
    694                 classDepth = 0;
    695             } else {
    696                 classDepth = superclass.classDepth + 1;
    697             }
    698 
    699             implementedInterfaces = loadAllImplementedInterfaces(classInfo);
    700 
    701             //TODO: we can probably get away with only creating the interface table for interface types
    702             interfaceTable = loadInterfaceTable(classInfo);
    703             virtualMethods = classInfo.virtualMethods;
    704             vtable = loadVtable(classInfo);
    705 
    706             int directMethodCount = 0;
    707             if (classInfo.directMethods != null) {
    708                 directMethodCount = classInfo.directMethods.length;
    709             }
    710             methodLookup = new HashMap<String, Integer>((int)Math.ceil(((vtable.length + directMethodCount)/ .7f)), .75f);
    711             for (int i=0; i<vtable.length; i++) {
    712                 methodLookup.put(vtable[i].method, i);
    713             }
    714             if (directMethodCount > 0) {
    715                 for (int i=0; i<classInfo.directMethods.length; i++) {
    716                     if (classInfo.staticMethods[i]) {
    717                         methodLookup.put(classInfo.directMethods[i], StaticMethod);
    718                     } else {
    719                         methodLookup.put(classInfo.directMethods[i], DirectMethod);
    720                     }
    721                 }
    722             }
    723 
    724             instanceFields = loadFields(classInfo);
    725         }
    726 
    727         public String getClassType() {
    728             return classType;
    729         }
    730 
    731         public ClassDef getSuperclass() {
    732             return superclass;
    733         }
    734 
    735         public int getClassDepth() {
    736             return classDepth;
    737         }
    738 
    739         public boolean isInterface() {
    740             return this.isInterface;
    741         }
    742 
    743         public boolean isPublic() {
    744             return this.isPublic;
    745         }
    746 
    747         public boolean extendsClass(ClassDef superclassDef) {
    748             if (superclassDef == null) {
    749                 return false;
    750             }
    751 
    752             if (this == superclassDef) {
    753                 return true;
    754             }
    755 
    756             if (superclassDef instanceof UnresolvedClassDef) {
    757                 throw ((UnresolvedClassDef)superclassDef).unresolvedValidationException();
    758             }
    759 
    760             int superclassDepth = superclassDef.classDepth;
    761             ClassDef ancestor = this;
    762             while (ancestor.classDepth > superclassDepth) {
    763                 ancestor = ancestor.getSuperclass();
    764             }
    765 
    766             return ancestor == superclassDef;
    767         }
    768 
    769         /**
    770          * Returns true if this class implements the given interface. This searches the interfaces that this class
    771          * directly implements, any interface implemented by this class's superclasses, and any super-interface of
    772          * any of these interfaces.
    773          * @param interfaceDef the interface
    774          * @return true if this class implements the given interface
    775          */
    776         public boolean implementsInterface(ClassDef interfaceDef) {
    777             assert !(interfaceDef instanceof UnresolvedClassDef);
    778             return implementedInterfaces.contains(interfaceDef);
    779         }
    780 
    781         public boolean hasVirtualMethod(String method) {
    782             Integer val = methodLookup.get(method);
    783             if (val == null || val < 0) {
    784                 return false;
    785             }
    786             return true;
    787         }
    788 
    789         public int getMethodType(String method) {
    790             Integer val = methodLookup.get(method);
    791             if (val == null) {
    792                 return -1;
    793             }
    794             if (val >= 0) {
    795                 return DeodexUtil.Virtual;
    796             }
    797             if (val == DirectMethod) {
    798                 return DeodexUtil.Direct;
    799             }
    800             if (val == StaticMethod) {
    801                 return DeodexUtil.Static;
    802             }
    803             throw new RuntimeException("Unexpected method type");
    804         }
    805 
    806         public FieldDef getInstanceField(int fieldOffset) {
    807             return this.instanceFields.get(fieldOffset, null);
    808         }
    809 
    810         public String getVirtualMethod(int vtableIndex) {
    811             if (vtableIndex < 0 || vtableIndex >= vtable.length) {
    812                 return null;
    813             }
    814             return this.vtable[vtableIndex].method;
    815         }
    816 
    817         private void swap(byte[] fieldTypes, FieldDef[] fields, int position1, int position2) {
    818             byte tempType = fieldTypes[position1];
    819             fieldTypes[position1] = fieldTypes[position2];
    820             fieldTypes[position2] = tempType;
    821 
    822             FieldDef tempField = fields[position1];
    823             fields[position1] = fields[position2];
    824             fields[position2] = tempField;
    825         }
    826 
    827         private ClassDef loadSuperclass(UnresolvedClassInfo classInfo) {
    828             if (classInfo.classType.equals("Ljava/lang/Object;")) {
    829                 if (classInfo.superclassType != null) {
    830                     throw new ExceptionWithContext("Invalid superclass " +
    831                             classInfo.superclassType + " for Ljava/lang/Object;. " +
    832                             "The Object class cannot have a superclass");
    833                 }
    834                 return null;
    835             } else {
    836                 String superclassType = classInfo.superclassType;
    837                 if (superclassType == null) {
    838                     throw new ExceptionWithContext(classInfo.classType + " has no superclass");
    839                 }
    840 
    841                 ClassDef superclass;
    842                 try {
    843                     superclass = ClassPath.getClassDef(superclassType);
    844                 } catch (Exception ex) {
    845                     throw ExceptionWithContext.withContext(ex,
    846                             String.format("Could not find superclass %s", superclassType));
    847                 }
    848 
    849                 if (!isInterface && superclass.isInterface) {
    850                     throw new ValidationException("Class " + classType + " has the interface " + superclass.classType +
    851                             " as its superclass");
    852                 }
    853                 if (isInterface && !superclass.isInterface && superclass !=
    854                         ClassPath.theClassPath.javaLangObjectClassDef) {
    855                     throw new ValidationException("Interface " + classType + " has the non-interface class " +
    856                             superclass.classType + " as its superclass");
    857                 }
    858 
    859                 return superclass;
    860             }
    861         }
    862 
    863         private TreeSet<ClassDef> loadAllImplementedInterfaces(UnresolvedClassInfo classInfo) {
    864             assert classType != null;
    865             assert classType.equals("Ljava/lang/Object;") || superclass != null;
    866             assert classInfo != null;
    867 
    868             TreeSet<ClassDef> implementedInterfaceSet = new TreeSet<ClassDef>();
    869 
    870             if (superclass != null) {
    871                 for (ClassDef interfaceDef: superclass.implementedInterfaces) {
    872                     implementedInterfaceSet.add(interfaceDef);
    873                 }
    874             }
    875 
    876 
    877             if (classInfo.interfaces != null) {
    878                 for (String interfaceType: classInfo.interfaces) {
    879                     ClassDef interfaceDef;
    880                     try {
    881                         interfaceDef = ClassPath.getClassDef(interfaceType);
    882                     } catch (Exception ex) {
    883                         throw ExceptionWithContext.withContext(ex,
    884                                 String.format("Could not find interface %s", interfaceType));
    885                     }
    886                     assert interfaceDef.isInterface();
    887                     implementedInterfaceSet.add(interfaceDef);
    888 
    889                     interfaceDef = interfaceDef.getSuperclass();
    890                     while (!interfaceDef.getClassType().equals("Ljava/lang/Object;")) {
    891                         assert interfaceDef.isInterface();
    892                         implementedInterfaceSet.add(interfaceDef);
    893                         interfaceDef = interfaceDef.getSuperclass();
    894                     }
    895                 }
    896             }
    897 
    898             return implementedInterfaceSet;
    899         }
    900 
    901         private LinkedHashMap<String, ClassDef> loadInterfaceTable(UnresolvedClassInfo classInfo) {
    902             if (classInfo.interfaces == null) {
    903                 return null;
    904             }
    905 
    906             LinkedHashMap<String, ClassDef> interfaceTable = new LinkedHashMap<String, ClassDef>();
    907 
    908             for (String interfaceType: classInfo.interfaces) {
    909                 if (!interfaceTable.containsKey(interfaceType)) {
    910                     ClassDef interfaceDef;
    911                     try {
    912                         interfaceDef = ClassPath.getClassDef(interfaceType);
    913                     } catch (Exception ex) {
    914                         throw ExceptionWithContext.withContext(ex,
    915                                 String.format("Could not find interface %s", interfaceType));
    916                     }
    917                     interfaceTable.put(interfaceType, interfaceDef);
    918 
    919                     if (interfaceDef.interfaceTable != null) {
    920                         for (ClassDef superInterface: interfaceDef.interfaceTable.values()) {
    921                             if (!interfaceTable.containsKey(superInterface.classType)) {
    922                                 interfaceTable.put(superInterface.classType, superInterface);
    923                             }
    924                         }
    925                     }
    926                 }
    927             }
    928 
    929             return interfaceTable;
    930         }
    931 
    932         //TODO: check the case when we have a package private method that overrides an interface method
    933         private VirtualMethod[] loadVtable(UnresolvedClassInfo classInfo) {
    934             //TODO: it might be useful to keep track of which class's implementation is used for each virtual method. In other words, associate the implementing class type with each vtable entry
    935             List<VirtualMethod> virtualMethodList = new LinkedList<VirtualMethod>();
    936 
    937             //copy the virtual methods from the superclass
    938             int methodIndex = 0;
    939             if (superclass != null) {
    940                 for (int i=0; i<superclass.vtable.length; i++) {
    941                     virtualMethodList.add(superclass.vtable[i]);
    942                 }
    943 
    944                 assert superclass.instanceFields != null;
    945             }
    946 
    947 
    948             //iterate over the virtual methods in the current class, and only add them when we don't already have the
    949             //method (i.e. if it was implemented by the superclass)
    950             if (!this.isInterface) {
    951                 if (classInfo.virtualMethods != null) {
    952                     addToVtable(classInfo.virtualMethods, virtualMethodList);
    953                 }
    954 
    955                 if (interfaceTable != null) {
    956                     for (ClassDef interfaceDef: interfaceTable.values()) {
    957                         if (interfaceDef.virtualMethods == null) {
    958                             continue;
    959                         }
    960 
    961                         addToVtable(interfaceDef.virtualMethods, virtualMethodList);
    962                     }
    963                 }
    964             }
    965 
    966             VirtualMethod[] vtable = new VirtualMethod[virtualMethodList.size()];
    967             for (int i=0; i<virtualMethodList.size(); i++) {
    968                 vtable[i] = virtualMethodList.get(i);
    969             }
    970 
    971             return vtable;
    972         }
    973 
    974         private void addToVtable(VirtualMethod[] localMethods, List<VirtualMethod> vtable) {
    975             for (VirtualMethod virtualMethod: localMethods) {
    976                 boolean found = false;
    977                 for (int i=0; i<vtable.size(); i++) {
    978                     VirtualMethod superMethod = vtable.get(i);
    979                     if (superMethod.method.equals(virtualMethod.method)) {
    980                         if (!ClassPath.theClassPath.checkPackagePrivateAccess || this.canAccess(superMethod)) {
    981                             found = true;
    982                             vtable.set(i, virtualMethod);
    983                             break;
    984                         }
    985                     }
    986                 }
    987                 if (!found) {
    988                     vtable.add(virtualMethod);
    989                 }
    990             }
    991         }
    992 
    993         private boolean canAccess(VirtualMethod virtualMethod) {
    994             if (!virtualMethod.isPackagePrivate) {
    995                 return true;
    996             }
    997 
    998             String otherPackage = getPackage(virtualMethod.containingClass);
    999             String ourPackage = getPackage(this.classType);
   1000             return otherPackage.equals(ourPackage);
   1001         }
   1002 
   1003         private String getPackage(String classType) {
   1004             int lastSlash = classType.lastIndexOf('/');
   1005             if (lastSlash < 0) {
   1006                 return "";
   1007             }
   1008             return classType.substring(1, lastSlash);
   1009         }
   1010 
   1011         private int getNextFieldOffset() {
   1012             if (instanceFields == null || instanceFields.size() == 0) {
   1013                 return 8;
   1014             }
   1015 
   1016             int lastItemIndex = instanceFields.size()-1;
   1017             int fieldOffset = instanceFields.keyAt(lastItemIndex);
   1018             FieldDef lastField = instanceFields.valueAt(lastItemIndex);
   1019 
   1020             switch (lastField.type.charAt(0)) {
   1021                 case 'J':
   1022                 case 'D':
   1023                     return fieldOffset + 8;
   1024                 default:
   1025                     return fieldOffset + 4;
   1026             }
   1027         }
   1028 
   1029         private SparseArray<FieldDef> loadFields(UnresolvedClassInfo classInfo) {
   1030             //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
   1031             //arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
   1032             //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
   1033 
   1034             final byte REFERENCE = 0;
   1035             final byte WIDE = 1;
   1036             final byte OTHER = 2;
   1037 
   1038             FieldDef[] fields = null;
   1039             //the "type" for each field in fields. 0=reference,1=wide,2=other
   1040             byte[] fieldTypes = null;
   1041 
   1042             if (classInfo.instanceFields != null) {
   1043                 fields = new FieldDef[classInfo.instanceFields.length];
   1044                 fieldTypes = new byte[fields.length];
   1045 
   1046                 for (int i=0; i<fields.length; i++) {
   1047                     String[] fieldInfo = classInfo.instanceFields[i];
   1048 
   1049                     String fieldName = fieldInfo[0];
   1050                     String fieldType = fieldInfo[1];
   1051 
   1052                     fieldTypes[i] = getFieldType(fieldType);
   1053                     fields[i] = new FieldDef(classInfo.classType, fieldName, fieldType);
   1054                 }
   1055             }
   1056 
   1057             if (fields == null) {
   1058                 fields = new FieldDef[0];
   1059                 fieldTypes = new byte[0];
   1060             }
   1061 
   1062             //The first operation is to move all of the reference fields to the front. To do this, find the first
   1063             //non-reference field, then find the last reference field, swap them and repeat
   1064             int back = fields.length - 1;
   1065             int front;
   1066             for (front = 0; front<fields.length; front++) {
   1067                 if (fieldTypes[front] != REFERENCE) {
   1068                     while (back > front) {
   1069                         if (fieldTypes[back] == REFERENCE) {
   1070                             swap(fieldTypes, fields, front, back--);
   1071                             break;
   1072                         }
   1073                         back--;
   1074                     }
   1075                 }
   1076 
   1077                 if (fieldTypes[front] != REFERENCE) {
   1078                     break;
   1079                 }
   1080             }
   1081 
   1082 
   1083             int startFieldOffset = 8;
   1084             if (this.superclass != null) {
   1085                 startFieldOffset = this.superclass.getNextFieldOffset();
   1086             }
   1087 
   1088             int fieldIndexMod;
   1089             if ((startFieldOffset % 8) == 0) {
   1090                 fieldIndexMod = 0;
   1091             } else {
   1092                 fieldIndexMod = 1;
   1093             }
   1094 
   1095             //next, we need to group all the wide fields after the reference fields. But the wide fields have to be
   1096             //8-byte aligned. If we're on an odd field index, we need to insert a 32-bit field. If the next field
   1097             //is already a 32-bit field, use that. Otherwise, find the first 32-bit field from the end and swap it in.
   1098             //If there are no 32-bit fields, do nothing for now. We'll add padding when calculating the field offsets
   1099             if (front < fields.length && (front % 2) != fieldIndexMod) {
   1100                 if (fieldTypes[front] == WIDE) {
   1101                     //we need to swap in a 32-bit field, so the wide fields will be correctly aligned
   1102                     back = fields.length - 1;
   1103                     while (back > front) {
   1104                         if (fieldTypes[back] == OTHER) {
   1105                             swap(fieldTypes, fields, front++, back);
   1106                             break;
   1107                         }
   1108                         back--;
   1109                     }
   1110                 } else {
   1111                     //there's already a 32-bit field here that we can use
   1112                     front++;
   1113                 }
   1114             }
   1115 
   1116             //do the swap thing for wide fields
   1117             back = fields.length - 1;
   1118             for (; front<fields.length; front++) {
   1119                 if (fieldTypes[front] != WIDE) {
   1120                     while (back > front) {
   1121                         if (fieldTypes[back] == WIDE) {
   1122                             swap(fieldTypes, fields, front, back--);
   1123                             break;
   1124                         }
   1125                         back--;
   1126                     }
   1127                 }
   1128 
   1129                 if (fieldTypes[front] != WIDE) {
   1130                     break;
   1131                 }
   1132             }
   1133 
   1134             int superFieldCount = 0;
   1135             if (superclass != null) {
   1136                 superFieldCount = superclass.instanceFields.size();
   1137             }
   1138 
   1139             //now the fields are in the correct order. Add them to the SparseArray and lookup, and calculate the offsets
   1140             int totalFieldCount = superFieldCount + fields.length;
   1141             SparseArray<FieldDef> instanceFields = new SparseArray<FieldDef>(totalFieldCount);
   1142 
   1143             int fieldOffset;
   1144 
   1145             if (superclass != null && superFieldCount > 0) {
   1146                 for (int i=0; i<superFieldCount; i++) {
   1147                     instanceFields.append(superclass.instanceFields.keyAt(i), superclass.instanceFields.valueAt(i));
   1148                 }
   1149 
   1150                 fieldOffset = instanceFields.keyAt(superFieldCount-1);
   1151 
   1152                 FieldDef lastSuperField = superclass.instanceFields.valueAt(superFieldCount-1);
   1153                 char fieldType = lastSuperField.type.charAt(0);
   1154                 if (fieldType == 'J' || fieldType == 'D') {
   1155                     fieldOffset += 8;
   1156                 } else {
   1157                     fieldOffset += 4;
   1158                 }
   1159             } else {
   1160                 //the field values start at 8 bytes into the DataObject dalvik structure
   1161                 fieldOffset = 8;
   1162             }
   1163 
   1164             boolean gotDouble = false;
   1165             for (int i=0; i<fields.length; i++) {
   1166                 FieldDef field = fields[i];
   1167 
   1168                 //add padding to align the wide fields, if needed
   1169                 if (fieldTypes[i] == WIDE && !gotDouble) {
   1170                     if (!gotDouble) {
   1171                         if (fieldOffset % 8 != 0) {
   1172                             assert fieldOffset % 8 == 4;
   1173                             fieldOffset += 4;
   1174                         }
   1175                         gotDouble = true;
   1176                     }
   1177                 }
   1178 
   1179                 instanceFields.append(fieldOffset, field);
   1180                 if (fieldTypes[i] == WIDE) {
   1181                     fieldOffset += 8;
   1182                 } else {
   1183                     fieldOffset += 4;
   1184                 }
   1185             }
   1186             return instanceFields;
   1187         }
   1188 
   1189         private byte getFieldType(String fieldType) {
   1190             switch (fieldType.charAt(0)) {
   1191                 case '[':
   1192                 case 'L':
   1193                     return 0; //REFERENCE
   1194                 case 'J':
   1195                 case 'D':
   1196                     return 1; //WIDE
   1197                 default:
   1198                     return 2; //OTHER
   1199             }
   1200         }
   1201 
   1202         @Override
   1203         public boolean equals(Object o) {
   1204             if (this == o) return true;
   1205             if (!(o instanceof ClassDef)) return false;
   1206 
   1207             ClassDef classDef = (ClassDef) o;
   1208 
   1209             return classType.equals(classDef.classType);
   1210         }
   1211 
   1212         @Override
   1213         public int hashCode() {
   1214             return classType.hashCode();
   1215         }
   1216 
   1217         public int compareTo(ClassDef classDef) {
   1218             return classType.compareTo(classDef.classType);
   1219         }
   1220     }
   1221 
   1222     private static class VirtualMethod {
   1223         public String containingClass;
   1224         public String method;
   1225         public boolean isPackagePrivate;
   1226     }
   1227 
   1228     /**
   1229      * This aggregates the basic information about a class in an easy-to-use format, without requiring references
   1230      * to any other class.
   1231      */
   1232     private static class UnresolvedClassInfo {
   1233         public final String dexFilePath;
   1234         public final String classType;
   1235         public final boolean isPublic;
   1236         public final boolean isInterface;
   1237         public final String superclassType;
   1238         public final String[] interfaces;
   1239         public final boolean[] staticMethods;
   1240         public final String[] directMethods;
   1241         public final VirtualMethod[] virtualMethods;
   1242         public final String[][] instanceFields;
   1243 
   1244         public UnresolvedClassInfo(String dexFilePath, ClassDefItem classDefItem) {
   1245             this.dexFilePath = dexFilePath;
   1246 
   1247             classType = classDefItem.getClassType().getTypeDescriptor();
   1248 
   1249             isPublic = (classDefItem.getAccessFlags() & AccessFlags.PUBLIC.getValue()) != 0;
   1250             isInterface = (classDefItem.getAccessFlags() & AccessFlags.INTERFACE.getValue()) != 0;
   1251 
   1252             TypeIdItem superclassType = classDefItem.getSuperclass();
   1253             if (superclassType == null) {
   1254                 this.superclassType = null;
   1255             } else {
   1256                 this.superclassType = superclassType.getTypeDescriptor();
   1257             }
   1258 
   1259             interfaces = loadInterfaces(classDefItem);
   1260 
   1261             ClassDataItem classDataItem = classDefItem.getClassData();
   1262             if (classDataItem != null) {
   1263                 boolean[][] _staticMethods = new boolean[1][];
   1264                 directMethods = loadDirectMethods(classDataItem, _staticMethods);
   1265                 staticMethods = _staticMethods[0];
   1266                 virtualMethods = loadVirtualMethods(classDataItem);
   1267                 instanceFields = loadInstanceFields(classDataItem);
   1268             } else {
   1269                 staticMethods = null;
   1270                 directMethods = null;
   1271                 virtualMethods = null;
   1272                 instanceFields = null;
   1273             }
   1274         }
   1275 
   1276         private String[] loadInterfaces(ClassDefItem classDefItem) {
   1277             TypeListItem typeList = classDefItem.getInterfaces();
   1278             if (typeList != null) {
   1279                 List<TypeIdItem> types = typeList.getTypes();
   1280                 if (types != null && types.size() > 0) {
   1281                     String[] interfaces = new String[types.size()];
   1282                     for (int i=0; i<interfaces.length; i++) {
   1283                         interfaces[i] = types.get(i).getTypeDescriptor();
   1284                     }
   1285                     return interfaces;
   1286                 }
   1287             }
   1288             return null;
   1289         }
   1290 
   1291         private String[] loadDirectMethods(ClassDataItem classDataItem, boolean[][] _staticMethods) {
   1292             List<EncodedMethod> encodedMethods = classDataItem.getDirectMethods();
   1293             if (encodedMethods.size() > 0) {
   1294                 boolean[] staticMethods = new boolean[encodedMethods.size()];
   1295                 String[] directMethods = new String[encodedMethods.size()];
   1296 
   1297                 for (int i=0; i<encodedMethods.size(); i++) {
   1298                     EncodedMethod encodedMethod = encodedMethods.get(i);
   1299 
   1300                     if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) != 0) {
   1301                         staticMethods[i] = true;
   1302                     }
   1303                     directMethods[i] = encodedMethod.method.getShortMethodString();
   1304                 }
   1305                 _staticMethods[0] = staticMethods;
   1306                 return directMethods;
   1307             }
   1308             return null;
   1309         }
   1310 
   1311         private VirtualMethod[] loadVirtualMethods(ClassDataItem classDataItem) {
   1312             List<EncodedMethod> encodedMethods = classDataItem.getVirtualMethods();
   1313             if (encodedMethods.size() > 0) {
   1314                 VirtualMethod[] virtualMethods = new VirtualMethod[encodedMethods.size()];
   1315                 for (int i=0; i<encodedMethods.size(); i++) {
   1316                     virtualMethods[i] = new VirtualMethod();
   1317                     EncodedMethod encodedMethod = encodedMethods.get(i);
   1318 
   1319                     virtualMethods[i].isPackagePrivate = methodIsPackagePrivate(encodedMethod.accessFlags);
   1320                     virtualMethods[i].containingClass = classDataItem.getParentType().getTypeDescriptor();
   1321                     virtualMethods[i].method = encodedMethods.get(i).method.getShortMethodString();
   1322                 }
   1323                 return virtualMethods;
   1324             }
   1325             return null;
   1326         }
   1327 
   1328         private static boolean methodIsPackagePrivate(int accessFlags) {
   1329             return (accessFlags & (AccessFlags.PRIVATE.getValue() |
   1330                                    AccessFlags.PROTECTED.getValue() |
   1331                                    AccessFlags.PUBLIC.getValue())) == 0;
   1332         }
   1333 
   1334         private String[][] loadInstanceFields(ClassDataItem classDataItem) {
   1335             List<EncodedField> encodedFields = classDataItem.getInstanceFields();
   1336             if (encodedFields.size() > 0) {
   1337                 String[][] instanceFields = new String[encodedFields.size()][2];
   1338                 for (int i=0; i<encodedFields.size(); i++) {
   1339                     EncodedField encodedField = encodedFields.get(i);
   1340                     instanceFields[i][0] = encodedField.field.getFieldName().getStringValue();
   1341                     instanceFields[i][1] = encodedField.field.getFieldType().getTypeDescriptor();
   1342                 }
   1343                 return instanceFields;
   1344             }
   1345             return null;
   1346         }
   1347     }
   1348 }
   1349