ReachableCodeMarker.java revision b9cc48a43ed984587c939d02fba5316bf5c0df6e
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.peephole; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.visitor.*; 26import proguard.classfile.instruction.*; 27import proguard.classfile.instruction.visitor.InstructionVisitor; 28import proguard.classfile.util.SimplifiedVisitor; 29 30import java.util.Arrays; 31 32/** 33 * This AttributeVisitor finds all instruction offsets, branch targets, and 34 * exception targets in the CodeAttribute objects that it visits. 35 * 36 * @author Eric Lafortune 37 */ 38public class ReachableCodeMarker 39extends SimplifiedVisitor 40implements AttributeVisitor, 41 InstructionVisitor, 42 ExceptionInfoVisitor 43{ 44 private boolean[] isReachable = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; 45 46 private boolean next; 47 private boolean evaluateExceptions; 48 49 50 /** 51 * Returns whether the instruction at the given offset is reachable in 52 * the CodeAttribute that was visited most recently. 53 */ 54 public boolean isReachable(int offset) 55 { 56 return isReachable[offset]; 57 } 58 59 60 /** 61 * Returns whether any of the instructions at the given offsets are 62 * reachable in the CodeAttribute that was visited most recently. 63 */ 64 public boolean isReachable(int startOffset, int endOffset) 65 { 66 // Check if any of the instructions is reachable. 67 for (int offset = startOffset; offset < endOffset; offset++) 68 { 69 if (isReachable[offset]) 70 { 71 return true; 72 } 73 } 74 75 return false; 76 } 77 78 79 // Implementations for AttributeVisitor. 80 81 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 82 83 84 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 85 { 86 // Make sure there is a sufficiently large array. 87 int codeLength = codeAttribute.u4codeLength; 88 if (isReachable.length < codeLength) 89 { 90 // Create a new array. 91 isReachable = new boolean[codeLength]; 92 } 93 else 94 { 95 // Reset the array. 96 Arrays.fill(isReachable, 0, codeLength, false); 97 } 98 99 // Mark the code, starting at the entry point. 100 markCode(clazz, method, codeAttribute, 0); 101 102 // Mark the exception handlers, iterating as long as necessary. 103 do 104 { 105 evaluateExceptions = false; 106 107 codeAttribute.exceptionsAccept(clazz, method, this); 108 } 109 while (evaluateExceptions); 110 } 111 112 113 // Implementations for InstructionVisitor. 114 115 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 116 { 117 byte opcode = simpleInstruction.opcode; 118 if (opcode == InstructionConstants.OP_IRETURN || 119 opcode == InstructionConstants.OP_LRETURN || 120 opcode == InstructionConstants.OP_FRETURN || 121 opcode == InstructionConstants.OP_DRETURN || 122 opcode == InstructionConstants.OP_ARETURN || 123 opcode == InstructionConstants.OP_RETURN || 124 opcode == InstructionConstants.OP_ATHROW) 125 { 126 next = false; 127 } 128 } 129 130 131 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 132 { 133 } 134 135 136 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 137 { 138 if (variableInstruction.opcode == InstructionConstants.OP_RET) 139 { 140 next = false; 141 } 142 } 143 144 145 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 146 { 147 // Mark the branch target. 148 markBranchTarget(clazz, 149 method, 150 codeAttribute, 151 offset + branchInstruction.branchOffset); 152 153 byte opcode = branchInstruction.opcode; 154 if (opcode == InstructionConstants.OP_GOTO || 155 opcode == InstructionConstants.OP_GOTO_W) 156 { 157 next = false; 158 } 159 } 160 161 162 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 163 { 164 // Mark the branch targets of the default jump offset. 165 markBranchTarget(clazz, 166 method, 167 codeAttribute, 168 offset + switchInstruction.defaultOffset); 169 170 // Mark the branch targets of the jump offsets. 171 markBranchTargets(clazz, 172 method, 173 codeAttribute, 174 offset, 175 switchInstruction.jumpOffsets); 176 177 next = false; 178 } 179 180 181 // Implementations for ExceptionInfoVisitor. 182 183 public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) 184 { 185 // Mark the exception handler if it's relevant. 186 if (!isReachable(exceptionInfo.u2handlerPC) && 187 isReachable(exceptionInfo.u2startPC, exceptionInfo.u2endPC)) 188 { 189 markCode(clazz, method, codeAttribute, exceptionInfo.u2handlerPC); 190 191 evaluateExceptions = true; 192 } 193 } 194 195 196 // Small utility methods. 197 198 /** 199 * Marks the branch targets of the given jump offsets for the instruction 200 * at the given offset. 201 */ 202 private void markBranchTargets(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int[] jumpOffsets) 203 { 204 for (int index = 0; index < jumpOffsets.length; index++) 205 { 206 markCode(clazz, method, codeAttribute, offset + jumpOffsets[index]); 207 } 208 } 209 210 211 /** 212 * Marks the branch target at the given offset. 213 */ 214 private void markBranchTarget(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset) 215 { 216 boolean oldNext = next; 217 218 markCode(clazz, method, codeAttribute, offset); 219 220 next = oldNext; 221 } 222 223 224 /** 225 * Marks the code starting at the given offset. 226 */ 227 private void markCode(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset) 228 { 229 boolean oldNext = next; 230 231 byte[] code = codeAttribute.code; 232 233 // Continue with the current instruction as long as we haven't marked it 234 // yet. 235 while (!isReachable[offset]) 236 { 237 // Get the current instruction. 238 Instruction instruction = InstructionFactory.create(code, offset); 239 240 // Mark it as reachable. 241 isReachable[offset] = true; 242 243 // By default, we'll assume we can continue with the next 244 // instruction in a moment. 245 next = true; 246 247 // Mark the branch targets, if any. 248 instruction.accept(clazz, method, codeAttribute, offset, this); 249 250 // Can we really continue with the next instruction? 251 if (!next) 252 { 253 break; 254 } 255 256 // Go to the next instruction. 257 offset += instruction.length(offset); 258 } 259 260 next = oldNext; 261 } 262} 263