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.CodeAttribute;
025: import proguard.classfile.constant.Constant;
026: import proguard.classfile.constant.visitor.ConstantVisitor;
027: import proguard.classfile.editor.CodeAttributeEditor;
028: import proguard.classfile.instruction.*;
029: import proguard.classfile.instruction.visitor.InstructionVisitor;
030: import proguard.classfile.util.*;
031:
032: /**
033: * This InstructionVisitor replaces a given pattern instruction sequence by
034: * another given replacement instruction sequence. The arguments of the
035: * instruction sequences can be wildcards that are matched and replaced.
036: *
037: * @see InstructionSequenceMatcher
038: * @author Eric Lafortune
039: */
040: public class InstructionSequenceReplacer extends SimplifiedVisitor
041: implements InstructionVisitor, ConstantVisitor {
042: private static final boolean DEBUG = false;
043:
044: private final InstructionSequenceMatcher instructionSequenceMatcher;
045: private final Instruction[] replacementInstructions;
046: private final BranchTargetFinder branchTargetFinder;
047: private final CodeAttributeEditor codeAttributeEditor;
048: private final InstructionVisitor extraInstructionVisitor;
049:
050: private final MyReplacementInstructionFactory replacementInstructionFactory = new MyReplacementInstructionFactory();
051:
052: /**
053: * Creates a new InstructionSequenceReplacer.
054: * @param patternConstants any constants referenced by the pattern
055: * instruction.
056: * @param patternInstructions the pattern instruction sequence.
057: * @param replacementInstructions the replacement instruction sequence.
058: * @param branchTargetFinder a branch target finder that has been
059: * initialized to indicate branch targets
060: * in the visited code.
061: * @param codeAttributeEditor a code editor that can be used for
062: * accumulating changes to the code.
063: */
064: public InstructionSequenceReplacer(Constant[] patternConstants,
065: Instruction[] patternInstructions,
066: Instruction[] replacementInstructions,
067: BranchTargetFinder branchTargetFinder,
068: CodeAttributeEditor codeAttributeEditor) {
069: this (patternConstants, patternInstructions,
070: replacementInstructions, branchTargetFinder,
071: codeAttributeEditor, null);
072: }
073:
074: /**
075: * Creates a new InstructionSequenceReplacer.
076: * @param patternConstants any constants referenced by the pattern
077: * instruction.
078: * @param branchTargetFinder a branch target finder that has been
079: * initialized to indicate branch targets
080: * in the visited code.
081: * @param codeAttributeEditor a code editor that can be used for
082: * accumulating changes to the code.
083: * @param extraInstructionVisitor an optional extra visitor for all deleted
084: * load instructions.
085: */
086: public InstructionSequenceReplacer(Constant[] patternConstants,
087: Instruction[] patternInstructions,
088: Instruction[] replacementInstructions,
089: BranchTargetFinder branchTargetFinder,
090: CodeAttributeEditor codeAttributeEditor,
091: InstructionVisitor extraInstructionVisitor) {
092: this .instructionSequenceMatcher = new InstructionSequenceMatcher(
093: patternConstants, patternInstructions);
094: this .replacementInstructions = replacementInstructions;
095: this .branchTargetFinder = branchTargetFinder;
096: this .codeAttributeEditor = codeAttributeEditor;
097: this .extraInstructionVisitor = extraInstructionVisitor;
098: }
099:
100: // Implementations for InstructionVisitor.
101:
102: public void visitAnyInstruction(Clazz clazz, Method method,
103: CodeAttribute codeAttribute, int offset,
104: Instruction instruction) {
105: // Reset the instruction sequence matcher if the instruction is a branch
106: // target or if it has already been modified.
107: if (branchTargetFinder.isTarget(offset)
108: || codeAttributeEditor.isModified(offset)) {
109: instructionSequenceMatcher.reset();
110: }
111:
112: // Try to match the instruction.
113: instruction.accept(clazz, method, codeAttribute, offset,
114: instructionSequenceMatcher);
115:
116: // Did the instruction sequence match and is it still unmodified?
117: if (instructionSequenceMatcher.isMatching()
118: && matchedInstructionsUnmodified()) {
119: if (DEBUG) {
120: System.out.println("InstructionSequenceReplacer: ["
121: + clazz.getName() + "." + method.getName(clazz)
122: + method.getDescriptor(clazz) + "]");
123: System.out.println(" Matched:");
124: for (int index = 0; index < instructionSequenceMatcher
125: .instructionCount(); index++) {
126: int matchedOffset = instructionSequenceMatcher
127: .matchedInstructionOffset(index);
128: System.out.println(" "
129: + InstructionFactory.create(
130: codeAttribute.code, matchedOffset)
131: .toString(matchedOffset));
132: }
133: System.out.println(" Replacement:");
134: for (int index = 0; index < replacementInstructions.length; index++) {
135: int matchedOffset = instructionSequenceMatcher
136: .matchedInstructionOffset(index);
137: System.out.println(" "
138: + replacementInstructionFactory.create(
139: index).shrink().toString(
140: matchedOffset));
141: }
142: }
143:
144: // Replace the instruction sequence.
145: for (int index = 0; index < replacementInstructions.length; index++) {
146: codeAttributeEditor.replaceInstruction(
147: instructionSequenceMatcher
148: .matchedInstructionOffset(index),
149: replacementInstructionFactory.create(index)
150: .shrink());
151: }
152:
153: // Delete any remaining instructions in the from sequence.
154: for (int index = replacementInstructions.length; index < instructionSequenceMatcher
155: .instructionCount(); index++) {
156: codeAttributeEditor
157: .deleteInstruction(instructionSequenceMatcher
158: .matchedInstructionOffset(index));
159: }
160:
161: // Visit the instruction, if required.
162: if (extraInstructionVisitor != null) {
163: instruction.accept(clazz, method, codeAttribute,
164: offset, extraInstructionVisitor);
165: }
166: }
167: }
168:
169: // Small utility methods.
170:
171: /**
172: * Returns whether the matched pattern instructions haven't been modified
173: * before.
174: */
175: private boolean matchedInstructionsUnmodified() {
176: for (int index = 0; index < instructionSequenceMatcher
177: .instructionCount(); index++) {
178: if (codeAttributeEditor
179: .isModified(instructionSequenceMatcher
180: .matchedInstructionOffset(index))) {
181: return false;
182: }
183: }
184:
185: return true;
186: }
187:
188: /**
189: * This class creates replacement instructions for matched sequences, with
190: * any matched arguments filled out.
191: */
192: private class MyReplacementInstructionFactory implements
193: InstructionVisitor {
194: private Instruction replacementInstruction;
195:
196: /**
197: * Creates the replacement instruction for the given index in the
198: * instruction sequence.
199: */
200: public Instruction create(int index) {
201: // Create the instruction.
202: replacementInstructions[index].accept(null, null, null,
203: instructionSequenceMatcher
204: .matchedInstructionOffset(index), this );
205:
206: // Return it.
207: return replacementInstruction.shrink();
208: }
209:
210: // Implementations for InstructionVisitor.
211:
212: public void visitSimpleInstruction(Clazz clazz, Method method,
213: CodeAttribute codeAttribute, int offset,
214: SimpleInstruction simpleInstruction) {
215: replacementInstruction = new SimpleInstruction(
216: simpleInstruction.opcode,
217: instructionSequenceMatcher
218: .matchedArgument(simpleInstruction.constant));
219: }
220:
221: public void visitVariableInstruction(Clazz clazz,
222: Method method, CodeAttribute codeAttribute, int offset,
223: VariableInstruction variableInstruction) {
224: replacementInstruction = new VariableInstruction(
225: variableInstruction.opcode,
226: instructionSequenceMatcher
227: .matchedArgument(variableInstruction.variableIndex),
228: instructionSequenceMatcher
229: .matchedArgument(variableInstruction.constant));
230: }
231:
232: public void visitConstantInstruction(Clazz clazz,
233: Method method, CodeAttribute codeAttribute, int offset,
234: ConstantInstruction constantInstruction) {
235: replacementInstruction = new ConstantInstruction(
236: constantInstruction.opcode,
237: instructionSequenceMatcher
238: .matchedConstantIndex(constantInstruction.constantIndex),
239: instructionSequenceMatcher
240: .matchedArgument(constantInstruction.constant));
241: }
242:
243: public void visitBranchInstruction(Clazz clazz, Method method,
244: CodeAttribute codeAttribute, int offset,
245: BranchInstruction branchInstruction) {
246: replacementInstruction = new BranchInstruction(
247: branchInstruction.opcode,
248: instructionSequenceMatcher.matchedBranchOffset(
249: offset, branchInstruction.branchOffset));
250: }
251:
252: public void visitTableSwitchInstruction(Clazz clazz,
253: Method method, CodeAttribute codeAttribute, int offset,
254: TableSwitchInstruction tableSwitchInstruction) {
255: replacementInstruction = new TableSwitchInstruction(
256: tableSwitchInstruction.opcode,
257: instructionSequenceMatcher.matchedBranchOffset(
258: offset,
259: tableSwitchInstruction.defaultOffset),
260: instructionSequenceMatcher
261: .matchedArgument(tableSwitchInstruction.lowCase),
262: instructionSequenceMatcher
263: .matchedArgument(tableSwitchInstruction.highCase),
264: instructionSequenceMatcher.matchedJumpOffsets(
265: offset, tableSwitchInstruction.jumpOffsets));
266:
267: }
268:
269: public void visitLookUpSwitchInstruction(Clazz clazz,
270: Method method, CodeAttribute codeAttribute, int offset,
271: LookUpSwitchInstruction lookUpSwitchInstruction) {
272: replacementInstruction = new LookUpSwitchInstruction(
273: lookUpSwitchInstruction.opcode,
274: instructionSequenceMatcher.matchedBranchOffset(
275: offset,
276: lookUpSwitchInstruction.defaultOffset),
277: instructionSequenceMatcher
278: .matchedArguments(lookUpSwitchInstruction.cases),
279: instructionSequenceMatcher
280: .matchedJumpOffsets(offset,
281: lookUpSwitchInstruction.jumpOffsets));
282: }
283: }
284: }
|