001: /*
002: * ProGuard -- shrinking, optimization, obfuscation, and preverification
003: * of Java bytecode.
004: *
005: * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
006: *
007: * This program is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License as published by the Free
009: * Software Foundation; either version 2 of the License, or (at your option)
010: * any later version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
015: * more details.
016: *
017: * You should have received a copy of the GNU General Public License along
018: * with this program; if not, write to the Free Software Foundation, Inc.,
019: * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */
021: package proguard.optimize.peephole;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.attribute.*;
025: import proguard.classfile.attribute.visitor.*;
026: import proguard.classfile.instruction.*;
027: import proguard.classfile.instruction.visitor.InstructionVisitor;
028: import proguard.classfile.util.SimplifiedVisitor;
029:
030: /**
031: * This AttributeVisitor finds all instruction offsets, branch targets, and
032: * exception targets in the CodeAttribute objects that it visits.
033: *
034: * @author Eric Lafortune
035: */
036: public class ReachableCodeMarker extends SimplifiedVisitor implements
037: AttributeVisitor, InstructionVisitor, ExceptionInfoVisitor {
038: private boolean[] isReachable = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
039:
040: private boolean next;
041: private boolean evaluateExceptions;
042:
043: /**
044: * Returns whether the instruction at the given offset is reachable in
045: * the CodeAttribute that was visited most recently.
046: */
047: public boolean isReachable(int offset) {
048: return isReachable[offset];
049: }
050:
051: /**
052: * Returns whether any of the instructions at the given offsets are
053: * reachable in the CodeAttribute that was visited most recently.
054: */
055: public boolean isReachable(int startOffset, int endOffset) {
056: // Check if any of the instructions is reachable.
057: for (int offset = startOffset; offset < endOffset; offset++) {
058: if (isReachable[offset]) {
059: return true;
060: }
061: }
062:
063: return false;
064: }
065:
066: // Implementations for AttributeVisitor.
067:
068: public void visitAnyAttribute(Clazz clazz, Attribute attribute) {
069: }
070:
071: public void visitCodeAttribute(Clazz clazz, Method method,
072: CodeAttribute codeAttribute) {
073: // Make sure there is a sufficiently large array.
074: int codeLength = codeAttribute.u4codeLength;
075: if (isReachable.length < codeLength) {
076: // Create a new array.
077: isReachable = new boolean[codeLength];
078: } else {
079: // Reset the array.
080: for (int index = 0; index < codeLength; index++) {
081: isReachable[index] = false;
082: }
083: }
084:
085: // Mark the code, starting at the entry point.
086: markCode(clazz, method, codeAttribute, 0);
087:
088: // Mark the exception handlers, iterating as long as necessary.
089: do {
090: evaluateExceptions = false;
091:
092: codeAttribute.exceptionsAccept(clazz, method, this );
093: } while (evaluateExceptions);
094: }
095:
096: // Implementations for InstructionVisitor.
097:
098: public void visitSimpleInstruction(Clazz clazz, Method method,
099: CodeAttribute codeAttribute, int offset,
100: SimpleInstruction simpleInstruction) {
101: byte opcode = simpleInstruction.opcode;
102: if (opcode == InstructionConstants.OP_IRETURN
103: || opcode == InstructionConstants.OP_LRETURN
104: || opcode == InstructionConstants.OP_FRETURN
105: || opcode == InstructionConstants.OP_DRETURN
106: || opcode == InstructionConstants.OP_ARETURN
107: || opcode == InstructionConstants.OP_RETURN
108: || opcode == InstructionConstants.OP_ATHROW) {
109: next = false;
110: }
111: }
112:
113: public void visitConstantInstruction(Clazz clazz, Method method,
114: CodeAttribute codeAttribute, int offset,
115: ConstantInstruction constantInstruction) {
116: }
117:
118: public void visitVariableInstruction(Clazz clazz, Method method,
119: CodeAttribute codeAttribute, int offset,
120: VariableInstruction variableInstruction) {
121: if (variableInstruction.opcode == InstructionConstants.OP_RET) {
122: next = false;
123: }
124: }
125:
126: public void visitBranchInstruction(Clazz clazz, Method method,
127: CodeAttribute codeAttribute, int offset,
128: BranchInstruction branchInstruction) {
129: // Mark the branch target.
130: markBranchTarget(clazz, method, codeAttribute, offset
131: + branchInstruction.branchOffset);
132:
133: byte opcode = branchInstruction.opcode;
134: if (opcode == InstructionConstants.OP_GOTO
135: || opcode == InstructionConstants.OP_GOTO_W) {
136: next = false;
137: }
138: }
139:
140: public void visitAnySwitchInstruction(Clazz clazz, Method method,
141: CodeAttribute codeAttribute, int offset,
142: SwitchInstruction switchInstruction) {
143: // Mark the branch targets of the default jump offset.
144: markBranchTarget(clazz, method, codeAttribute, offset
145: + switchInstruction.defaultOffset);
146:
147: // Mark the branch targets of the jump offsets.
148: markBranchTargets(clazz, method, codeAttribute, offset,
149: switchInstruction.jumpOffsets);
150:
151: next = false;
152: }
153:
154: // Implementations for ExceptionInfoVisitor.
155:
156: public void visitExceptionInfo(Clazz clazz, Method method,
157: CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) {
158: // Mark the exception handler if it's relevant.
159: if (!isReachable(exceptionInfo.u2handlerPC)
160: && isReachable(exceptionInfo.u2startPC,
161: exceptionInfo.u2endPC)) {
162: markCode(clazz, method, codeAttribute,
163: exceptionInfo.u2handlerPC);
164:
165: evaluateExceptions = true;
166: }
167: }
168:
169: // Small utility methods.
170:
171: /**
172: * Marks the branch targets of the given jump offsets for the instruction
173: * at the given offset.
174: */
175: private void markBranchTargets(Clazz clazz, Method method,
176: CodeAttribute codeAttribute, int offset, int[] jumpOffsets) {
177: for (int index = 0; index < jumpOffsets.length; index++) {
178: markCode(clazz, method, codeAttribute, offset
179: + jumpOffsets[index]);
180: }
181: }
182:
183: /**
184: * Marks the branch target at the given offset.
185: */
186: private void markBranchTarget(Clazz clazz, Method method,
187: CodeAttribute codeAttribute, int offset) {
188: boolean oldNext = next;
189:
190: markCode(clazz, method, codeAttribute, offset);
191:
192: next = oldNext;
193: }
194:
195: /**
196: * Marks the code starting at the given offset.
197: */
198: private void markCode(Clazz clazz, Method method,
199: CodeAttribute codeAttribute, int offset) {
200: boolean oldNext = next;
201:
202: byte[] code = codeAttribute.code;
203:
204: // Continue with the current instruction as long as we haven't marked it
205: // yet.
206: while (!isReachable[offset]) {
207: // Get the current instruction.
208: Instruction instruction = InstructionFactory.create(code,
209: offset);
210:
211: // Mark it as reachable.
212: isReachable[offset] = true;
213:
214: // By default, we'll assume we can continue with the next
215: // instruction in a moment.
216: next = true;
217:
218: // Mark the branch targets, if any.
219: instruction.accept(clazz, method, codeAttribute, offset,
220: this );
221:
222: // Can we really continue with the next instruction?
223: if (!next) {
224: break;
225: }
226:
227: // Go to the next instruction.
228: offset += instruction.length(offset);
229: }
230:
231: next = oldNext;
232: }
233: }
|