1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard; 22 23import proguard.classfile.attribute.annotation.visitor.*; 24import proguard.classfile.attribute.visitor.AllAttributeVisitor; 25import proguard.classfile.visitor.*; 26 27import 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 */ 35public 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