1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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.*; 24import proguard.classfile.attribute.annotation.visitor.AllElementValueVisitor; 25import proguard.classfile.attribute.visitor.AllAttributeVisitor; 26import proguard.classfile.constant.visitor.AllConstantVisitor; 27import proguard.classfile.instruction.visitor.AllInstructionVisitor; 28import proguard.classfile.util.*; 29import proguard.classfile.visitor.*; 30import proguard.util.*; 31 32import java.io.IOException; 33import java.util.*; 34 35/** 36 * This class initializes class pools. 37 * 38 * @author Eric Lafortune 39 */ 40public class Initializer 41{ 42 private final Configuration configuration; 43 44 45 /** 46 * Creates a new Initializer to initialize classes according to the given 47 * configuration. 48 */ 49 public Initializer(Configuration configuration) 50 { 51 this.configuration = configuration; 52 } 53 54 55 /** 56 * Initializes the classes in the given program class pool and library class 57 * pool, performs some basic checks, and shrinks the library class pool. 58 */ 59 public void execute(ClassPool programClassPool, 60 ClassPool libraryClassPool) throws IOException 61 { 62 int originalLibraryClassPoolSize = libraryClassPool.size(); 63 64 // Perform basic checks on the configuration. 65 WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningPrinter(System.out, configuration.note); 66 67 FullyQualifiedClassNameChecker fullyQualifiedClassNameChecker = 68 new FullyQualifiedClassNameChecker(programClassPool, 69 libraryClassPool, 70 fullyQualifiedClassNameNotePrinter); 71 72 fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.keep); 73 fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoSideEffects); 74 75 StringMatcher keepAttributesMatcher = configuration.keepAttributes != null ? 76 new ListParser(new NameParser()).parse(configuration.keepAttributes) : 77 new EmptyStringMatcher(); 78 79 WarningPrinter getAnnotationNotePrinter = new WarningPrinter(System.out, configuration.note); 80 81 if (!keepAttributesMatcher.matches(ClassConstants.ATTR_RuntimeVisibleAnnotations)) 82 { 83 programClassPool.classesAccept( 84 new AllConstantVisitor( 85 new GetAnnotationChecker(getAnnotationNotePrinter))); 86 } 87 88 WarningPrinter getSignatureNotePrinter = new WarningPrinter(System.out, configuration.note); 89 90 if (!keepAttributesMatcher.matches(ClassConstants.ATTR_Signature)) 91 { 92 programClassPool.classesAccept( 93 new AllConstantVisitor( 94 new GetSignatureChecker(getSignatureNotePrinter))); 95 } 96 97 WarningPrinter getEnclosingClassNotePrinter = new WarningPrinter(System.out, configuration.note); 98 99 if (!keepAttributesMatcher.matches(ClassConstants.ATTR_InnerClasses)) 100 { 101 programClassPool.classesAccept( 102 new AllConstantVisitor( 103 new GetEnclosingClassChecker(getEnclosingClassNotePrinter))); 104 } 105 106 WarningPrinter getEnclosingMethodNotePrinter = new WarningPrinter(System.out, configuration.note); 107 108 if (!keepAttributesMatcher.matches(ClassConstants.ATTR_EnclosingMethod)) 109 { 110 programClassPool.classesAccept( 111 new AllConstantVisitor( 112 new GetEnclosingMethodChecker(getEnclosingMethodNotePrinter))); 113 } 114 115 // Construct a reduced library class pool with only those library 116 // classes whose hierarchies are referenced by the program classes. 117 // We can't do this if we later have to come up with the obfuscated 118 // class member names that are globally unique. 119 ClassPool reducedLibraryClassPool = configuration.useUniqueClassMemberNames ? 120 null : new ClassPool(); 121 122 WarningPrinter classReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); 123 WarningPrinter dependencyWarningPrinter = new WarningPrinter(System.err, configuration.warn); 124 125 // Initialize the superclass hierarchies for program classes. 126 programClassPool.classesAccept( 127 new ClassSuperHierarchyInitializer(programClassPool, 128 libraryClassPool, 129 classReferenceWarningPrinter, 130 null)); 131 132 // Initialize the superclass hierarchy of all library classes, without 133 // warnings. 134 libraryClassPool.classesAccept( 135 new ClassSuperHierarchyInitializer(programClassPool, 136 libraryClassPool, 137 null, 138 dependencyWarningPrinter)); 139 140 // Initialize the class references of program class members and 141 // attributes. Note that all superclass hierarchies have to be 142 // initialized for this purpose. 143 WarningPrinter programMemberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); 144 WarningPrinter libraryMemberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); 145 146 programClassPool.classesAccept( 147 new ClassReferenceInitializer(programClassPool, 148 libraryClassPool, 149 classReferenceWarningPrinter, 150 programMemberReferenceWarningPrinter, 151 libraryMemberReferenceWarningPrinter, 152 null)); 153 154 if (reducedLibraryClassPool != null) 155 { 156 // Collect the library classes that are directly referenced by 157 // program classes, without introspection. 158 programClassPool.classesAccept( 159 new ReferencedClassVisitor( 160 new LibraryClassFilter( 161 new ClassPoolFiller(reducedLibraryClassPool)))); 162 163 // Reinitialize the superclass hierarchies of referenced library 164 // classes, this time with warnings. 165 reducedLibraryClassPool.classesAccept( 166 new ClassSuperHierarchyInitializer(programClassPool, 167 libraryClassPool, 168 classReferenceWarningPrinter, 169 null)); 170 } 171 172 // Initialize the enum annotation references. 173 programClassPool.classesAccept( 174 new AllAttributeVisitor(true, 175 new AllElementValueVisitor(true, 176 new EnumFieldReferenceInitializer()))); 177 178 // Initialize the Class.forName references. 179 WarningPrinter dynamicClassReferenceNotePrinter = new WarningPrinter(System.out, configuration.note); 180 WarningPrinter classForNameNotePrinter = new WarningPrinter(System.out, configuration.note); 181 182 programClassPool.classesAccept( 183 new AllMethodVisitor( 184 new AllAttributeVisitor( 185 new AllInstructionVisitor( 186 new DynamicClassReferenceInitializer(programClassPool, 187 libraryClassPool, 188 dynamicClassReferenceNotePrinter, 189 null, 190 classForNameNotePrinter, 191 createClassNoteExceptionMatcher(configuration.keep)))))); 192 193 // Initialize the Class.get[Declared]{Field,Method} references. 194 WarningPrinter getMemberNotePrinter = new WarningPrinter(System.out, configuration.note); 195 196 programClassPool.classesAccept( 197 new AllMethodVisitor( 198 new AllAttributeVisitor( 199 new AllInstructionVisitor( 200 new DynamicMemberReferenceInitializer(programClassPool, 201 libraryClassPool, 202 getMemberNotePrinter, 203 createClassMemberNoteExceptionMatcher(configuration.keep, true), 204 createClassMemberNoteExceptionMatcher(configuration.keep, false)))))); 205 206 // Initialize other string constant references, if requested. 207 if (configuration.adaptClassStrings != null) 208 { 209 programClassPool.classesAccept( 210 new ClassNameFilter(configuration.adaptClassStrings, 211 new AllConstantVisitor( 212 new StringReferenceInitializer(programClassPool, 213 libraryClassPool)))); 214 } 215 216 // Initialize the class references of library class members. 217 if (reducedLibraryClassPool != null) 218 { 219 // Collect the library classes that are referenced by program 220 // classes, directly or indirectly, with or without reflection. 221 programClassPool.classesAccept( 222 new ReferencedClassVisitor( 223 new LibraryClassFilter( 224 new ClassHierarchyTraveler(true, true, true, false, 225 new LibraryClassFilter( 226 new ClassPoolFiller(reducedLibraryClassPool)))))); 227 228 // Initialize the class references of referenced library 229 // classes, without warnings. 230 reducedLibraryClassPool.classesAccept( 231 new ClassReferenceInitializer(programClassPool, 232 libraryClassPool, 233 null, 234 null, 235 null, 236 dependencyWarningPrinter)); 237 238 // Reset the library class pool. 239 libraryClassPool.clear(); 240 241 // Copy the library classes that are referenced directly by program 242 // classes and the library classes that are referenced by referenced 243 // library classes. 244 reducedLibraryClassPool.classesAccept( 245 new MultiClassVisitor(new ClassVisitor[] 246 { 247 new ClassHierarchyTraveler(true, true, true, false, 248 new LibraryClassFilter( 249 new ClassPoolFiller(libraryClassPool))), 250 251 new ReferencedClassVisitor( 252 new LibraryClassFilter( 253 new ClassHierarchyTraveler(true, true, true, false, 254 new LibraryClassFilter( 255 new ClassPoolFiller(libraryClassPool))))) 256 })); 257 } 258 else 259 { 260 // Initialize the class references of all library class members. 261 libraryClassPool.classesAccept( 262 new ClassReferenceInitializer(programClassPool, 263 libraryClassPool, 264 null, 265 null, 266 null, 267 dependencyWarningPrinter)); 268 } 269 270 // Initialize the subclass hierarchies. 271 programClassPool.classesAccept(new ClassSubHierarchyInitializer()); 272 libraryClassPool.classesAccept(new ClassSubHierarchyInitializer()); 273 274 // Share strings between the classes, to reduce heap memory usage. 275 programClassPool.classesAccept(new StringSharer()); 276 libraryClassPool.classesAccept(new StringSharer()); 277 278 // Check for any unmatched class members. 279 WarningPrinter classMemberNotePrinter = new WarningPrinter(System.out, configuration.note); 280 281 ClassMemberChecker classMemberChecker = 282 new ClassMemberChecker(programClassPool, 283 classMemberNotePrinter); 284 285 classMemberChecker.checkClassSpecifications(configuration.keep); 286 classMemberChecker.checkClassSpecifications(configuration.assumeNoSideEffects); 287 288 // Check for unkept descriptor classes of kept class members. 289 WarningPrinter descriptorKeepNotePrinter = new WarningPrinter(System.out, configuration.note); 290 291 new DescriptorKeepChecker(programClassPool, 292 libraryClassPool, 293 descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep); 294 295 // Check for keep options that only match library classes. 296 WarningPrinter libraryKeepNotePrinter = new WarningPrinter(System.out, configuration.note); 297 298 new LibraryKeepChecker(programClassPool, 299 libraryClassPool, 300 libraryKeepNotePrinter).checkClassSpecifications(configuration.keep); 301 302 // Print out a summary of the notes, if necessary. 303 int fullyQualifiedNoteCount = fullyQualifiedClassNameNotePrinter.getWarningCount(); 304 if (fullyQualifiedNoteCount > 0) 305 { 306 System.out.println("Note: there were " + fullyQualifiedNoteCount + 307 " references to unknown classes."); 308 System.out.println(" You should check your configuration for typos."); 309 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass)"); 310 } 311 312 int classMemberNoteCount = classMemberNotePrinter.getWarningCount(); 313 if (classMemberNoteCount > 0) 314 { 315 System.out.println("Note: there were " + classMemberNoteCount + 316 " references to unknown class members."); 317 System.out.println(" You should check your configuration for typos."); 318 } 319 320 int getAnnotationNoteCount = getAnnotationNotePrinter.getWarningCount(); 321 if (getAnnotationNoteCount > 0) 322 { 323 System.out.println("Note: there were " + getAnnotationNoteCount + 324 " classes trying to access annotations using reflection."); 325 System.out.println(" You should consider keeping the annotation attributes"); 326 System.out.println(" (using '-keepattributes *Annotation*')."); 327 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); 328 } 329 330 int getSignatureNoteCount = getSignatureNotePrinter.getWarningCount(); 331 if (getSignatureNoteCount > 0) 332 { 333 System.out.println("Note: there were " + getSignatureNoteCount + 334 " classes trying to access generic signatures using reflection."); 335 System.out.println(" You should consider keeping the signature attributes"); 336 System.out.println(" (using '-keepattributes Signature')."); 337 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); 338 } 339 340 int getEnclosingClassNoteCount = getEnclosingClassNotePrinter.getWarningCount(); 341 if (getEnclosingClassNoteCount > 0) 342 { 343 System.out.println("Note: there were " + getEnclosingClassNoteCount + 344 " classes trying to access enclosing classes using reflection."); 345 System.out.println(" You should consider keeping the inner classes attributes"); 346 System.out.println(" (using '-keepattributes InnerClasses')."); 347 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); 348 } 349 350 int getEnclosingMethodNoteCount = getEnclosingMethodNotePrinter.getWarningCount(); 351 if (getEnclosingMethodNoteCount > 0) 352 { 353 System.out.println("Note: there were " + getEnclosingMethodNoteCount + 354 " classes trying to access enclosing methods using reflection."); 355 System.out.println(" You should consider keeping the enclosing method attributes"); 356 System.out.println(" (using '-keepattributes InnerClasses,EnclosingMethod')."); 357 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); 358 } 359 360 int descriptorNoteCount = descriptorKeepNotePrinter.getWarningCount(); 361 if (descriptorNoteCount > 0) 362 { 363 System.out.println("Note: there were " + descriptorNoteCount + 364 " unkept descriptor classes in kept class members."); 365 System.out.println(" You should consider explicitly keeping the mentioned classes"); 366 System.out.println(" (using '-keep')."); 367 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#descriptorclass)"); 368 } 369 370 int libraryNoteCount = libraryKeepNotePrinter.getWarningCount(); 371 if (libraryNoteCount > 0) 372 { 373 System.out.println("Note: there were " + libraryNoteCount + 374 " library classes explicitly being kept."); 375 System.out.println(" You don't need to keep library classes; they are already left unchanged."); 376 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#libraryclass)"); 377 } 378 379 int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount(); 380 if (dynamicClassReferenceNoteCount > 0) 381 { 382 System.out.println("Note: there were " + dynamicClassReferenceNoteCount + 383 " unresolved dynamic references to classes or interfaces."); 384 System.out.println(" You should check if you need to specify additional program jars."); 385 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclass)"); 386 } 387 388 int classForNameNoteCount = classForNameNotePrinter.getWarningCount(); 389 if (classForNameNoteCount > 0) 390 { 391 System.out.println("Note: there were " + classForNameNoteCount + 392 " class casts of dynamically created class instances."); 393 System.out.println(" You might consider explicitly keeping the mentioned classes and/or"); 394 System.out.println(" their implementations (using '-keep')."); 395 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclasscast)"); 396 } 397 398 int getmemberNoteCount = getMemberNotePrinter.getWarningCount(); 399 if (getmemberNoteCount > 0) 400 { 401 System.out.println("Note: there were " + getmemberNoteCount + 402 " accesses to class members by means of introspection."); 403 System.out.println(" You should consider explicitly keeping the mentioned class members"); 404 System.out.println(" (using '-keep' or '-keepclassmembers')."); 405 System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclassmember)"); 406 } 407 408 // Print out a summary of the warnings, if necessary. 409 int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount(); 410 if (classReferenceWarningCount > 0) 411 { 412 System.err.println("Warning: there were " + classReferenceWarningCount + 413 " unresolved references to classes or interfaces."); 414 System.err.println(" You may need to add missing library jars or update their versions."); 415 System.err.println(" If your code works fine without the missing classes, you can suppress"); 416 System.err.println(" the warnings with '-dontwarn' options."); 417 418 if (configuration.skipNonPublicLibraryClasses) 419 { 420 System.err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'."); 421 } 422 423 System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass)"); 424 } 425 426 int dependencyWarningCount = dependencyWarningPrinter.getWarningCount(); 427 if (dependencyWarningCount > 0) 428 { 429 System.err.println("Warning: there were " + dependencyWarningCount + 430 " instances of library classes depending on program classes."); 431 System.err.println(" You must avoid such dependencies, since the program classes will"); 432 System.err.println(" be processed, while the library classes will remain unchanged."); 433 System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dependency)"); 434 } 435 436 int programMemberReferenceWarningCount = programMemberReferenceWarningPrinter.getWarningCount(); 437 if (programMemberReferenceWarningCount > 0) 438 { 439 System.err.println("Warning: there were " + programMemberReferenceWarningCount + 440 " unresolved references to program class members."); 441 System.err.println(" Your input classes appear to be inconsistent."); 442 System.err.println(" You may need to recompile the code."); 443 System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedprogramclassmember)"); 444 } 445 446 int libraryMemberReferenceWarningCount = libraryMemberReferenceWarningPrinter.getWarningCount(); 447 if (libraryMemberReferenceWarningCount > 0) 448 { 449 System.err.println("Warning: there were " + libraryMemberReferenceWarningCount + 450 " unresolved references to library class members."); 451 System.err.println(" You probably need to update the library versions."); 452 453 if (!configuration.skipNonPublicLibraryClassMembers) 454 { 455 System.err.println(" Alternatively, you may have to specify the option "); 456 System.err.println(" '-dontskipnonpubliclibraryclassmembers'."); 457 } 458 459 if (configuration.skipNonPublicLibraryClasses) 460 { 461 System.err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'."); 462 } 463 464 System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedlibraryclassmember)"); 465 } 466 467 if ((classReferenceWarningCount > 0 || 468 dependencyWarningCount > 0 || 469 programMemberReferenceWarningCount > 0 || 470 libraryMemberReferenceWarningCount > 0) && 471 !configuration.ignoreWarnings) 472 { 473 throw new IOException("Please correct the above warnings first."); 474 } 475 476 if ((configuration.note == null || 477 !configuration.note.isEmpty()) && 478 (configuration.warn != null && 479 configuration.warn.isEmpty() || 480 configuration.ignoreWarnings)) 481 { 482 System.out.println("Note: you're ignoring all warnings!"); 483 } 484 485 // Discard unused library classes. 486 if (configuration.verbose) 487 { 488 System.out.println("Ignoring unused library classes..."); 489 System.out.println(" Original number of library classes: " + originalLibraryClassPoolSize); 490 System.out.println(" Final number of library classes: " + libraryClassPool.size()); 491 } 492 } 493 494 495 /** 496 * Extracts a list of exceptions of classes for which not to print notes, 497 * from the keep configuration. 498 */ 499 private StringMatcher createClassNoteExceptionMatcher(List noteExceptions) 500 { 501 if (noteExceptions != null) 502 { 503 List noteExceptionNames = new ArrayList(noteExceptions.size()); 504 for (int index = 0; index < noteExceptions.size(); index++) 505 { 506 KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); 507 if (keepClassSpecification.markClasses) 508 { 509 // If the class itself is being kept, it's ok. 510 String className = keepClassSpecification.className; 511 if (className != null) 512 { 513 noteExceptionNames.add(className); 514 } 515 516 // If all of its extensions are being kept, it's ok too. 517 String extendsClassName = keepClassSpecification.extendsClassName; 518 if (extendsClassName != null) 519 { 520 noteExceptionNames.add(extendsClassName); 521 } 522 } 523 } 524 525 if (noteExceptionNames.size() > 0) 526 { 527 return new ListParser(new ClassNameParser()).parse(noteExceptionNames); 528 } 529 } 530 531 return null; 532 } 533 534 535 /** 536 * Extracts a list of exceptions of field or method names for which not to 537 * print notes, from the keep configuration. 538 */ 539 private StringMatcher createClassMemberNoteExceptionMatcher(List noteExceptions, 540 boolean isField) 541 { 542 if (noteExceptions != null) 543 { 544 List noteExceptionNames = new ArrayList(); 545 for (int index = 0; index < noteExceptions.size(); index++) 546 { 547 KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); 548 List memberSpecifications = isField ? 549 keepClassSpecification.fieldSpecifications : 550 keepClassSpecification.methodSpecifications; 551 552 if (memberSpecifications != null) 553 { 554 for (int index2 = 0; index2 < memberSpecifications.size(); index2++) 555 { 556 MemberSpecification memberSpecification = 557 (MemberSpecification)memberSpecifications.get(index2); 558 559 String memberName = memberSpecification.name; 560 if (memberName != null) 561 { 562 noteExceptionNames.add(memberName); 563 } 564 } 565 } 566 } 567 568 if (noteExceptionNames.size() > 0) 569 { 570 return new ListParser(new ClassNameParser()).parse(noteExceptionNames); 571 } 572 } 573 574 return null; 575 } 576} 577