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.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(externalClassName); 305 306 stringConstant.referencedClass = findClass(clazz.getName(), internalClassName); 307 } 308 309 310 /** 311 * Prints out a note about the class cast to this class, if applicable. 312 */ 313 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 314 { 315 // Print out a note about the class cast. 316 if (noteExceptionMatcher == null || 317 !noteExceptionMatcher.matches(classConstant.getName(clazz))) 318 { 319 notePrinter.print(clazz.getName(), 320 classConstant.getName(clazz), 321 "Note: " + 322 ClassUtil.externalClassName(clazz.getName()) + 323 " calls '(" + 324 ClassUtil.externalClassName(classConstant.getName(clazz)) + 325 ")Class.forName(variable).newInstance()'"); 326 } 327 } 328 329 330 /** 331 * Checks whether the referenced method is a .class method. 332 */ 333 public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) 334 { 335 String methodType = methodrefConstant.getType(clazz); 336 337 // Do the method's class and type match? 338 if (methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) || 339 methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES)) 340 { 341 String methodName = methodrefConstant.getName(clazz); 342 343 // Does the method's name match one of the special names? 344 isClassForNameInvocation = 345 methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC) || 346 methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES); 347 348 if (isClassForNameInvocation) 349 { 350 return; 351 } 352 353 String className = methodrefConstant.getClassName(clazz); 354 355 // Note that we look for the class by name, since the referenced 356 // class has not been initialized yet. 357 Clazz referencedClass = programClassPool.getClass(className); 358 if (referencedClass != null) 359 { 360 // Check if the code of the referenced method is .class code. 361 // Note that we look for the method by name and type, since the 362 // referenced method has not been initialized yet. 363 referencedClass.methodAccept(methodName, 364 methodType, 365 new AllAttributeVisitor(this)); 366 } 367 } 368 } 369 370 371 // Implementations for AttributeVisitor. 372 373 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 374 375 376 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 377 { 378 // Check whether this is class$(String), as generated by javac, or 379 // class(String, boolean), as generated by jikes, or an optimized 380 // version. 381 isClassForNameInvocation = 382 isDotClassMethodCode(clazz, method, codeAttribute, 383 dotClassJavacImplementationMatcher, 5) || 384 isDotClassMethodCode(clazz, method, codeAttribute, 385 dotClassJikesImplementationMatcher, 12) || 386 isDotClassMethodCode(clazz, method, codeAttribute, 387 dotClassJikesImplementationMatcher2, 8); 388 } 389 390 391 // Small utility methods. 392 393 /** 394 * Returns whether the given method reference corresponds to a .class 395 * method, as generated by javac or by jikes. 396 */ 397 private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex) 398 { 399 isClassForNameInvocation = false; 400 401 // Check if the code of the referenced method is .class code. 402 clazz.constantPoolEntryAccept(methodrefConstantIndex, this); 403 404 return isClassForNameInvocation; 405 } 406 407 408 /** 409 * Returns whether the first whether the first instructions of the 410 * given code attribute match with the given instruction matcher. 411 */ 412 private boolean isDotClassMethodCode(Clazz clazz, 413 Method method, 414 CodeAttribute codeAttribute, 415 InstructionSequenceMatcher codeMatcher, 416 int codeLength) 417 { 418 // Check the minimum code length. 419 if (codeAttribute.u4codeLength < codeLength) 420 { 421 return false; 422 } 423 424 // Check the actual instructions. 425 codeMatcher.reset(); 426 codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher); 427 return codeMatcher.isMatching(); 428 } 429 430 431 /** 432 * Returns the class with the given name, either for the program class pool 433 * or from the library class pool, or <code>null</code> if it can't be found. 434 */ 435 private Clazz findClass(String referencingClassName, String name) 436 { 437 // Ignore any primitive array types. 438 if (ClassUtil.isInternalArrayType(name) && 439 !ClassUtil.isInternalClassType(name)) 440 { 441 return null; 442 } 443 444 // First look for the class in the program class pool. 445 Clazz clazz = programClassPool.getClass(name); 446 447 // Otherwise look for the class in the library class pool. 448 if (clazz == null) 449 { 450 clazz = libraryClassPool.getClass(name); 451 452 if (clazz == null && 453 missingNotePrinter != null) 454 { 455 // We didn't find the superclass or interface. Print a note. 456 missingNotePrinter.print(referencingClassName, 457 name, 458 "Note: " + 459 ClassUtil.externalClassName(referencingClassName) + 460 ": can't find dynamically referenced class " + 461 ClassUtil.externalClassName(name)); 462 } 463 } 464 else if (dependencyWarningPrinter != null) 465 { 466 // The superclass or interface was found in the program class pool. 467 // Print a warning. 468 dependencyWarningPrinter.print(referencingClassName, 469 name, 470 "Warning: library class " + 471 ClassUtil.externalClassName(referencingClassName) + 472 " depends dynamically on program class " + 473 ClassUtil.externalClassName(name)); 474 } 475 476 return clazz; 477 } 478} 479