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 */ 21package proguard.optimize.info; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.CodeAttribute; 25import proguard.classfile.constant.*; 26import proguard.classfile.constant.visitor.ConstantVisitor; 27import proguard.classfile.instruction.*; 28import proguard.classfile.instruction.visitor.InstructionVisitor; 29import proguard.classfile.util.SimplifiedVisitor; 30import proguard.classfile.visitor.*; 31 32import java.util.*; 33 34/** 35 * This class can tell whether an instruction has any side effects outside of 36 * its method. Return instructions and local field accesses can be included or 37 * not. 38 * 39 * @see ReadWriteFieldMarker 40 * @see StaticInitializerContainingClassMarker 41 * @see NoSideEffectMethodMarker 42 * @see SideEffectMethodMarker 43 * @author Eric Lafortune 44 */ 45public class SideEffectInstructionChecker 46extends SimplifiedVisitor 47implements InstructionVisitor, 48 ConstantVisitor, 49 MemberVisitor 50{ 51 static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null; 52 53 54 private final boolean includeReturnInstructions; 55 private final boolean includeLocalFieldAccess; 56 57 // A return value for the visitor methods. 58 private boolean writingField; 59 private Clazz referencingClass; 60 private boolean hasSideEffects; 61 62 63 /** 64 * Creates a new SideEffectInstructionChecker 65 * @param includeReturnInstructions specifies whether return instructions 66 * count as side effects. 67 * @param includeLocalFieldAccess specifies whether reading or writing 68 * local fields counts as side effects. 69 */ 70 public SideEffectInstructionChecker(boolean includeReturnInstructions, 71 boolean includeLocalFieldAccess) 72 { 73 this.includeReturnInstructions = includeReturnInstructions; 74 this.includeLocalFieldAccess = includeLocalFieldAccess; 75 } 76 77 78 /** 79 * Returns whether the given instruction has side effects outside of its 80 * method. 81 */ 82 public boolean hasSideEffects(Clazz clazz, 83 Method method, 84 CodeAttribute codeAttribute, 85 int offset, 86 Instruction instruction) 87 { 88 hasSideEffects = false; 89 90 instruction.accept(clazz, method, codeAttribute, offset, this); 91 92 return hasSideEffects; 93 } 94 95 96 // Implementations for InstructionVisitor. 97 98 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} 99 100 101 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 102 { 103 byte opcode = simpleInstruction.opcode; 104 105 // Check for instructions that might cause side effects. 106 switch (opcode) 107 { 108 case InstructionConstants.OP_IDIV: 109 case InstructionConstants.OP_LDIV: 110 case InstructionConstants.OP_IREM: 111 case InstructionConstants.OP_LREM: 112 case InstructionConstants.OP_IALOAD: 113 case InstructionConstants.OP_LALOAD: 114 case InstructionConstants.OP_FALOAD: 115 case InstructionConstants.OP_DALOAD: 116 case InstructionConstants.OP_AALOAD: 117 case InstructionConstants.OP_BALOAD: 118 case InstructionConstants.OP_CALOAD: 119 case InstructionConstants.OP_SALOAD: 120 case InstructionConstants.OP_NEWARRAY: 121 case InstructionConstants.OP_ARRAYLENGTH: 122 case InstructionConstants.OP_ANEWARRAY: 123 case InstructionConstants.OP_MULTIANEWARRAY: 124 // These instructions strictly taken may cause a side effect 125 // (ArithmeticException, NullPointerException, 126 // ArrayIndexOutOfBoundsException, NegativeArraySizeException). 127 hasSideEffects = OPTIMIZE_CONSERVATIVELY; 128 break; 129 130 case InstructionConstants.OP_IASTORE: 131 case InstructionConstants.OP_LASTORE: 132 case InstructionConstants.OP_FASTORE: 133 case InstructionConstants.OP_DASTORE: 134 case InstructionConstants.OP_AASTORE: 135 case InstructionConstants.OP_BASTORE: 136 case InstructionConstants.OP_CASTORE: 137 case InstructionConstants.OP_SASTORE: 138 case InstructionConstants.OP_ATHROW : 139 case InstructionConstants.OP_MONITORENTER: 140 case InstructionConstants.OP_MONITOREXIT: 141 // These instructions always cause a side effect. 142 hasSideEffects = true; 143 break; 144 145 case InstructionConstants.OP_IRETURN: 146 case InstructionConstants.OP_LRETURN: 147 case InstructionConstants.OP_FRETURN: 148 case InstructionConstants.OP_DRETURN: 149 case InstructionConstants.OP_ARETURN: 150 case InstructionConstants.OP_RETURN: 151 // These instructions may have a side effect. 152 hasSideEffects = includeReturnInstructions; 153 break; 154 } 155 } 156 157 158 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 159 { 160 byte opcode = variableInstruction.opcode; 161 162 // Check for instructions that might cause side effects. 163 switch (opcode) 164 { 165 case InstructionConstants.OP_RET: 166 // This instruction may have a side effect. 167 hasSideEffects = includeReturnInstructions; 168 break; 169 } 170 } 171 172 173 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 174 { 175 byte opcode = constantInstruction.opcode; 176 177 // Check for instructions that might cause side effects. 178 switch (opcode) 179 { 180 case InstructionConstants.OP_GETSTATIC: 181 // Check if accessing the field might cause any side effects. 182 writingField = false; 183 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 184 break; 185 186 case InstructionConstants.OP_PUTSTATIC: 187 // Check if accessing the field might cause any side effects. 188 writingField = true; 189 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 190 break; 191 192 case InstructionConstants.OP_GETFIELD: 193 if (OPTIMIZE_CONSERVATIVELY) 194 { 195 // These instructions strictly taken may cause a side effect 196 // (NullPointerException). 197 hasSideEffects = true; 198 } 199 else 200 { 201 // Check if the field is write-only or volatile. 202 writingField = false; 203 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 204 } 205 break; 206 207 case InstructionConstants.OP_PUTFIELD: 208 if (OPTIMIZE_CONSERVATIVELY) 209 { 210 // These instructions strictly taken may cause a side effect 211 // (NullPointerException). 212 hasSideEffects = true; 213 } 214 else 215 { 216 // Check if the field is write-only or volatile. 217 writingField = true; 218 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 219 } 220 break; 221 222 case InstructionConstants.OP_INVOKESPECIAL: 223 case InstructionConstants.OP_INVOKESTATIC: 224 // Check if the invoked method is causing any side effects. 225 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 226 break; 227 228 case InstructionConstants.OP_INVOKEVIRTUAL: 229 case InstructionConstants.OP_INVOKEINTERFACE: 230 case InstructionConstants.OP_INVOKEDYNAMIC: 231 if (OPTIMIZE_CONSERVATIVELY) 232 { 233 // These instructions strictly taken may cause a side effect 234 // (NullPointerException). 235 hasSideEffects = true; 236 } 237 else 238 { 239 // Check if the invoked method is causing any side effects. 240 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 241 } 242 break; 243 244 case InstructionConstants.OP_ANEWARRAY: 245 case InstructionConstants.OP_CHECKCAST: 246 case InstructionConstants.OP_MULTIANEWARRAY: 247 // This instructions strictly taken may cause a side effect 248 // (ClassCastException, NegativeArraySizeException). 249 hasSideEffects = OPTIMIZE_CONSERVATIVELY; 250 break; 251 } 252 } 253 254 255 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 256 { 257 byte opcode = branchInstruction.opcode; 258 259 // Check for instructions that might cause side effects. 260 switch (opcode) 261 { 262 case InstructionConstants.OP_JSR: 263 case InstructionConstants.OP_JSR_W: 264 hasSideEffects = includeReturnInstructions; 265 break; 266 } 267 } 268 269 270 // Implementations for ConstantVisitor. 271 272 public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) 273 { 274 // We'll have to assume invoking an unknown method has side effects. 275 hasSideEffects = true; 276 } 277 278 279 public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) 280 { 281 // Pass the referencing class. 282 referencingClass = clazz; 283 284 // We'll have to assume accessing an unknown field has side effects. 285 hasSideEffects = true; 286 287 // Check the referenced field, if known. 288 fieldrefConstant.referencedMemberAccept(this); 289 } 290 291 292 public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) 293 { 294 // Pass the referencing class. 295 referencingClass = clazz; 296 297 // We'll have to assume invoking an unknown method has side effects. 298 hasSideEffects = true; 299 300 // Check the referenced method, if known. 301 refConstant.referencedMemberAccept(this); 302 } 303 304 305 // Implementations for MemberVisitor. 306 307 public void visitProgramField(ProgramClass programClass, ProgramField programField) 308 { 309 hasSideEffects = 310 (includeLocalFieldAccess || !programClass.equals(referencingClass)) && 311 ((writingField && ReadWriteFieldMarker.isRead(programField)) || 312 (programField.getAccessFlags() & ClassConstants.ACC_VOLATILE) != 0 || 313 mayHaveSideEffects(referencingClass, programClass)); 314 } 315 316 317 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 318 { 319 // Note that side effects already include synchronization of some 320 // implementation of the method. 321 hasSideEffects = 322 !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) && 323 (SideEffectMethodMarker.hasSideEffects(programMethod) || 324 mayHaveSideEffects(referencingClass, programClass)); 325 } 326 327 328 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) 329 { 330 hasSideEffects = true; 331 } 332 333 334 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 335 { 336 hasSideEffects = 337 !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod); 338 } 339 340 341 // Small utility methods. 342 343 /** 344 * Returns whether a field reference or method invocation from the 345 * referencing class to the referenced class might have any side 346 * effects. 347 */ 348 private boolean mayHaveSideEffects(Clazz referencingClass, Clazz referencedClass) 349 { 350 return 351 !referencedClass.equals(referencingClass) && 352 !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(referencedClass)); 353 } 354 355 356 /** 357 * Returns the set of superclasses and interfaces that are initialized. 358 */ 359 private Set initializedSuperClasses(Clazz clazz) 360 { 361 Set set = new HashSet(); 362 363 // Visit all superclasses and interfaces, collecting the ones that have 364 // static initializers. 365 clazz.hierarchyAccept(true, true, true, false, 366 new StaticInitializerContainingClassFilter( 367 new NamedMethodVisitor(ClassConstants.METHOD_NAME_CLINIT, 368 ClassConstants.METHOD_TYPE_CLINIT, 369 new SideEffectMethodFilter( 370 new MemberToClassVisitor( 371 new ClassCollector(set)))))); 372 373 return set; 374 } 375} 376