1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 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 can be included or not. 37 * 38 * @see ReadWriteFieldMarker 39 * @see StaticInitializerContainingClassMarker 40 * @see NoSideEffectMethodMarker 41 * @see SideEffectMethodMarker 42 * @author Eric Lafortune 43 */ 44public class SideEffectInstructionChecker 45extends SimplifiedVisitor 46implements InstructionVisitor, 47 ConstantVisitor, 48 MemberVisitor 49{ 50 private static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null; 51 52 53 private final boolean includeReturnInstructions; 54 private final boolean includeLocalFieldAccess; 55 56 // A return value for the visitor methods. 57 private Clazz referencingClass; 58 private boolean hasSideEffects; 59 60 61 public SideEffectInstructionChecker(boolean includeReturnInstructions, 62 boolean includeLocalFieldAccess) 63 { 64 this.includeReturnInstructions = includeReturnInstructions; 65 this.includeLocalFieldAccess = includeLocalFieldAccess; 66 } 67 68 69 /** 70 * Returns whether the given instruction has side effects outside of its 71 * method. 72 */ 73 public boolean hasSideEffects(Clazz clazz, 74 Method method, 75 CodeAttribute codeAttribute, 76 int offset, 77 Instruction instruction) 78 { 79 hasSideEffects = false; 80 81 instruction.accept(clazz, method, codeAttribute, offset, this); 82 83 return hasSideEffects; 84 } 85 86 87 // Implementations for InstructionVisitor. 88 89 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} 90 91 92 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 93 { 94 byte opcode = simpleInstruction.opcode; 95 96 // Check for instructions that might cause side effects. 97 switch (opcode) 98 { 99 case InstructionConstants.OP_IALOAD: 100 case InstructionConstants.OP_LALOAD: 101 case InstructionConstants.OP_FALOAD: 102 case InstructionConstants.OP_DALOAD: 103 case InstructionConstants.OP_AALOAD: 104 case InstructionConstants.OP_BALOAD: 105 case InstructionConstants.OP_CALOAD: 106 case InstructionConstants.OP_SALOAD: 107 // These instructions strictly taken may cause a side effect 108 // (NullPointerException, ArrayIndexOutOfBoundsException). 109 hasSideEffects = OPTIMIZE_CONSERVATIVELY; 110 break; 111 112 case InstructionConstants.OP_IASTORE: 113 case InstructionConstants.OP_LASTORE: 114 case InstructionConstants.OP_FASTORE: 115 case InstructionConstants.OP_DASTORE: 116 case InstructionConstants.OP_AASTORE: 117 case InstructionConstants.OP_BASTORE: 118 case InstructionConstants.OP_CASTORE: 119 case InstructionConstants.OP_SASTORE: 120 case InstructionConstants.OP_ATHROW : 121 case InstructionConstants.OP_MONITORENTER: 122 case InstructionConstants.OP_MONITOREXIT: 123 // These instructions always cause a side effect. 124 hasSideEffects = true; 125 break; 126 127 case InstructionConstants.OP_IRETURN: 128 case InstructionConstants.OP_LRETURN: 129 case InstructionConstants.OP_FRETURN: 130 case InstructionConstants.OP_DRETURN: 131 case InstructionConstants.OP_ARETURN: 132 case InstructionConstants.OP_RETURN: 133 // These instructions may have a side effect. 134 hasSideEffects = includeReturnInstructions; 135 break; 136 } 137 } 138 139 140 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 141 { 142 byte opcode = variableInstruction.opcode; 143 144 // Check for instructions that might cause side effects. 145 switch (opcode) 146 { 147 case InstructionConstants.OP_RET: 148 // This instruction may have a side effect. 149 hasSideEffects = includeReturnInstructions; 150 break; 151 } 152 } 153 154 155 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 156 { 157 byte opcode = constantInstruction.opcode; 158 159 // Check for instructions that might cause side effects. 160 switch (opcode) 161 { 162 case InstructionConstants.OP_GETSTATIC: 163 case InstructionConstants.OP_PUTSTATIC: 164 case InstructionConstants.OP_INVOKESPECIAL: 165 case InstructionConstants.OP_INVOKESTATIC: 166 // Check if the field is write-only or volatile, or if the 167 // invoked method is causing any side effects. 168 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 169 break; 170 171 case InstructionConstants.OP_GETFIELD: 172 case InstructionConstants.OP_PUTFIELD: 173 case InstructionConstants.OP_INVOKEVIRTUAL: 174 case InstructionConstants.OP_INVOKEINTERFACE: 175 case InstructionConstants.OP_INVOKEDYNAMIC: 176 if (OPTIMIZE_CONSERVATIVELY) 177 { 178 // These instructions strictly taken may cause a side effect 179 // (NullPointerException). 180 hasSideEffects = true; 181 } 182 else 183 { 184 // Check if the field is write-only or volatile, or if the 185 // invoked method is causing any side effects. 186 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 187 } 188 break; 189 190 case InstructionConstants.OP_CHECKCAST: 191 // This instructions strictly taken may cause a side effect 192 // (ClassCastException). 193 hasSideEffects = OPTIMIZE_CONSERVATIVELY; 194 break; 195 } 196 } 197 198 199 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 200 { 201 byte opcode = branchInstruction.opcode; 202 203 // Check for instructions that might cause side effects. 204 switch (opcode) 205 { 206 case InstructionConstants.OP_JSR: 207 case InstructionConstants.OP_JSR_W: 208 hasSideEffects = includeReturnInstructions; 209 break; 210 } 211 } 212 213 214 // Implementations for ConstantVisitor. 215 216 public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) 217 { 218 // We'll have to assume invoking an unknown method has side effects. 219 hasSideEffects = true; 220 } 221 222 223 public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) 224 { 225 // Pass the referencing class. 226 referencingClass = clazz; 227 228 // We'll have to assume accessing an unknown field has side effects. 229 hasSideEffects = true; 230 231 // Check the referenced field, if known. 232 fieldrefConstant.referencedMemberAccept(this); 233 } 234 235 236 public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) 237 { 238 // Pass the referencing class. 239 referencingClass = clazz; 240 241 // We'll have to assume invoking an unknown method has side effects. 242 hasSideEffects = true; 243 244 // Check the referenced method, if known. 245 refConstant.referencedMemberAccept(this); 246 } 247 248 249 // Implementations for MemberVisitor. 250 251 public void visitProgramField(ProgramClass programClass, ProgramField programField) 252 { 253 hasSideEffects = 254 (includeLocalFieldAccess || !programClass.equals(referencingClass)) && 255 ((ReadWriteFieldMarker.isRead(programField) && 256 ReadWriteFieldMarker.isWritten(programField)) || 257 ((programField.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) || 258 (!programClass.equals(referencingClass) && 259 !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass)))); 260 } 261 262 263 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 264 { 265 // Note that side effects already include synchronization of some 266 // implementation of the method. 267 hasSideEffects = 268 !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) && 269 (SideEffectMethodMarker.hasSideEffects(programMethod) || 270 (!programClass.equals(referencingClass) && 271 !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass)))); 272 } 273 274 275 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) 276 { 277 hasSideEffects = true; 278 } 279 280 281 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 282 { 283 hasSideEffects = 284 !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod); 285 } 286 287 288 /** 289 * Returns the set of superclasses and interfaces that are initialized. 290 */ 291 private Set initializedSuperClasses(Clazz clazz) 292 { 293 Set set = new HashSet(); 294 295 // Visit all superclasses and interfaces, collecting the ones that have 296 // static initializers. 297 clazz.hierarchyAccept(true, true, true, false, 298 new StaticInitializerContainingClassFilter( 299 new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, 300 ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, 301 new SideEffectMethodFilter( 302 new MemberToClassVisitor( 303 new ClassCollector(set)))))); 304 305 return set; 306 } 307} 308