DynamicClassReferenceInitializer.java revision cfead78069f3dc32998dc118ee08cab3867acea2
1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2011 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.classfile.util; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.visitor.*; 26import proguard.classfile.constant.*; 27import proguard.classfile.constant.visitor.ConstantVisitor; 28import proguard.classfile.instruction.*; 29import proguard.classfile.instruction.visitor.InstructionVisitor; 30import proguard.util.StringMatcher; 31 32/** 33 * This InstructionVisitor initializes any constant <code>Class.forName</code> or 34 * <code>.class</code> references of all classes it visits. More specifically, 35 * it fills out the references of string constant pool entries that refer to a 36 * class in the program class pool or in the library class pool. 37 * <p> 38 * It optionally prints notes if on usage of 39 * <code>(SomeClass)Class.forName(variable).newInstance()</code>. 40 * <p> 41 * The class hierarchy must be initialized before using this visitor. 42 * 43 * @see ClassSuperHierarchyInitializer 44 * 45 * @author Eric Lafortune 46 */ 47public class DynamicClassReferenceInitializer 48extends SimplifiedVisitor 49implements InstructionVisitor, 50 ConstantVisitor, 51 AttributeVisitor 52{ 53 public static final int X = InstructionSequenceMatcher.X; 54 public static final int Y = InstructionSequenceMatcher.Y; 55 public static final int Z = InstructionSequenceMatcher.Z; 56 57 public static final int A = InstructionSequenceMatcher.A; 58 public static final int B = InstructionSequenceMatcher.B; 59 public static final int C = InstructionSequenceMatcher.C; 60 public static final int D = InstructionSequenceMatcher.D; 61 62 63 private final Constant[] CLASS_FOR_NAME_CONSTANTS = new Constant[] 64 { 65 // 0 66 new MethodrefConstant(1, 2, null, null), 67 new ClassConstant(3, null), 68 new NameAndTypeConstant(4, 5), 69 new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), 70 new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_FOR_NAME), 71 new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_FOR_NAME), 72 73 // 6 74 new MethodrefConstant(1, 7, null, null), 75 new NameAndTypeConstant(8, 9), 76 new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_INSTANCE), 77 new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INSTANCE), 78 79 // 10 80 new MethodrefConstant(1, 11, null, null), 81 new NameAndTypeConstant(12, 13), 82 new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_COMPONENT_TYPE), 83 new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_COMPONENT_TYPE), 84 }; 85 86 // Class.forName("SomeClass"). 87 private final Instruction[] CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS = new Instruction[] 88 { 89 new ConstantInstruction(InstructionConstants.OP_LDC, X), 90 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), 91 }; 92 93 // (SomeClass)Class.forName(someName).newInstance(). 94 private final Instruction[] CLASS_FOR_NAME_CAST_INSTRUCTIONS = new Instruction[] 95 { 96 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), 97 new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 6), 98 new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X), 99 }; 100 101 102// private Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[] 103// { 104// new MethodrefConstant(A, 1, null, null), 105// new NameAndTypeConstant(2, 3), 106// new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC), 107// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC), 108// }; 109 110 private final Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[] 111 { 112 new MethodrefConstant(A, 1, null, null), 113 new NameAndTypeConstant(B, 2), 114 new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC), 115 }; 116 117 // SomeClass.class = class$("SomeClass") (javac). 118 private final Instruction[] DOT_CLASS_JAVAC_INSTRUCTIONS = new Instruction[] 119 { 120 new ConstantInstruction(InstructionConstants.OP_LDC, X), 121 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), 122 }; 123 124 125// private Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[] 126// { 127// new MethodrefConstant(A, 1, null, null), 128// new NameAndTypeConstant(2, 3), 129// new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES), 130// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES), 131// }; 132 133 private final Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[] 134 { 135 new MethodrefConstant(A, 1, null, null), 136 new NameAndTypeConstant(B, 2), 137 new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES), 138 }; 139 140 // SomeClass.class = class("SomeClass", false) (jikes). 141 private final Instruction[] DOT_CLASS_JIKES_INSTRUCTIONS = new Instruction[] 142 { 143 new ConstantInstruction(InstructionConstants.OP_LDC, X), 144 new SimpleInstruction(InstructionConstants.OP_ICONST_0), 145 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), 146 }; 147 148 // return Class.forName(v0). 149 private final Instruction[] DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS = new Instruction[] 150 { 151 new VariableInstruction(InstructionConstants.OP_ALOAD_0), 152 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), 153 new SimpleInstruction(InstructionConstants.OP_ARETURN), 154 }; 155 156 // return Class.forName(v0), if (!v1) .getComponentType(). 157 private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS = new Instruction[] 158 { 159 new VariableInstruction(InstructionConstants.OP_ALOAD_0), 160 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), 161 new VariableInstruction(InstructionConstants.OP_ALOAD_1), 162 new BranchInstruction(InstructionConstants.OP_IFNE, +6), 163 new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10), 164 new SimpleInstruction(InstructionConstants.OP_ARETURN), 165 }; 166 167 // return Class.forName(v0).getComponentType(). 168 private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2 = new Instruction[] 169 { 170 new VariableInstruction(InstructionConstants.OP_ALOAD_0), 171 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), 172 new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10), 173 new SimpleInstruction(InstructionConstants.OP_ARETURN), 174 }; 175 176 177 private final ClassPool programClassPool; 178 private final ClassPool libraryClassPool; 179 private final WarningPrinter missingNotePrinter; 180 private final WarningPrinter dependencyWarningPrinter; 181 private final WarningPrinter notePrinter; 182 private final StringMatcher noteExceptionMatcher; 183 184 185 private final InstructionSequenceMatcher constantClassForNameMatcher = 186 new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, 187 CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS); 188 189 private final InstructionSequenceMatcher classForNameCastMatcher = 190 new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, 191 CLASS_FOR_NAME_CAST_INSTRUCTIONS); 192 193 private final InstructionSequenceMatcher dotClassJavacMatcher = 194 new InstructionSequenceMatcher(DOT_CLASS_JAVAC_CONSTANTS, 195 DOT_CLASS_JAVAC_INSTRUCTIONS); 196 197 private final InstructionSequenceMatcher dotClassJikesMatcher = 198 new InstructionSequenceMatcher(DOT_CLASS_JIKES_CONSTANTS, 199 DOT_CLASS_JIKES_INSTRUCTIONS); 200 201 private final InstructionSequenceMatcher dotClassJavacImplementationMatcher = 202 new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, 203 DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS); 204 205 private final InstructionSequenceMatcher dotClassJikesImplementationMatcher = 206 new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, 207 DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS); 208 209 private final InstructionSequenceMatcher dotClassJikesImplementationMatcher2 = 210 new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, 211 DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2); 212 213 214 // A field acting as a return variable for the visitors. 215 private boolean isClassForNameInvocation; 216 217 218 /** 219 * Creates a new DynamicClassReferenceInitializer that optionally prints 220 * warnings and notes, with optional class specifications for which never 221 * to print notes. 222 */ 223 public DynamicClassReferenceInitializer(ClassPool programClassPool, 224 ClassPool libraryClassPool, 225 WarningPrinter missingNotePrinter, 226 WarningPrinter dependencyWarningPrinter, 227 WarningPrinter notePrinter, 228 StringMatcher noteExceptionMatcher) 229 { 230 this.programClassPool = programClassPool; 231 this.libraryClassPool = libraryClassPool; 232 this.missingNotePrinter = missingNotePrinter; 233 this.dependencyWarningPrinter = dependencyWarningPrinter; 234 this.notePrinter = notePrinter; 235 this.noteExceptionMatcher = noteExceptionMatcher; 236 } 237 238 239 // Implementations for InstructionVisitor. 240 241 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) 242 { 243 // Try to match the Class.forName("SomeClass") construct. 244 instruction.accept(clazz, method, codeAttribute, offset, 245 constantClassForNameMatcher); 246 247 // Did we find a match? 248 if (constantClassForNameMatcher.isMatching()) 249 { 250 // Fill out the matched string constant. 251 clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this); 252 253 // Don't look for the dynamic construct. 254 classForNameCastMatcher.reset(); 255 } 256 257 // Try to match the (SomeClass)Class.forName(someName).newInstance() 258 // construct. 259 instruction.accept(clazz, method, codeAttribute, offset, 260 classForNameCastMatcher); 261 262 // Did we find a match? 263 if (classForNameCastMatcher.isMatching()) 264 { 265 // Print out a note about the construct. 266 clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this); 267 } 268 269 // Try to match the javac .class construct. 270 instruction.accept(clazz, method, codeAttribute, offset, 271 dotClassJavacMatcher); 272 273 // Did we find a match? 274 if (dotClassJavacMatcher.isMatching() && 275 isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0))) 276 { 277 // Fill out the matched string constant. 278 clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this); 279 } 280 281 // Try to match the jikes .class construct. 282 instruction.accept(clazz, method, codeAttribute, offset, 283 dotClassJikesMatcher); 284 285 // Did we find a match? 286 if (dotClassJikesMatcher.isMatching() && 287 isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0))) 288 { 289 // Fill out the matched string constant. 290 clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this); 291 } 292 } 293 294 295 // Implementations for ConstantVisitor. 296 297 /** 298 * Fills out the link to the referenced class. 299 */ 300 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 301 { 302 // Save a reference to the corresponding class. 303 String externalClassName = stringConstant.getString(clazz); 304 String internalClassName = ClassUtil.internalClassName( 305 ClassUtil.externalBaseType(externalClassName)); 306 307 stringConstant.referencedClass = findClass(clazz.getName(), internalClassName); 308 } 309 310 311 /** 312 * Prints out a note about the class cast to this class, if applicable. 313 */ 314 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 315 { 316 // Print out a note about the class cast. 317 if (noteExceptionMatcher == null || 318 !noteExceptionMatcher.matches(classConstant.getName(clazz))) 319 { 320 notePrinter.print(clazz.getName(), 321 classConstant.getName(clazz), 322 "Note: " + 323 ClassUtil.externalClassName(clazz.getName()) + 324 " calls '(" + 325 ClassUtil.externalClassName(classConstant.getName(clazz)) + 326 ")Class.forName(variable).newInstance()'"); 327 } 328 } 329 330 331 /** 332 * Checks whether the referenced method is a .class method. 333 */ 334 public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) 335 { 336 String methodType = methodrefConstant.getType(clazz); 337 338 // Do the method's class and type match? 339 if (methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) || 340 methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES)) 341 { 342 String methodName = methodrefConstant.getName(clazz); 343 344 // Does the method's name match one of the special names? 345 isClassForNameInvocation = 346 methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC) || 347 methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES); 348 349 if (isClassForNameInvocation) 350 { 351 return; 352 } 353 354 String className = methodrefConstant.getClassName(clazz); 355 356 // Note that we look for the class by name, since the referenced 357 // class has not been initialized yet. 358 Clazz referencedClass = programClassPool.getClass(className); 359 if (referencedClass != null) 360 { 361 // Check if the code of the referenced method is .class code. 362 // Note that we look for the method by name and type, since the 363 // referenced method has not been initialized yet. 364 referencedClass.methodAccept(methodName, 365 methodType, 366 new AllAttributeVisitor(this)); 367 } 368 } 369 } 370 371 372 // Implementations for AttributeVisitor. 373 374 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 375 376 377 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 378 { 379 // Check whether this is class$(String), as generated by javac, or 380 // class(String, boolean), as generated by jikes, or an optimized 381 // version. 382 isClassForNameInvocation = 383 isDotClassMethodCode(clazz, method, codeAttribute, 384 dotClassJavacImplementationMatcher, 5) || 385 isDotClassMethodCode(clazz, method, codeAttribute, 386 dotClassJikesImplementationMatcher, 12) || 387 isDotClassMethodCode(clazz, method, codeAttribute, 388 dotClassJikesImplementationMatcher2, 8); 389 } 390 391 392 // Small utility methods. 393 394 /** 395 * Returns whether the given method reference corresponds to a .class 396 * method, as generated by javac or by jikes. 397 */ 398 private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex) 399 { 400 isClassForNameInvocation = false; 401 402 // Check if the code of the referenced method is .class code. 403 clazz.constantPoolEntryAccept(methodrefConstantIndex, this); 404 405 return isClassForNameInvocation; 406 } 407 408 409 /** 410 * Returns whether the first whether the first instructions of the 411 * given code attribute match with the given instruction matcher. 412 */ 413 private boolean isDotClassMethodCode(Clazz clazz, 414 Method method, 415 CodeAttribute codeAttribute, 416 InstructionSequenceMatcher codeMatcher, 417 int codeLength) 418 { 419 // Check the minimum code length. 420 if (codeAttribute.u4codeLength < codeLength) 421 { 422 return false; 423 } 424 425 // Check the actual instructions. 426 codeMatcher.reset(); 427 codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher); 428 return codeMatcher.isMatching(); 429 } 430 431 432 /** 433 * Returns the class with the given name, either for the program class pool 434 * or from the library class pool, or <code>null</code> if it can't be found. 435 */ 436 private Clazz findClass(String referencingClassName, String name) 437 { 438 // Is it an array type? 439 if (ClassUtil.isInternalArrayType(name)) 440 { 441 // Ignore any primitive array types. 442 if (!ClassUtil.isInternalClassType(name)) 443 { 444 return null; 445 } 446 447 // Strip the array part. 448 name = ClassUtil.internalClassNameFromClassType(name); 449 } 450 451 // First look for the class in the program class pool. 452 Clazz clazz = programClassPool.getClass(name); 453 454 // Otherwise look for the class in the library class pool. 455 if (clazz == null) 456 { 457 clazz = libraryClassPool.getClass(name); 458 459 if (clazz == null && 460 missingNotePrinter != null) 461 { 462 // We didn't find the superclass or interface. Print a note. 463 missingNotePrinter.print(referencingClassName, 464 name, 465 "Note: " + 466 ClassUtil.externalClassName(referencingClassName) + 467 ": can't find dynamically referenced class " + 468 ClassUtil.externalClassName(name)); 469 } 470 } 471 else if (dependencyWarningPrinter != null) 472 { 473 // The superclass or interface was found in the program class pool. 474 // Print a warning. 475 dependencyWarningPrinter.print(referencingClassName, 476 name, 477 "Warning: library class " + 478 ClassUtil.externalClassName(referencingClassName) + 479 " depends dynamically on program class " + 480 ClassUtil.externalClassName(name)); 481 } 482 483 return clazz; 484 } 485} 486