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.classfile.editor; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.CodeAttribute; 25import proguard.classfile.attribute.visitor.AttributeVisitor; 26import proguard.classfile.instruction.*; 27import proguard.classfile.instruction.visitor.InstructionVisitor; 28import proguard.classfile.util.SimplifiedVisitor; 29 30/** 31 * This InstructionVisitor writes out the instructions that it visits, 32 * collecting instructions that have to be widened. As an AttributeVisitor, 33 * it then applies the collected changes. The process will be repeated 34 * recursively, if necessary. 35 * 36 * @author Eric Lafortune 37 */ 38public class InstructionWriter 39extends SimplifiedVisitor 40implements InstructionVisitor, 41 AttributeVisitor 42{ 43 private int codeLength; 44 45 private CodeAttributeEditor codeAttributeEditor; 46 47 48 /** 49 * Resets the accumulated code changes. 50 * @param codeLength the length of the code that will be edited next. 51 */ 52 public void reset(int codeLength) 53 { 54 this.codeLength = codeLength; 55 56 // The code attribute editor has to be created lazily. 57 if (codeAttributeEditor != null) 58 { 59 codeAttributeEditor.reset(codeLength); 60 } 61 } 62 63 64 // Implementations for InstructionVisitor. 65 66 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 67 { 68 // Try to write out the instruction. 69 // Simple instructions should always fit. 70 simpleInstruction.write(codeAttribute, offset); 71 } 72 73 74 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 75 { 76 try 77 { 78 // Try to write out the instruction. 79 constantInstruction.write(codeAttribute, offset); 80 } 81 catch (IllegalArgumentException exception) 82 { 83 // Create a new constant instruction that will fit. 84 Instruction replacementInstruction = 85 new ConstantInstruction(constantInstruction.opcode, 86 constantInstruction.constantIndex, 87 constantInstruction.constant).shrink(); 88 89 replaceInstruction(offset, replacementInstruction); 90 91 // Write out a dummy constant instruction for now. 92 constantInstruction.constantIndex = 0; 93 constantInstruction.constant = 0; 94 constantInstruction.write(codeAttribute, offset); 95 } 96 } 97 98 99 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 100 { 101 try 102 { 103 // Try to write out the instruction. 104 variableInstruction.write(codeAttribute, offset); 105 } 106 catch (IllegalArgumentException exception) 107 { 108 // Create a new variable instruction that will fit. 109 Instruction replacementInstruction = 110 new VariableInstruction(variableInstruction.opcode, 111 variableInstruction.variableIndex, 112 variableInstruction.constant).shrink(); 113 114 replaceInstruction(offset, replacementInstruction); 115 116 // Write out a dummy variable instruction for now. 117 variableInstruction.variableIndex = 0; 118 variableInstruction.constant = 0; 119 variableInstruction.write(codeAttribute, offset); 120 } 121 } 122 123 124 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 125 { 126 try 127 { 128 // Try to write out the instruction. 129 branchInstruction.write(codeAttribute, offset); 130 } 131 catch (IllegalArgumentException exception) 132 { 133 // Create a new unconditional branch that will fit. 134 Instruction replacementInstruction = 135 new BranchInstruction(InstructionConstants.OP_GOTO_W, 136 branchInstruction.branchOffset); 137 138 // Create a new instruction that will fit. 139 switch (branchInstruction.opcode) 140 { 141 default: 142 { 143 // Create a new branch instruction that will fit. 144 replacementInstruction = 145 new BranchInstruction(branchInstruction.opcode, 146 branchInstruction.branchOffset).shrink(); 147 148 break; 149 } 150 151 // Some special cases, for which a wide branch doesn't exist. 152 case InstructionConstants.OP_IFEQ: 153 case InstructionConstants.OP_IFNE: 154 case InstructionConstants.OP_IFLT: 155 case InstructionConstants.OP_IFGE: 156 case InstructionConstants.OP_IFGT: 157 case InstructionConstants.OP_IFLE: 158 case InstructionConstants.OP_IFICMPEQ: 159 case InstructionConstants.OP_IFICMPNE: 160 case InstructionConstants.OP_IFICMPLT: 161 case InstructionConstants.OP_IFICMPGE: 162 case InstructionConstants.OP_IFICMPGT: 163 case InstructionConstants.OP_IFICMPLE: 164 case InstructionConstants.OP_IFACMPEQ: 165 case InstructionConstants.OP_IFACMPNE: 166 { 167 // Insert the complementary conditional branch. 168 Instruction complementaryConditionalBranch = 169 new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1), 170 (1+2) + (1+4)); 171 172 insertBeforeInstruction(offset, complementaryConditionalBranch); 173 174 // Create a new unconditional branch that will fit. 175 break; 176 } 177 178 case InstructionConstants.OP_IFNULL: 179 case InstructionConstants.OP_IFNONNULL: 180 { 181 // Insert the complementary conditional branch. 182 Instruction complementaryConditionalBranch = 183 new BranchInstruction((byte)(branchInstruction.opcode ^ 1), 184 (1+2) + (1+4)); 185 186 insertBeforeInstruction(offset, complementaryConditionalBranch); 187 188 // Create a new unconditional branch that will fit. 189 break; 190 } 191 } 192 193 replaceInstruction(offset, replacementInstruction); 194 195 // Write out a dummy branch instruction for now. 196 branchInstruction.branchOffset = 0; 197 branchInstruction.write(codeAttribute, offset); 198 } 199 } 200 201 202 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 203 { 204 // Try to write out the instruction. 205 // Switch instructions should always fit. 206 switchInstruction.write(codeAttribute, offset); 207 } 208 209 210 // Implementations for AttributeVisitor. 211 212 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 213 { 214 // Avoid doing any work if nothing is changing anyway. 215 if (codeAttributeEditor != null) 216 { 217 // Apply the collected expansions. 218 codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); 219 220 // Clear the modifications for the next run. 221 codeAttributeEditor = null; 222 } 223 } 224 225 226 // Small utility methods. 227 228 /** 229 * Remembers to place the given instruction right before the instruction 230 * at the given offset. 231 */ 232 private void insertBeforeInstruction(int instructionOffset, Instruction instruction) 233 { 234 ensureCodeAttributeEditor(); 235 236 // Replace the instruction. 237 codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction); 238 } 239 240 241 /** 242 * Remembers to replace the instruction at the given offset by the given 243 * instruction. 244 */ 245 private void replaceInstruction(int instructionOffset, Instruction instruction) 246 { 247 ensureCodeAttributeEditor(); 248 249 // Replace the instruction. 250 codeAttributeEditor.replaceInstruction(instructionOffset, instruction); 251 } 252 253 254 /** 255 * Remembers to place the given instruction right after the instruction 256 * at the given offset. 257 */ 258 private void insertAfterInstruction(int instructionOffset, Instruction instruction) 259 { 260 ensureCodeAttributeEditor(); 261 262 // Replace the instruction. 263 codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction); 264 } 265 266 267 /** 268 * Makes sure there is a code attribute editor for the given code attribute. 269 */ 270 private void ensureCodeAttributeEditor() 271 { 272 if (codeAttributeEditor == null) 273 { 274 codeAttributeEditor = new CodeAttributeEditor(); 275 codeAttributeEditor.reset(codeLength); 276 } 277 } 278} 279