Initializer.java revision b72c5c2e5482cf10117b2b25f642f7616b2326c3
1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard; 22 23import proguard.classfile.ClassPool; 24import proguard.classfile.attribute.visitor.AllAttributeVisitor; 25import proguard.classfile.constant.visitor.*; 26import proguard.classfile.instruction.visitor.AllInstructionVisitor; 27import proguard.classfile.util.*; 28import proguard.classfile.visitor.*; 29import proguard.util.*; 30 31import java.io.IOException; 32import java.util.*; 33 34/** 35 * This class initializes class pools. 36 * 37 * @author Eric Lafortune 38 */ 39public class Initializer 40{ 41 private final Configuration configuration; 42 43 44 /** 45 * Creates a new Initializer to initialize classes according to the given 46 * configuration. 47 */ 48 public Initializer(Configuration configuration) 49 { 50 this.configuration = configuration; 51 } 52 53 54 /** 55 * Initializes the classes in the given program class pool and library class 56 * pool, performs some basic checks, and shrinks the library class pool. 57 */ 58 public void execute(ClassPool programClassPool, 59 ClassPool libraryClassPool) throws IOException 60 { 61 int originalLibraryClassPoolSize = libraryClassPool.size(); 62 63 // Construct a reduced library class pool with only those library 64 // classes whose hierarchies are referenced by the program classes. 65 // We can't do this if we later have to come up with the obfuscated 66 // class member names that are globally unique. 67 ClassPool reducedLibraryClassPool = configuration.useUniqueClassMemberNames ? 68 null : new ClassPool(); 69 70 WarningPrinter classReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); 71 WarningPrinter dependencyWarningPrinter = new WarningPrinter(System.err, configuration.warn); 72 73 // Initialize the superclass hierarchies for program classes. 74 programClassPool.classesAccept( 75 new ClassSuperHierarchyInitializer(programClassPool, 76 libraryClassPool, 77 classReferenceWarningPrinter, 78 null)); 79 80 // Initialize the superclass hierarchy of all library classes, without 81 // warnings. 82 libraryClassPool.classesAccept( 83 new ClassSuperHierarchyInitializer(programClassPool, 84 libraryClassPool, 85 null, 86 dependencyWarningPrinter)); 87 88 // Initialize the class references of program class members and 89 // attributes. Note that all superclass hierarchies have to be 90 // initialized for this purpose. 91 WarningPrinter memberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); 92 93 programClassPool.classesAccept( 94 new ClassReferenceInitializer(programClassPool, 95 libraryClassPool, 96 classReferenceWarningPrinter, 97 memberReferenceWarningPrinter, 98 null)); 99 100 if (reducedLibraryClassPool != null) 101 { 102 // Collect the library classes that are directly referenced by 103 // program classes, without introspection. 104 programClassPool.classesAccept( 105 new ReferencedClassVisitor( 106 new LibraryClassFilter( 107 new ClassPoolFiller(reducedLibraryClassPool)))); 108 109 // Reinitialize the superclass hierarchies of referenced library 110 // classes, this time with warnings. 111 reducedLibraryClassPool.classesAccept( 112 new ClassSuperHierarchyInitializer(programClassPool, 113 libraryClassPool, 114 classReferenceWarningPrinter, 115 null)); 116 } 117 118 // Initialize the Class.forName references. 119 WarningPrinter dynamicClassReferenceNotePrinter = new WarningPrinter(System.out, configuration.note); 120 WarningPrinter classForNameNotePrinter = new WarningPrinter(System.out, configuration.note); 121 122 programClassPool.classesAccept( 123 new AllMethodVisitor( 124 new AllAttributeVisitor( 125 new AllInstructionVisitor( 126 new DynamicClassReferenceInitializer(programClassPool, 127 libraryClassPool, 128 dynamicClassReferenceNotePrinter, 129 null, 130 classForNameNotePrinter, 131 createClassNoteExceptionMatcher(configuration.keep)))))); 132 133 // Initialize the Class.get[Declared]{Field,Method} references. 134 WarningPrinter getMemberNotePrinter = new WarningPrinter(System.out, configuration.note); 135 136 programClassPool.classesAccept( 137 new AllMethodVisitor( 138 new AllAttributeVisitor( 139 new AllInstructionVisitor( 140 new DynamicMemberReferenceInitializer(programClassPool, 141 libraryClassPool, 142 getMemberNotePrinter, 143 createClassMemberNoteExceptionMatcher(configuration.keep, true), 144 createClassMemberNoteExceptionMatcher(configuration.keep, false)))))); 145 146 // Initialize other string constant references, if requested. 147 if (configuration.adaptClassStrings != null) 148 { 149 programClassPool.classesAccept( 150 new ClassNameFilter(configuration.adaptClassStrings, 151 new AllConstantVisitor( 152 new StringReferenceInitializer(programClassPool, 153 libraryClassPool)))); 154 } 155 156 // Print various notes, if specified. 157 WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningPrinter(System.out, configuration.note); 158 WarningPrinter descriptorKeepNotePrinter = new WarningPrinter(System.out, configuration.note); 159 160 new FullyQualifiedClassNameChecker(programClassPool, 161 libraryClassPool, 162 fullyQualifiedClassNameNotePrinter).checkClassSpecifications(configuration.keep); 163 164 new DescriptorKeepChecker(programClassPool, 165 libraryClassPool, 166 descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep); 167 168 // Initialize the class references of library class members. 169 if (reducedLibraryClassPool != null) 170 { 171 // Collect the library classes that are referenced by program 172 // classes, directly or indirectly, with or without introspection. 173 programClassPool.classesAccept( 174 new ReferencedClassVisitor( 175 new LibraryClassFilter( 176 new ClassHierarchyTraveler(true, true, true, false, 177 new LibraryClassFilter( 178 new ClassPoolFiller(reducedLibraryClassPool)))))); 179 180 // Initialize the class references of referenced library 181 // classes, without warnings. 182 reducedLibraryClassPool.classesAccept( 183 new ClassReferenceInitializer(programClassPool, 184 libraryClassPool, 185 null, 186 null, 187 dependencyWarningPrinter)); 188 189 // Reset the library class pool. 190 libraryClassPool.clear(); 191 192 // Copy the library classes that are referenced directly by program 193 // classes and the library classes that are referenced by referenced 194 // library classes. 195 reducedLibraryClassPool.classesAccept( 196 new MultiClassVisitor(new ClassVisitor[] 197 { 198 new ClassHierarchyTraveler(true, true, true, false, 199 new LibraryClassFilter( 200 new ClassPoolFiller(libraryClassPool))), 201 202 new ReferencedClassVisitor( 203 new LibraryClassFilter( 204 new ClassHierarchyTraveler(true, true, true, false, 205 new LibraryClassFilter( 206 new ClassPoolFiller(libraryClassPool))))) 207 })); 208 } 209 else 210 { 211 // Initialize the class references of all library class members. 212 libraryClassPool.classesAccept( 213 new ClassReferenceInitializer(programClassPool, 214 libraryClassPool, 215 null, 216 null, 217 dependencyWarningPrinter)); 218 } 219 220 // Initialize the subclass hierarchies. 221 programClassPool.classesAccept(new ClassSubHierarchyInitializer()); 222 libraryClassPool.classesAccept(new ClassSubHierarchyInitializer()); 223 224 // Share strings between the classes, to reduce heap memory usage. 225 programClassPool.classesAccept(new StringSharer()); 226 libraryClassPool.classesAccept(new StringSharer()); 227 228 // Print out a summary of the notes, if necessary. 229 int fullyQualifiedNoteCount = fullyQualifiedClassNameNotePrinter.getWarningCount(); 230 if (fullyQualifiedNoteCount > 0) 231 { 232 System.out.println("Note: there were " + fullyQualifiedNoteCount + 233 " references to unknown classes."); 234 System.out.println(" You should check your configuration for typos."); 235 } 236 237 int descriptorNoteCount = descriptorKeepNotePrinter.getWarningCount(); 238 if (descriptorNoteCount > 0) 239 { 240 System.out.println("Note: there were " + descriptorNoteCount + 241 " unkept descriptor classes in kept class members."); 242 System.out.println(" You should consider explicitly keeping the mentioned classes"); 243 System.out.println(" (using '-keep')."); 244 } 245 246 int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount(); 247 if (dynamicClassReferenceNoteCount > 0) 248 { 249 System.out.println("Note: there were " + dynamicClassReferenceNoteCount + 250 " unresolved dynamic references to classes or interfaces."); 251 System.err.println(" You should check if you need to specify additional program jars."); 252 } 253 254 int classForNameNoteCount = classForNameNotePrinter.getWarningCount(); 255 if (classForNameNoteCount > 0) 256 { 257 System.out.println("Note: there were " + classForNameNoteCount + 258 " class casts of dynamically created class instances."); 259 System.out.println(" You might consider explicitly keeping the mentioned classes and/or"); 260 System.out.println(" their implementations (using '-keep')."); 261 } 262 263 int getmemberNoteCount = getMemberNotePrinter.getWarningCount(); 264 if (getmemberNoteCount > 0) 265 { 266 System.out.println("Note: there were " + getmemberNoteCount + 267 " accesses to class members by means of introspection."); 268 System.out.println(" You should consider explicitly keeping the mentioned class members"); 269 System.out.println(" (using '-keep' or '-keepclassmembers')."); 270 } 271 272 // Print out a summary of the warnings, if necessary. 273 int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount(); 274 if (classReferenceWarningCount > 0) 275 { 276 System.err.println("Warning: there were " + classReferenceWarningCount + 277 " unresolved references to classes or interfaces."); 278 System.err.println(" You may need to specify additional library jars (using '-libraryjars'),"); 279 System.err.println(" or perhaps the '-dontskipnonpubliclibraryclasses' option."); 280 } 281 282 int dependencyWarningCount = dependencyWarningPrinter.getWarningCount(); 283 if (dependencyWarningCount > 0) 284 { 285 System.err.println("Warning: there were " + dependencyWarningCount + 286 " instances of library classes depending on program classes."); 287 System.err.println(" You must avoid such dependencies, since the program classes will"); 288 System.err.println(" be processed, while the library classes will remain unchanged."); 289 } 290 291 int memberReferenceWarningCount = memberReferenceWarningPrinter.getWarningCount(); 292 if (memberReferenceWarningCount > 0) 293 { 294 System.err.println("Warning: there were " + memberReferenceWarningCount + 295 " unresolved references to program class members."); 296 System.err.println(" Your input classes appear to be inconsistent."); 297 System.err.println(" You may need to recompile them and try again."); 298 System.err.println(" Alternatively, you may have to specify the options "); 299 System.err.println(" '-dontskipnonpubliclibraryclasses' and/or"); 300 System.err.println(" '-dontskipnonpubliclibraryclassmembers'."); 301 } 302 303 if ((classReferenceWarningCount > 0 || 304 dependencyWarningCount > 0 || 305 memberReferenceWarningCount > 0) && 306 !configuration.ignoreWarnings) 307 { 308 throw new IOException("Please correct the above warnings first."); 309 } 310 311 if ((configuration.note == null || 312 !configuration.note.isEmpty()) && 313 (configuration.warn != null && 314 configuration.warn.isEmpty() || 315 configuration.ignoreWarnings)) 316 { 317 System.out.println("Note: You're ignoring all warnings!"); 318 } 319 320 // Discard unused library classes. 321 if (configuration.verbose) 322 { 323 System.out.println("Ignoring unused library classes..."); 324 System.out.println(" Original number of library classes: " + originalLibraryClassPoolSize); 325 System.out.println(" Final number of library classes: " + libraryClassPool.size()); 326 } 327 } 328 329 330 /** 331 * Extracts a list of exceptions of classes for which not to print notes, 332 * from the keep configuration. 333 */ 334 private StringMatcher createClassNoteExceptionMatcher(List noteExceptions) 335 { 336 if (noteExceptions != null) 337 { 338 List noteExceptionNames = new ArrayList(noteExceptions.size()); 339 for (int index = 0; index < noteExceptions.size(); index++) 340 { 341 KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); 342 if (keepClassSpecification.markClasses) 343 { 344 // If the class itself is being kept, it's ok. 345 String className = keepClassSpecification.className; 346 if (className != null) 347 { 348 noteExceptionNames.add(className); 349 } 350 351 // If all of its extensions are being kept, it's ok too. 352 String extendsClassName = keepClassSpecification.extendsClassName; 353 if (extendsClassName != null) 354 { 355 noteExceptionNames.add(extendsClassName); 356 } 357 } 358 } 359 360 if (noteExceptionNames.size() > 0) 361 { 362 return new ListParser(new ClassNameParser()).parse(noteExceptionNames); 363 } 364 } 365 366 return null; 367 } 368 369 370 /** 371 * Extracts a list of exceptions of field or method names for which not to 372 * print notes, from the keep configuration. 373 */ 374 private StringMatcher createClassMemberNoteExceptionMatcher(List noteExceptions, 375 boolean isField) 376 { 377 if (noteExceptions != null) 378 { 379 List noteExceptionNames = new ArrayList(); 380 for (int index = 0; index < noteExceptions.size(); index++) 381 { 382 KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); 383 List memberSpecifications = isField ? 384 keepClassSpecification.fieldSpecifications : 385 keepClassSpecification.methodSpecifications; 386 387 if (memberSpecifications != null) 388 { 389 for (int index2 = 0; index2 < memberSpecifications.size(); index2++) 390 { 391 MemberSpecification memberSpecification = 392 (MemberSpecification)memberSpecifications.get(index2); 393 394 String memberName = memberSpecification.name; 395 if (memberName != null) 396 { 397 noteExceptionNames.add(memberName); 398 } 399 } 400 } 401 } 402 403 if (noteExceptionNames.size() > 0) 404 { 405 return new ListParser(new ClassNameParser()).parse(noteExceptionNames); 406 } 407 } 408 409 return null; 410 } 411} 412