1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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.classfile.util; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.*; 25 import proguard.classfile.attribute.visitor.*; 26 import proguard.classfile.constant.*; 27 import proguard.classfile.constant.visitor.ConstantVisitor; 28 import proguard.classfile.instruction.*; 29 import proguard.classfile.instruction.visitor.InstructionVisitor; 30 import 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 */ 47 public class DynamicClassReferenceInitializer 48 extends SimplifiedVisitor 49 implements 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.NAME_JAVA_LANG_CLASS), 70 new Utf8Constant(ClassConstants.METHOD_NAME_CLASS_FOR_NAME), 71 new Utf8Constant(ClassConstants.METHOD_TYPE_CLASS_FOR_NAME), 72 73 // 6 74 new MethodrefConstant(1, 7, null, null), 75 new NameAndTypeConstant(8, 9), 76 new Utf8Constant(ClassConstants.METHOD_NAME_NEW_INSTANCE), 77 new Utf8Constant(ClassConstants.METHOD_TYPE_NEW_INSTANCE), 78 79 // 10 80 new MethodrefConstant(1, 11, null, null), 81 new NameAndTypeConstant(12, 13), 82 new Utf8Constant(ClassConstants.METHOD_NAME_CLASS_GET_COMPONENT_TYPE), 83 new Utf8Constant(ClassConstants.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.METHOD_NAME_DOT_CLASS_JAVAC), 107 // new Utf8Constant(ClassConstants.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.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.METHOD_NAME_DOT_CLASS_JIKES), 130 // new Utf8Constant(ClassConstants.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.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 */ DynamicClassReferenceInitializer(ClassPool programClassPool, ClassPool libraryClassPool, WarningPrinter missingNotePrinter, WarningPrinter dependencyWarningPrinter, WarningPrinter notePrinter, StringMatcher noteExceptionMatcher)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 visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)241 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) 242 { 243 // Try to match the (SomeClass)Class.forName(someName).newInstance() 244 // construct. Apply this matcher first, so the next matcher can still 245 // reset it after the first instruction. 246 instruction.accept(clazz, method, codeAttribute, offset, 247 classForNameCastMatcher); 248 249 // Did we find a match? 250 if (classForNameCastMatcher.isMatching()) 251 { 252 // Print out a note about the construct. 253 clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this); 254 } 255 256 // Try to match the Class.forName("SomeClass") construct. 257 instruction.accept(clazz, method, codeAttribute, offset, 258 constantClassForNameMatcher); 259 260 // Did we find a match? 261 if (constantClassForNameMatcher.isMatching()) 262 { 263 // Fill out the matched string constant. 264 clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this); 265 266 // Don't look for the dynamic construct. 267 classForNameCastMatcher.reset(); 268 } 269 270 // Try to match the javac .class construct. 271 instruction.accept(clazz, method, codeAttribute, offset, 272 dotClassJavacMatcher); 273 274 // Did we find a match? 275 if (dotClassJavacMatcher.isMatching() && 276 isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0))) 277 { 278 // Fill out the matched string constant. 279 clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this); 280 } 281 282 // Try to match the jikes .class construct. 283 instruction.accept(clazz, method, codeAttribute, offset, 284 dotClassJikesMatcher); 285 286 // Did we find a match? 287 if (dotClassJikesMatcher.isMatching() && 288 isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0))) 289 { 290 // Fill out the matched string constant. 291 clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this); 292 } 293 } 294 295 296 // Implementations for ConstantVisitor. 297 298 /** 299 * Fills out the link to the referenced class. 300 */ visitStringConstant(Clazz clazz, StringConstant stringConstant)301 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 302 { 303 // Save a reference to the corresponding class. 304 String externalClassName = stringConstant.getString(clazz); 305 String internalClassName = ClassUtil.internalClassName( 306 ClassUtil.externalBaseType(externalClassName)); 307 308 stringConstant.referencedClass = findClass(clazz.getName(), internalClassName); 309 } 310 311 312 /** 313 * Prints out a note about the class cast to this class, if applicable. 314 */ visitClassConstant(Clazz clazz, ClassConstant classConstant)315 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 316 { 317 // Print out a note about the class cast. 318 if (noteExceptionMatcher == null || 319 !noteExceptionMatcher.matches(classConstant.getName(clazz))) 320 { 321 notePrinter.print(clazz.getName(), 322 classConstant.getName(clazz), 323 "Note: " + 324 ClassUtil.externalClassName(clazz.getName()) + 325 " calls '(" + 326 ClassUtil.externalClassName(classConstant.getName(clazz)) + 327 ")Class.forName(variable).newInstance()'"); 328 } 329 } 330 331 332 /** 333 * Checks whether the referenced method is a .class method. 334 */ visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)335 public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) 336 { 337 String methodType = methodrefConstant.getType(clazz); 338 339 // Do the method's class and type match? 340 if (methodType.equals(ClassConstants.METHOD_TYPE_DOT_CLASS_JAVAC) || 341 methodType.equals(ClassConstants.METHOD_TYPE_DOT_CLASS_JIKES)) 342 { 343 String methodName = methodrefConstant.getName(clazz); 344 345 // Does the method's name match one of the special names? 346 isClassForNameInvocation = 347 methodName.equals(ClassConstants.METHOD_NAME_DOT_CLASS_JAVAC) || 348 methodName.equals(ClassConstants.METHOD_NAME_DOT_CLASS_JIKES); 349 350 if (isClassForNameInvocation) 351 { 352 return; 353 } 354 355 String className = methodrefConstant.getClassName(clazz); 356 357 // Note that we look for the class by name, since the referenced 358 // class has not been initialized yet. 359 Clazz referencedClass = programClassPool.getClass(className); 360 if (referencedClass != null) 361 { 362 // Check if the code of the referenced method is .class code. 363 // Note that we look for the method by name and type, since the 364 // referenced method has not been initialized yet. 365 referencedClass.methodAccept(methodName, 366 methodType, 367 new AllAttributeVisitor(this)); 368 } 369 } 370 } 371 372 373 // Implementations for AttributeVisitor. 374 visitAnyAttribute(Clazz clazz, Attribute attribute)375 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 376 377 visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)378 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 379 { 380 // Check whether this is class$(String), as generated by javac, or 381 // class(String, boolean), as generated by jikes, or an optimized 382 // version. 383 isClassForNameInvocation = 384 isDotClassMethodCode(clazz, method, codeAttribute, 385 dotClassJavacImplementationMatcher, 5) || 386 isDotClassMethodCode(clazz, method, codeAttribute, 387 dotClassJikesImplementationMatcher, 12) || 388 isDotClassMethodCode(clazz, method, codeAttribute, 389 dotClassJikesImplementationMatcher2, 8); 390 } 391 392 393 // Small utility methods. 394 395 /** 396 * Returns whether the given method reference corresponds to a .class 397 * method, as generated by javac or by jikes. 398 */ isDotClassMethodref(Clazz clazz, int methodrefConstantIndex)399 private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex) 400 { 401 isClassForNameInvocation = false; 402 403 // Check if the code of the referenced method is .class code. 404 clazz.constantPoolEntryAccept(methodrefConstantIndex, this); 405 406 return isClassForNameInvocation; 407 } 408 409 410 /** 411 * Returns whether the first whether the first instructions of the 412 * given code attribute match with the given instruction matcher. 413 */ isDotClassMethodCode(Clazz clazz, Method method, CodeAttribute codeAttribute, InstructionSequenceMatcher codeMatcher, int codeLength)414 private boolean isDotClassMethodCode(Clazz clazz, 415 Method method, 416 CodeAttribute codeAttribute, 417 InstructionSequenceMatcher codeMatcher, 418 int codeLength) 419 { 420 // Check the minimum code length. 421 if (codeAttribute.u4codeLength < codeLength) 422 { 423 return false; 424 } 425 426 // Check the actual instructions. 427 codeMatcher.reset(); 428 codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher); 429 return codeMatcher.isMatching(); 430 } 431 432 433 /** 434 * Returns the class with the given name, either for the program class pool 435 * or from the library class pool, or <code>null</code> if it can't be found. 436 */ findClass(String referencingClassName, String name)437 private Clazz findClass(String referencingClassName, String name) 438 { 439 // Is it an array type? 440 if (ClassUtil.isInternalArrayType(name)) 441 { 442 // Ignore any primitive array types. 443 if (!ClassUtil.isInternalClassType(name)) 444 { 445 return null; 446 } 447 448 // Strip the array part. 449 name = ClassUtil.internalClassNameFromClassType(name); 450 } 451 452 // First look for the class in the program class pool. 453 Clazz clazz = programClassPool.getClass(name); 454 455 // Otherwise look for the class in the library class pool. 456 if (clazz == null) 457 { 458 clazz = libraryClassPool.getClass(name); 459 460 if (clazz == null && 461 missingNotePrinter != null) 462 { 463 // We didn't find the superclass or interface. Print a note. 464 missingNotePrinter.print(referencingClassName, 465 name, 466 "Note: " + 467 ClassUtil.externalClassName(referencingClassName) + 468 ": can't find dynamically referenced class " + 469 ClassUtil.externalClassName(name)); 470 } 471 } 472 else if (dependencyWarningPrinter != null) 473 { 474 // The superclass or interface was found in the program class pool. 475 // Print a warning. 476 dependencyWarningPrinter.print(referencingClassName, 477 name, 478 "Warning: library class " + 479 ClassUtil.externalClassName(referencingClassName) + 480 " depends dynamically on program class " + 481 ClassUtil.externalClassName(name)); 482 } 483 484 return clazz; 485 } 486 } 487