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