• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.rop.cst.Constant;
21 import com.android.dx.rop.cst.ConstantPool;
22 import com.android.dx.rop.cst.CstFieldRef;
23 import com.android.dx.rop.cst.CstMethodRef;
24 import com.android.dx.rop.cst.CstType;
25 import com.android.dx.rop.type.Prototype;
26 import com.android.dx.rop.type.StdTypeList;
27 import com.android.dx.rop.type.Type;
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 
ClassReferenceListBuilder(Path path)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
main(String[] args)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      */
addRoots(ZipFile jarOfRoots)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 
91                 addDependencies(classFile.getConstantPool());
92             }
93         }
94     }
95 
getClassNames()96     Set<String> getClassNames() {
97         return classNames;
98     }
99 
addDependencies(ConstantPool pool)100     private void addDependencies(ConstantPool pool) {
101         for (Constant constant : pool.getEntries()) {
102             if (constant instanceof CstType) {
103                 checkDescriptor(((CstType) constant).getClassType());
104             } else if (constant instanceof CstFieldRef) {
105                 checkDescriptor(((CstFieldRef) constant).getType());
106             } else if (constant instanceof CstMethodRef) {
107                 Prototype proto = ((CstMethodRef) constant).getPrototype();
108                 checkDescriptor(proto.getReturnType());
109                 StdTypeList args = proto.getParameterTypes();
110                 for (int i = 0; i < args.size(); i++) {
111                     checkDescriptor(args.get(i));
112                 }
113             }
114         }
115     }
116 
checkDescriptor(Type type)117     private void checkDescriptor(Type type) {
118         String descriptor = type.getDescriptor();
119         if (descriptor.endsWith(";")) {
120             int lastBrace = descriptor.lastIndexOf('[');
121             if (lastBrace < 0) {
122                 addClassWithHierachy(descriptor.substring(1, descriptor.length()-1));
123             } else {
124                 assert descriptor.length() > lastBrace + 3
125                 && descriptor.charAt(lastBrace + 1) == 'L';
126                 addClassWithHierachy(descriptor.substring(lastBrace + 2,
127                         descriptor.length() - 1));
128             }
129         }
130     }
131 
addClassWithHierachy(String classBinaryName)132     private void addClassWithHierachy(String classBinaryName) {
133         if (classNames.contains(classBinaryName)) {
134             return;
135         }
136 
137         try {
138             DirectClassFile classFile = path.getClass(classBinaryName + CLASS_EXTENSION);
139             classNames.add(classBinaryName);
140             CstType superClass = classFile.getSuperclass();
141             if (superClass != null) {
142                 addClassWithHierachy(superClass.getClassType().getClassName());
143             }
144 
145             TypeList interfaceList = classFile.getInterfaces();
146             int interfaceNumber = interfaceList.size();
147             for (int i = 0; i < interfaceNumber; i++) {
148                 addClassWithHierachy(interfaceList.getType(i).getClassName());
149             }
150         } catch (FileNotFoundException e) {
151             // Ignore: The referenced type is not in the path it must be part of the libraries.
152         }
153     }
154 
155 }
156