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 import java.io.FileNotFoundException; 30 import java.io.IOException; 31 import java.util.Enumeration; 32 import java.util.HashSet; 33 import java.util.Set; 34 import java.util.zip.ZipEntry; 35 import java.util.zip.ZipFile; 36 37 /** 38 * Tool to find direct class references to other classes. 39 */ 40 public class ClassReferenceListBuilder { 41 private static final String CLASS_EXTENSION = ".class"; 42 43 private final Path path; 44 private final Set<String> classNames = new HashSet<String>(); 45 ClassReferenceListBuilder(Path path)46 public ClassReferenceListBuilder(Path path) { 47 this.path = path; 48 } 49 50 /** 51 * Kept for compatibility with the gradle integration, this method just forwards to 52 * {@link MainDexListBuilder#main(String[])}. 53 * @deprecated use {@link MainDexListBuilder#main(String[])} instead. 54 */ 55 @Deprecated main(String[] args)56 public static void main(String[] args) { 57 MainDexListBuilder.main(args); 58 } 59 60 /** 61 * @param jarOfRoots Archive containing the class files resulting of the tracing, typically 62 * this is the result of running ProGuard. 63 */ addRoots(ZipFile jarOfRoots)64 public void addRoots(ZipFile jarOfRoots) throws IOException { 65 66 // keep roots 67 for (Enumeration<? extends ZipEntry> entries = jarOfRoots.entries(); 68 entries.hasMoreElements();) { 69 ZipEntry entry = entries.nextElement(); 70 String name = entry.getName(); 71 if (name.endsWith(CLASS_EXTENSION)) { 72 classNames.add(name.substring(0, name.length() - CLASS_EXTENSION.length())); 73 } 74 } 75 76 // keep direct references of roots (+ direct references hierarchy) 77 for (Enumeration<? extends ZipEntry> entries = jarOfRoots.entries(); 78 entries.hasMoreElements();) { 79 ZipEntry entry = entries.nextElement(); 80 String name = entry.getName(); 81 if (name.endsWith(CLASS_EXTENSION)) { 82 DirectClassFile classFile; 83 try { 84 classFile = path.getClass(name); 85 } catch (FileNotFoundException e) { 86 throw new IOException("Class " + name + 87 " is missing form original class path " + path, e); 88 } 89 addDependencies(classFile); 90 } 91 } 92 } 93 getClassNames()94 Set<String> getClassNames() { 95 return classNames; 96 } 97 addDependencies(DirectClassFile classFile)98 private void addDependencies(DirectClassFile classFile) { 99 for (Constant constant : classFile.getConstantPool().getEntries()) { 100 if (constant instanceof CstType) { 101 checkDescriptor(((CstType) constant).getClassType().getDescriptor()); 102 } else if (constant instanceof CstFieldRef) { 103 checkDescriptor(((CstFieldRef) constant).getType().getDescriptor()); 104 } else if (constant instanceof CstBaseMethodRef) { 105 checkPrototype(((CstBaseMethodRef) constant).getPrototype()); 106 } 107 } 108 109 FieldList fields = classFile.getFields(); 110 int nbField = fields.size(); 111 for (int i = 0; i < nbField; i++) { 112 checkDescriptor(fields.get(i).getDescriptor().getString()); 113 } 114 115 MethodList methods = classFile.getMethods(); 116 int nbMethods = methods.size(); 117 for (int i = 0; i < nbMethods; i++) { 118 checkPrototype(Prototype.intern(methods.get(i).getDescriptor().getString())); 119 } 120 } 121 checkPrototype(Prototype proto)122 private void checkPrototype(Prototype proto) { 123 checkDescriptor(proto.getReturnType().getDescriptor()); 124 StdTypeList args = proto.getParameterTypes(); 125 for (int i = 0; i < args.size(); i++) { 126 checkDescriptor(args.get(i).getDescriptor()); 127 } 128 } 129 checkDescriptor(String typeDescriptor)130 private void checkDescriptor(String typeDescriptor) { 131 if (typeDescriptor.endsWith(";")) { 132 int lastBrace = typeDescriptor.lastIndexOf('['); 133 if (lastBrace < 0) { 134 addClassWithHierachy(typeDescriptor.substring(1, typeDescriptor.length()-1)); 135 } else { 136 assert typeDescriptor.length() > lastBrace + 3 137 && typeDescriptor.charAt(lastBrace + 1) == 'L'; 138 addClassWithHierachy(typeDescriptor.substring(lastBrace + 2, 139 typeDescriptor.length() - 1)); 140 } 141 } 142 } 143 addClassWithHierachy(String classBinaryName)144 private void addClassWithHierachy(String classBinaryName) { 145 if (classNames.contains(classBinaryName)) { 146 return; 147 } 148 149 try { 150 DirectClassFile classFile = path.getClass(classBinaryName + CLASS_EXTENSION); 151 classNames.add(classBinaryName); 152 CstType superClass = classFile.getSuperclass(); 153 if (superClass != null) { 154 addClassWithHierachy(superClass.getClassType().getClassName()); 155 } 156 157 TypeList interfaceList = classFile.getInterfaces(); 158 int interfaceNumber = interfaceList.size(); 159 for (int i = 0; i < interfaceNumber; i++) { 160 addClassWithHierachy(interfaceList.getType(i).getClassName()); 161 } 162 } catch (FileNotFoundException e) { 163 // Ignore: The referenced type is not in the path it must be part of the libraries. 164 } 165 } 166 167 } 168