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