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