1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 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.CodeAttribute; 25import proguard.classfile.constant.Constant; 26import proguard.classfile.constant.visitor.ConstantVisitor; 27import proguard.classfile.editor.CodeAttributeEditor; 28import proguard.classfile.instruction.*; 29import proguard.classfile.instruction.visitor.InstructionVisitor; 30import proguard.classfile.util.*; 31 32/** 33 * This InstructionVisitor replaces a given pattern instruction sequence by 34 * another given replacement instruction sequence. The arguments of the 35 * instruction sequences can be wildcards that are matched and replaced. 36 * 37 * @see InstructionSequenceMatcher 38 * @author Eric Lafortune 39 */ 40public class InstructionSequenceReplacer 41extends SimplifiedVisitor 42implements InstructionVisitor, 43 ConstantVisitor 44{ 45 private static final boolean DEBUG = false; 46 47 48 private final InstructionSequenceMatcher instructionSequenceMatcher; 49 private final Instruction[] replacementInstructions; 50 private final BranchTargetFinder branchTargetFinder; 51 private final CodeAttributeEditor codeAttributeEditor; 52 private final InstructionVisitor extraInstructionVisitor; 53 54 private final MyReplacementInstructionFactory replacementInstructionFactory = new MyReplacementInstructionFactory(); 55 56 57 /** 58 * Creates a new InstructionSequenceReplacer. 59 * @param patternConstants any constants referenced by the pattern 60 * instruction. 61 * @param patternInstructions the pattern instruction sequence. 62 * @param replacementInstructions the replacement instruction sequence. 63 * @param branchTargetFinder a branch target finder that has been 64 * initialized to indicate branch targets 65 * in the visited code. 66 * @param codeAttributeEditor a code editor that can be used for 67 * accumulating changes to the code. 68 */ 69 public InstructionSequenceReplacer(Constant[] patternConstants, 70 Instruction[] patternInstructions, 71 Instruction[] replacementInstructions, 72 BranchTargetFinder branchTargetFinder, 73 CodeAttributeEditor codeAttributeEditor) 74 { 75 this(patternConstants, 76 patternInstructions, 77 replacementInstructions, 78 branchTargetFinder, 79 codeAttributeEditor, 80 null); 81 } 82 83 84 /** 85 * Creates a new InstructionSequenceReplacer. 86 * @param patternConstants any constants referenced by the pattern 87 * instruction. 88 * @param branchTargetFinder a branch target finder that has been 89 * initialized to indicate branch targets 90 * in the visited code. 91 * @param codeAttributeEditor a code editor that can be used for 92 * accumulating changes to the code. 93 * @param extraInstructionVisitor an optional extra visitor for all deleted 94 * load instructions. 95 */ 96 public InstructionSequenceReplacer(Constant[] patternConstants, 97 Instruction[] patternInstructions, 98 Instruction[] replacementInstructions, 99 BranchTargetFinder branchTargetFinder, 100 CodeAttributeEditor codeAttributeEditor, 101 InstructionVisitor extraInstructionVisitor) 102 { 103 this.instructionSequenceMatcher = new InstructionSequenceMatcher(patternConstants, patternInstructions); 104 this.replacementInstructions = replacementInstructions; 105 this.branchTargetFinder = branchTargetFinder; 106 this.codeAttributeEditor = codeAttributeEditor; 107 this.extraInstructionVisitor = extraInstructionVisitor; 108 } 109 110 111 // Implementations for InstructionVisitor. 112 113 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) 114 { 115 // Reset the instruction sequence matcher if the instruction is a branch 116 // target or if it has already been modified. 117 if (branchTargetFinder.isTarget(offset) || 118 codeAttributeEditor.isModified(offset)) 119 { 120 instructionSequenceMatcher.reset(); 121 } 122 123 // Try to match the instruction. 124 instruction.accept(clazz, method, codeAttribute, offset, instructionSequenceMatcher); 125 126 // Did the instruction sequence match and is it still unmodified? 127 if (instructionSequenceMatcher.isMatching() && 128 matchedInstructionsUnmodified()) 129 { 130 if (DEBUG) 131 { 132 System.out.println("InstructionSequenceReplacer: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 133 System.out.println(" Matched:"); 134 for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++) 135 { 136 int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index); 137 System.out.println(" "+InstructionFactory.create(codeAttribute.code, matchedOffset).toString(matchedOffset)); 138 } 139 System.out.println(" Replacement:"); 140 for (int index = 0; index < replacementInstructions.length; index++) 141 { 142 int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index); 143 System.out.println(" "+replacementInstructionFactory.create(index).shrink().toString(matchedOffset)); 144 } 145 } 146 147 // Replace the instruction sequence. 148 for (int index = 0; index < replacementInstructions.length; index++) 149 { 150 codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index), 151 replacementInstructionFactory.create(index).shrink()); 152 } 153 154 // Delete any remaining instructions in the from sequence. 155 for (int index = replacementInstructions.length; index < instructionSequenceMatcher.instructionCount(); index++) 156 { 157 codeAttributeEditor.deleteInstruction(instructionSequenceMatcher.matchedInstructionOffset(index)); 158 } 159 160 // Visit the instruction, if required. 161 if (extraInstructionVisitor != null) 162 { 163 instruction.accept(clazz, 164 method, 165 codeAttribute, 166 offset, 167 extraInstructionVisitor); 168 } 169 } 170 } 171 172 173 // Small utility methods. 174 175 /** 176 * Returns whether the matched pattern instructions haven't been modified 177 * before. 178 */ 179 private boolean matchedInstructionsUnmodified() 180 { 181 for (int index = 0; index < instructionSequenceMatcher.instructionCount(); index++) 182 { 183 if (codeAttributeEditor.isModified(instructionSequenceMatcher.matchedInstructionOffset(index))) 184 { 185 return false; 186 } 187 } 188 189 return true; 190 } 191 192 193 /** 194 * This class creates replacement instructions for matched sequences, with 195 * any matched arguments filled out. 196 */ 197 private class MyReplacementInstructionFactory 198 implements InstructionVisitor 199 { 200 private Instruction replacementInstruction; 201 202 203 /** 204 * Creates the replacement instruction for the given index in the 205 * instruction sequence. 206 */ 207 public Instruction create(int index) 208 { 209 // Create the instruction. 210 replacementInstructions[index].accept(null, 211 null, 212 null, 213 instructionSequenceMatcher.matchedInstructionOffset(index), 214 this); 215 216 // Return it. 217 return replacementInstruction.shrink(); 218 } 219 220 221 // Implementations for InstructionVisitor. 222 223 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 224 { 225 replacementInstruction = 226 new SimpleInstruction(simpleInstruction.opcode, 227 instructionSequenceMatcher.matchedArgument(simpleInstruction.constant)); 228 } 229 230 231 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 232 { 233 replacementInstruction = 234 new VariableInstruction(variableInstruction.opcode, 235 instructionSequenceMatcher.matchedArgument(variableInstruction.variableIndex), 236 instructionSequenceMatcher.matchedArgument(variableInstruction.constant)); 237 } 238 239 240 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 241 { 242 replacementInstruction = 243 new ConstantInstruction(constantInstruction.opcode, 244 instructionSequenceMatcher.matchedConstantIndex(constantInstruction.constantIndex), 245 instructionSequenceMatcher.matchedArgument(constantInstruction.constant)); 246 } 247 248 249 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 250 { 251 replacementInstruction = 252 new BranchInstruction(branchInstruction.opcode, 253 instructionSequenceMatcher.matchedBranchOffset(offset, branchInstruction.branchOffset)); 254 } 255 256 257 public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) 258 { 259 replacementInstruction = 260 new TableSwitchInstruction(tableSwitchInstruction.opcode, 261 instructionSequenceMatcher.matchedBranchOffset(offset, tableSwitchInstruction.defaultOffset), 262 instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.lowCase), 263 instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.highCase), 264 instructionSequenceMatcher.matchedJumpOffsets(offset, tableSwitchInstruction.jumpOffsets)); 265 266 } 267 268 269 public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) 270 { 271 replacementInstruction = 272 new LookUpSwitchInstruction(lookUpSwitchInstruction.opcode, 273 instructionSequenceMatcher.matchedBranchOffset(offset, lookUpSwitchInstruction.defaultOffset), 274 instructionSequenceMatcher.matchedArguments(lookUpSwitchInstruction.cases), 275 instructionSequenceMatcher.matchedJumpOffsets(offset, lookUpSwitchInstruction.jumpOffsets)); 276 } 277 } 278} 279