Home | History | Annotate | Download | only in multidex
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.multidex;
     18 
     19 import com.android.dx.cf.direct.DirectClassFile;
     20 import com.android.dx.cf.iface.FieldList;
     21 import com.android.dx.cf.iface.MethodList;
     22 import com.android.dx.rop.cst.Constant;
     23 import com.android.dx.rop.cst.CstBaseMethodRef;
     24 import com.android.dx.rop.cst.CstFieldRef;
     25 import com.android.dx.rop.cst.CstType;
     26 import com.android.dx.rop.type.Prototype;
     27 import com.android.dx.rop.type.StdTypeList;
     28 import com.android.dx.rop.type.TypeList;
     29 
     30 import java.io.FileNotFoundException;
     31 import java.io.IOException;
     32 import java.util.Enumeration;
     33 import java.util.HashSet;
     34 import java.util.Set;
     35 import java.util.zip.ZipEntry;
     36 import java.util.zip.ZipFile;
     37 
     38 /**
     39  * Tool to find direct class references to other classes.
     40  */
     41 public class ClassReferenceListBuilder {
     42     private static final String CLASS_EXTENSION = ".class";
     43 
     44     private final Path path;
     45     private final Set<String> classNames = new HashSet<String>();
     46 
     47     public ClassReferenceListBuilder(Path path) {
     48         this.path = path;
     49     }
     50 
     51     /**
     52      * Kept for compatibility with the gradle integration, this method just forwards to
     53      * {@link MainDexListBuilder#main(String[])}.
     54      * @deprecated use {@link MainDexListBuilder#main(String[])} instead.
     55      */
     56     @Deprecated
     57     public static void main(String[] args) {
     58         MainDexListBuilder.main(args);
     59     }
     60 
     61     /**
     62      * @param jarOfRoots Archive containing the class files resulting of the tracing, typically
     63      * this is the result of running ProGuard.
     64      */
     65     public void addRoots(ZipFile jarOfRoots) throws IOException {
     66 
     67         // keep roots
     68         for (Enumeration<? extends ZipEntry> entries = jarOfRoots.entries();
     69                 entries.hasMoreElements();) {
     70             ZipEntry entry = entries.nextElement();
     71             String name = entry.getName();
     72             if (name.endsWith(CLASS_EXTENSION)) {
     73                 classNames.add(name.substring(0, name.length() - CLASS_EXTENSION.length()));
     74             }
     75         }
     76 
     77         // keep direct references of roots (+ direct references hierarchy)
     78         for (Enumeration<? extends ZipEntry> entries = jarOfRoots.entries();
     79                 entries.hasMoreElements();) {
     80             ZipEntry entry = entries.nextElement();
     81             String name = entry.getName();
     82             if (name.endsWith(CLASS_EXTENSION)) {
     83                 DirectClassFile classFile;
     84                 try {
     85                     classFile = path.getClass(name);
     86                 } catch (FileNotFoundException e) {
     87                     throw new IOException("Class " + name +
     88                             " is missing form original class path " + path, e);
     89                 }
     90                 addDependencies(classFile);
     91             }
     92         }
     93     }
     94 
     95     Set<String> getClassNames() {
     96         return classNames;
     97     }
     98 
     99     private void addDependencies(DirectClassFile classFile) {
    100         for (Constant constant : classFile.getConstantPool().getEntries()) {
    101             if (constant instanceof CstType) {
    102                 checkDescriptor(((CstType) constant).getClassType().getDescriptor());
    103             } else if (constant instanceof CstFieldRef) {
    104                 checkDescriptor(((CstFieldRef) constant).getType().getDescriptor());
    105             } else if (constant instanceof CstBaseMethodRef) {
    106                 checkPrototype(((CstBaseMethodRef) constant).getPrototype());
    107             }
    108         }
    109 
    110         FieldList fields = classFile.getFields();
    111         int nbField = fields.size();
    112         for (int i = 0; i < nbField; i++) {
    113           checkDescriptor(fields.get(i).getDescriptor().getString());
    114         }
    115 
    116         MethodList methods = classFile.getMethods();
    117         int nbMethods = methods.size();
    118         for (int i = 0; i < nbMethods; i++) {
    119           checkPrototype(Prototype.intern(methods.get(i).getDescriptor().getString()));
    120         }
    121     }
    122 
    123     private void checkPrototype(Prototype proto) {
    124       checkDescriptor(proto.getReturnType().getDescriptor());
    125       StdTypeList args = proto.getParameterTypes();
    126       for (int i = 0; i < args.size(); i++) {
    127           checkDescriptor(args.get(i).getDescriptor());
    128       }
    129     }
    130 
    131     private void checkDescriptor(String typeDescriptor) {
    132         if (typeDescriptor.endsWith(";")) {
    133             int lastBrace = typeDescriptor.lastIndexOf('[');
    134             if (lastBrace < 0) {
    135                 addClassWithHierachy(typeDescriptor.substring(1, typeDescriptor.length()-1));
    136             } else {
    137                 assert typeDescriptor.length() > lastBrace + 3
    138                 && typeDescriptor.charAt(lastBrace + 1) == 'L';
    139                 addClassWithHierachy(typeDescriptor.substring(lastBrace + 2,
    140                         typeDescriptor.length() - 1));
    141             }
    142         }
    143     }
    144 
    145     private void addClassWithHierachy(String classBinaryName) {
    146         if (classNames.contains(classBinaryName)) {
    147             return;
    148         }
    149 
    150         try {
    151             DirectClassFile classFile = path.getClass(classBinaryName + CLASS_EXTENSION);
    152             classNames.add(classBinaryName);
    153             CstType superClass = classFile.getSuperclass();
    154             if (superClass != null) {
    155                 addClassWithHierachy(superClass.getClassType().getClassName());
    156             }
    157 
    158             TypeList interfaceList = classFile.getInterfaces();
    159             int interfaceNumber = interfaceList.size();
    160             for (int i = 0; i < interfaceNumber; i++) {
    161                 addClassWithHierachy(interfaceList.getType(i).getClassName());
    162             }
    163         } catch (FileNotFoundException e) {
    164             // Ignore: The referenced type is not in the path it must be part of the libraries.
    165         }
    166     }
    167 
    168 }
    169