• 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.attribute.annotation.visitor.*;
24 import proguard.classfile.attribute.visitor.AllAttributeVisitor;
25 import proguard.classfile.visitor.*;
26 
27 import java.util.List;
28 
29 /**
30  * This factory creates visitors to efficiently travel to specified classes and
31  * class members.
32  *
33  * @author Eric Lafortune
34  */
35 public class ClassSpecificationVisitorFactory
36 {
37     /**
38      * Constructs a ClassPoolVisitor to efficiently travel to the specified
39      * classes and class members.
40      *
41      * @param keepClassSpecifications the list of KeepClassSpecification
42      *                                instances, defining of the classes and
43      *                                class members to visit.
44      * @param classVisitor            the ClassVisitor to be applied to matching
45      *                                classes.
46      * @param memberVisitor           the MemberVisitor to be applied to matching
47      *                                class members.
48      */
createClassPoolVisitor(List keepClassSpecifications, ClassVisitor classVisitor, MemberVisitor memberVisitor, boolean shrinking, boolean optimizing, boolean obfuscating)49     public static ClassPoolVisitor createClassPoolVisitor(List          keepClassSpecifications,
50                                                           ClassVisitor  classVisitor,
51                                                           MemberVisitor memberVisitor,
52                                                           boolean       shrinking,
53                                                           boolean       optimizing,
54                                                           boolean       obfuscating)
55     {
56         MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
57 
58         if (keepClassSpecifications != null)
59         {
60             for (int index = 0; index < keepClassSpecifications.size(); index++)
61             {
62                 KeepClassSpecification keepClassSpecification =
63                     (KeepClassSpecification)keepClassSpecifications.get(index);
64 
65                 if ((shrinking   && !keepClassSpecification.allowShrinking)    ||
66                     (optimizing  && !keepClassSpecification.allowOptimization) ||
67                     (obfuscating && !keepClassSpecification.allowObfuscation))
68                 {
69                     multiClassPoolVisitor.addClassPoolVisitor(
70                         createClassPoolVisitor(keepClassSpecification,
71                                                classVisitor,
72                                                memberVisitor));
73                 }
74             }
75         }
76 
77         return multiClassPoolVisitor;
78     }
79 
80 
81     /**
82      * Constructs a ClassPoolVisitor to efficiently travel to the specified
83      * classes and class members.
84      *
85      * @param classSpecifications the list of ClassSpecification instances,
86      *                            defining of the classes and class members to
87      *                            visit.
88      * @param classVisitor        the ClassVisitor to be applied to matching
89      *                            classes.
90      * @param memberVisitor       the MemberVisitor to be applied to matching
91      *                            class members.
92      */
createClassPoolVisitor(List classSpecifications, ClassVisitor classVisitor, MemberVisitor memberVisitor)93     public static ClassPoolVisitor createClassPoolVisitor(List          classSpecifications,
94                                                           ClassVisitor  classVisitor,
95                                                           MemberVisitor memberVisitor)
96     {
97         MultiClassPoolVisitor multiClassPoolVisitor = new MultiClassPoolVisitor();
98 
99         if (classSpecifications != null)
100         {
101             for (int index = 0; index < classSpecifications.size(); index++)
102             {
103                 ClassSpecification classSpecification =
104                     (ClassSpecification)classSpecifications.get(index);
105 
106                 multiClassPoolVisitor.addClassPoolVisitor(
107                     createClassPoolVisitor(classSpecification,
108                                            classVisitor,
109                                            memberVisitor));
110             }
111         }
112 
113         return multiClassPoolVisitor;
114     }
115 
116 
117     /**
118      * Constructs a ClassPoolVisitor to efficiently travel to the specified
119      * classes and class members.
120      *
121      * @param keepClassSpecification the specifications of the class(es) and class
122      *                          members to visit.
123      * @param classVisitor      the ClassVisitor to be applied to matching
124      *                          classes.
125      * @param memberVisitor     the MemberVisitor to be applied to matching
126      *                          class members.
127      */
createClassPoolVisitor(KeepClassSpecification keepClassSpecification, ClassVisitor classVisitor, MemberVisitor memberVisitor)128     private static ClassPoolVisitor createClassPoolVisitor(KeepClassSpecification keepClassSpecification,
129                                                            ClassVisitor      classVisitor,
130                                                            MemberVisitor     memberVisitor)
131     {
132         // Don't  visit the classes if not specified.
133         if (!keepClassSpecification.markClasses &&
134             !keepClassSpecification.markConditionally)
135         {
136             classVisitor = null;
137         }
138 
139         // If specified, let the marker visit the class and its class
140         // members conditionally.
141         if (keepClassSpecification.markConditionally)
142         {
143             // Combine both visitors.
144             ClassVisitor composedClassVisitor =
145                 createCombinedClassVisitor(keepClassSpecification,
146                                            classVisitor,
147                                            memberVisitor);
148 
149             // Replace the class visitor.
150             classVisitor =
151                 createClassMemberTester(keepClassSpecification,
152                                         composedClassVisitor);
153 
154             // Discard the member visitor, because it has already been included.
155             memberVisitor = null;
156         }
157 
158         return createClassPoolVisitor((ClassSpecification)keepClassSpecification,
159                                       classVisitor,
160                                       memberVisitor);
161     }
162 
163 
164     /**
165      * Constructs a ClassPoolVisitor to efficiently travel to the specified
166      * classes and class members.
167      *
168      * @param classSpecification the specifications of the class(es) and class
169      *                           members to visit.
170      * @param classVisitor       the ClassVisitor to be applied to matching
171      *                           classes.
172      * @param memberVisitor      the MemberVisitor to be applied to matching
173      *                           class members.
174      */
createClassPoolVisitor(ClassSpecification classSpecification, ClassVisitor classVisitor, MemberVisitor memberVisitor)175     private static ClassPoolVisitor createClassPoolVisitor(ClassSpecification classSpecification,
176                                                            ClassVisitor       classVisitor,
177                                                            MemberVisitor      memberVisitor)
178     {
179         // Combine both visitors.
180         ClassVisitor composedClassVisitor =
181             createCombinedClassVisitor(classSpecification,
182                                        classVisitor,
183                                        memberVisitor);
184 
185         // By default, start visiting from the named class name, if specified.
186         String className = classSpecification.className;
187 
188         // Although we may have to start from the extended class.
189         String extendsAnnotationType = classSpecification.extendsAnnotationType;
190         String extendsClassName      = classSpecification.extendsClassName;
191 
192         // If wildcarded, only visit classes with matching names.
193         if (className != null &&
194             (extendsAnnotationType != null ||
195              extendsClassName      != null ||
196              containsWildCards(className)))
197         {
198             composedClassVisitor =
199                 new ClassNameFilter(className, composedClassVisitor);
200 
201             // We'll have to visit all classes now.
202             className = null;
203         }
204 
205         // If specified, only visit classes with the right annotation.
206         String annotationType = classSpecification.annotationType;
207 
208         if (annotationType != null)
209         {
210             composedClassVisitor =
211                 new AllAttributeVisitor(
212                 new AllAnnotationVisitor(
213                 new AnnotationTypeFilter(annotationType,
214                 new AnnotatedClassVisitor(composedClassVisitor))));
215         }
216 
217         // If specified, only visit classes with the right access flags.
218         if (classSpecification.requiredSetAccessFlags   != 0 ||
219             classSpecification.requiredUnsetAccessFlags != 0)
220         {
221             composedClassVisitor =
222                 new ClassAccessFilter(classSpecification.requiredSetAccessFlags,
223                                       classSpecification.requiredUnsetAccessFlags,
224                                       composedClassVisitor);
225         }
226 
227         // If it's specified, start visiting from the extended class.
228         if (extendsAnnotationType != null ||
229             extendsClassName      != null)
230         {
231             // Start visiting from the extended class.
232             composedClassVisitor =
233                 new ClassHierarchyTraveler(false, false, false, true,
234                                            composedClassVisitor);
235 
236             // If specified, only visit extended classes with the right annotation.
237             if (extendsAnnotationType != null)
238             {
239                 composedClassVisitor =
240                     new AllAttributeVisitor(
241                     new AllAnnotationVisitor(
242                     new AnnotationTypeFilter(extendsAnnotationType,
243                     new AnnotatedClassVisitor(composedClassVisitor))));
244             }
245 
246             // If specified, only visit extended classes with matching names.
247             if (extendsClassName != null)
248             {
249                 // If wildcarded, only visit extended classes with matching names.
250                 if (containsWildCards(extendsClassName))
251                 {
252                     composedClassVisitor =
253                         new ClassNameFilter(extendsClassName,
254                                             composedClassVisitor);
255                 }
256                 else
257                 {
258                     // Start visiting from the named extended class.
259                     className = extendsClassName;
260                 }
261             }
262         }
263 
264         // If specified, visit a single named class, otherwise visit all classes.
265         return className != null ?
266             (ClassPoolVisitor)new NamedClassVisitor(composedClassVisitor, className) :
267             (ClassPoolVisitor)new AllClassVisitor(composedClassVisitor);
268     }
269 
270 
271     /**
272      * Constructs a ClassVisitor to efficiently travel to the specified
273      * classes and class members.
274      *
275      * @param classSpecification the specifications of the class(es) and class
276      *                           members to visit.
277      * @param classVisitor       the ClassVisitor to be applied to matching
278      *                           classes.
279      * @param memberVisitor      the MemberVisitor to be applied to matching
280      *                           class members.
281      */
createCombinedClassVisitor(ClassSpecification classSpecification, ClassVisitor classVisitor, MemberVisitor memberVisitor)282     private static ClassVisitor createCombinedClassVisitor(ClassSpecification classSpecification,
283                                                            ClassVisitor       classVisitor,
284                                                            MemberVisitor      memberVisitor)
285     {
286         // Don't visit any members if there aren't any member specifications.
287         if (classSpecification.fieldSpecifications  == null &&
288             classSpecification.methodSpecifications == null)
289         {
290             memberVisitor = null;
291         }
292 
293         // The class visitor for classes and their members.
294         MultiClassVisitor multiClassVisitor = new MultiClassVisitor();
295 
296         // If specified, let the class visitor visit the class itself.
297         if (classVisitor != null)
298         {
299             // This class visitor may be the only one.
300             if (memberVisitor == null)
301             {
302                 return classVisitor;
303             }
304 
305             multiClassVisitor.addClassVisitor(classVisitor);
306         }
307 
308         // If specified, let the member info visitor visit the class members.
309         if (memberVisitor != null)
310         {
311             ClassVisitor memberClassVisitor =
312                 createClassVisitor(classSpecification, memberVisitor);
313 
314             // This class visitor may be the only one.
315             if (classVisitor == null)
316             {
317                 return memberClassVisitor;
318             }
319 
320             multiClassVisitor.addClassVisitor(memberClassVisitor);
321         }
322 
323         return multiClassVisitor;
324     }
325 
326 
327     /**
328      * Constructs a ClassVisitor to efficiently travel to the specified class
329      * members.
330      *
331      * @param classSpecification the specifications of the class members to visit.
332      * @param memberVisitor      the MemberVisitor to be applied to matching
333      *                           class members.
334      */
createClassVisitor(ClassSpecification classSpecification, MemberVisitor memberVisitor)335     private static ClassVisitor createClassVisitor(ClassSpecification classSpecification,
336                                                    MemberVisitor      memberVisitor)
337     {
338         MultiClassVisitor multiClassVisitor = new MultiClassVisitor();
339 
340         addMemberVisitors(classSpecification.fieldSpecifications,  true,  multiClassVisitor, memberVisitor);
341         addMemberVisitors(classSpecification.methodSpecifications, false, multiClassVisitor, memberVisitor);
342 
343         // Mark the class member in this class and in super classes.
344         return new ClassHierarchyTraveler(true, true, false, false,
345                                           multiClassVisitor);
346     }
347 
348 
349     /**
350      * Adds elements to the given MultiClassVisitor, to apply the given
351      * MemberVisitor to all class members that match the given List
352      * of options (of the given type).
353      */
addMemberVisitors(List memberSpecifications, boolean isField, MultiClassVisitor multiClassVisitor, MemberVisitor memberVisitor)354     private static void addMemberVisitors(List              memberSpecifications,
355                                           boolean           isField,
356                                           MultiClassVisitor multiClassVisitor,
357                                           MemberVisitor     memberVisitor)
358     {
359         if (memberSpecifications != null)
360         {
361             for (int index = 0; index < memberSpecifications.size(); index++)
362             {
363                 MemberSpecification memberSpecification =
364                     (MemberSpecification)memberSpecifications.get(index);
365 
366                 multiClassVisitor.addClassVisitor(
367                     createClassVisitor(memberSpecification,
368                                        isField,
369                                        memberVisitor));
370             }
371         }
372     }
373 
374 
375     /**
376      * Constructs a ClassVisitor that conditionally applies the given
377      * ClassVisitor to all classes that contain the given class members.
378      */
createClassMemberTester(ClassSpecification classSpecification, ClassVisitor classVisitor)379     private static ClassVisitor createClassMemberTester(ClassSpecification classSpecification,
380                                                         ClassVisitor       classVisitor)
381     {
382         // Create a linked list of conditional visitors, for fields and for
383         // methods.
384         return createClassMemberTester(classSpecification.fieldSpecifications,
385                                        true,
386                createClassMemberTester(classSpecification.methodSpecifications,
387                                        false,
388                                        classVisitor));
389     }
390 
391 
392     /**
393      * Constructs a ClassVisitor that conditionally applies the given
394      * ClassVisitor to all classes that contain the given List of class
395      * members (of the given type).
396      */
createClassMemberTester(List memberSpecifications, boolean isField, ClassVisitor classVisitor)397     private static ClassVisitor createClassMemberTester(List         memberSpecifications,
398                                                         boolean      isField,
399                                                         ClassVisitor classVisitor)
400     {
401         // Create a linked list of conditional visitors.
402         if (memberSpecifications != null)
403         {
404             for (int index = 0; index < memberSpecifications.size(); index++)
405             {
406                 MemberSpecification memberSpecification =
407                     (MemberSpecification)memberSpecifications.get(index);
408 
409                 classVisitor =
410                     createClassVisitor(memberSpecification,
411                                        isField,
412                                        new MemberToClassVisitor(classVisitor));
413             }
414         }
415 
416         return classVisitor;
417     }
418 
419 
420     /**
421      * Creates a new ClassVisitor to efficiently travel to the specified class
422      * members.
423      *
424      * @param memberSpecification the specification of the class member(s) to
425      *                            visit.
426      * @param memberVisitor       the MemberVisitor to be applied to matching
427      *                            class member(s).
428      */
createClassVisitor(MemberSpecification memberSpecification, boolean isField, MemberVisitor memberVisitor)429     private static ClassVisitor createClassVisitor(MemberSpecification memberSpecification,
430                                                    boolean             isField,
431                                                    MemberVisitor       memberVisitor)
432     {
433         String name       = memberSpecification.name;
434         String descriptor = memberSpecification.descriptor;
435 
436         // If name or descriptor are not fully specified, only visit matching
437         // class members.
438         boolean fullySpecified =
439             name       != null &&
440             descriptor != null &&
441             !containsWildCards(name) &&
442             !containsWildCards(descriptor);
443 
444         if (!fullySpecified)
445         {
446             if (descriptor != null)
447             {
448                 memberVisitor =
449                     new MemberDescriptorFilter(descriptor, memberVisitor);
450             }
451 
452             if (name != null)
453             {
454                 memberVisitor =
455                     new MemberNameFilter(name, memberVisitor);
456             }
457         }
458 
459         // If specified, only visit class members with the right annotation.
460         if (memberSpecification.annotationType != null)
461         {
462             memberVisitor =
463                 new AllAttributeVisitor(
464                 new AllAnnotationVisitor(
465                 new AnnotationTypeFilter(memberSpecification.annotationType,
466                 new AnnotationToMemberVisitor(memberVisitor))));
467         }
468 
469         // If any access flags are specified, only visit matching class members.
470         if (memberSpecification.requiredSetAccessFlags   != 0 ||
471             memberSpecification.requiredUnsetAccessFlags != 0)
472         {
473             memberVisitor =
474                 new MemberAccessFilter(memberSpecification.requiredSetAccessFlags,
475                                        memberSpecification.requiredUnsetAccessFlags,
476                                        memberVisitor);
477         }
478 
479         // Depending on what's specified, visit a single named class member,
480         // or all class members, filtering the matching ones.
481         return isField ?
482             fullySpecified ?
483                 (ClassVisitor)new NamedFieldVisitor(name, descriptor, memberVisitor) :
484                 (ClassVisitor)new AllFieldVisitor(memberVisitor) :
485             fullySpecified ?
486                 (ClassVisitor)new NamedMethodVisitor(name, descriptor, memberVisitor) :
487                 (ClassVisitor)new AllMethodVisitor(memberVisitor);
488     }
489 
490 
491     // Small utility methods.
492 
containsWildCards(String string)493     private static boolean containsWildCards(String string)
494     {
495         return string != null &&
496             (string.indexOf('*')   >= 0 ||
497              string.indexOf('?')   >= 0 ||
498              string.indexOf('%')   >= 0 ||
499              string.indexOf(',')   >= 0 ||
500              string.indexOf("///") >= 0);
501     }
502 }
503