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