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