Home | History | Annotate | Download | only in proguard
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 Eric Lafortune (eric (at) 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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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