• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
21 package proguard;
22 
23 import proguard.classfile.ClassPool;
24 import proguard.classfile.attribute.visitor.AllAttributeVisitor;
25 import proguard.classfile.constant.visitor.*;
26 import proguard.classfile.instruction.visitor.AllInstructionVisitor;
27 import proguard.classfile.util.*;
28 import proguard.classfile.visitor.*;
29 import proguard.util.*;
30 
31 import java.io.IOException;
32 import java.util.*;
33 
34 /**
35  * This class initializes class pools.
36  *
37  * @author Eric Lafortune
38  */
39 public 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      */
Initializer(Configuration configuration)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      */
execute(ClassPool programClassPool, ClassPool libraryClassPool)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      */
createClassNoteExceptionMatcher(List noteExceptions)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      */
createClassMemberNoteExceptionMatcher(List noteExceptions, boolean isField)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