1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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. The caller still has to update the frame sizes. 35 * 36 * @author Eric Lafortune 37 */ 38public class InstructionWriter 39extends SimplifiedVisitor 40implements InstructionVisitor, 41 AttributeVisitor 42{ 43 //* 44 private static final boolean DEBUG = false; 45 /*/ 46 public static boolean DEBUG = false; 47 //*/ 48 49 50 private int codeLength; 51 52 private CodeAttributeEditor codeAttributeEditor; 53 54 55 /** 56 * Resets the accumulated code. 57 * @param codeLength the length of the code that will be edited next. 58 */ 59 public void reset(int codeLength) 60 { 61 this.codeLength = codeLength; 62 63 if (codeAttributeEditor != null) 64 { 65 codeAttributeEditor.reset(codeLength); 66 } 67 } 68 69 70 /** 71 * Extends the size of the accumulated code. 72 * @param codeLength the length of the code that will be edited next. 73 */ 74 public void extend(int codeLength) 75 { 76 this.codeLength = codeLength; 77 78 if (codeAttributeEditor != null) 79 { 80 codeAttributeEditor.extend(codeLength); 81 } 82 } 83 84 85 // Implementations for InstructionVisitor. 86 87 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 88 { 89 // Try to write out the instruction. 90 // Simple instructions should always fit. 91 simpleInstruction.write(codeAttribute, offset); 92 } 93 94 95 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 96 { 97 try 98 { 99 // Try to write out the instruction. 100 constantInstruction.write(codeAttribute, offset); 101 } 102 catch (IllegalArgumentException exception) 103 { 104 // Create a new constant instruction that will fit. 105 Instruction replacementInstruction = 106 new ConstantInstruction(constantInstruction.opcode, 107 constantInstruction.constantIndex, 108 constantInstruction.constant); 109 110 if (DEBUG) 111 { 112 System.out.println(" "+constantInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString()); 113 } 114 115 replaceInstruction(offset, replacementInstruction); 116 117 // Write out a dummy constant instruction for now. 118 constantInstruction.constantIndex = 0; 119 constantInstruction.constant = 0; 120 constantInstruction.write(codeAttribute, offset); 121 } 122 } 123 124 125 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 126 { 127 try 128 { 129 // Try to write out the instruction. 130 variableInstruction.write(codeAttribute, offset); 131 } 132 catch (IllegalArgumentException exception) 133 { 134 // Create a new variable instruction that will fit. 135 Instruction replacementInstruction = 136 new VariableInstruction(variableInstruction.opcode, 137 variableInstruction.variableIndex, 138 variableInstruction.constant); 139 140 replaceInstruction(offset, replacementInstruction); 141 142 if (DEBUG) 143 { 144 System.out.println(" "+variableInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString()); 145 } 146 147 // Write out a dummy variable instruction for now. 148 variableInstruction.variableIndex = 0; 149 variableInstruction.constant = 0; 150 variableInstruction.write(codeAttribute, offset); 151 } 152 } 153 154 155 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 156 { 157 try 158 { 159 // Try to write out the instruction. 160 branchInstruction.write(codeAttribute, offset); 161 } 162 catch (IllegalArgumentException exception) 163 { 164 // Create a new unconditional branch that will fit. 165 Instruction replacementInstruction = 166 new BranchInstruction(InstructionConstants.OP_GOTO_W, 167 branchInstruction.branchOffset); 168 169 // Create a new instruction that will fit. 170 switch (branchInstruction.opcode) 171 { 172 default: 173 { 174 // Create a new branch instruction that will fit. 175 replacementInstruction = 176 new BranchInstruction(branchInstruction.opcode, 177 branchInstruction.branchOffset); 178 179 break; 180 } 181 182 // Some special cases, for which a wide branch doesn't exist. 183 case InstructionConstants.OP_IFEQ: 184 case InstructionConstants.OP_IFNE: 185 case InstructionConstants.OP_IFLT: 186 case InstructionConstants.OP_IFGE: 187 case InstructionConstants.OP_IFGT: 188 case InstructionConstants.OP_IFLE: 189 case InstructionConstants.OP_IFICMPEQ: 190 case InstructionConstants.OP_IFICMPNE: 191 case InstructionConstants.OP_IFICMPLT: 192 case InstructionConstants.OP_IFICMPGE: 193 case InstructionConstants.OP_IFICMPGT: 194 case InstructionConstants.OP_IFICMPLE: 195 case InstructionConstants.OP_IFACMPEQ: 196 case InstructionConstants.OP_IFACMPNE: 197 { 198 // Insert the complementary conditional branch. 199 Instruction complementaryConditionalBranch = 200 new BranchInstruction((byte)(((branchInstruction.opcode+1) ^ 1) - 1), 201 (1+2) + (1+4)); 202 203 insertBeforeInstruction(offset, complementaryConditionalBranch); 204 205 // Create a new unconditional branch that will fit. 206 break; 207 } 208 209 case InstructionConstants.OP_IFNULL: 210 case InstructionConstants.OP_IFNONNULL: 211 { 212 // Insert the complementary conditional branch. 213 Instruction complementaryConditionalBranch = 214 new BranchInstruction((byte)(branchInstruction.opcode ^ 1), 215 (1+2) + (1+4)); 216 217 insertBeforeInstruction(offset, complementaryConditionalBranch); 218 219 // Create a new unconditional branch that will fit. 220 break; 221 } 222 } 223 224 if (DEBUG) 225 { 226 System.out.println(" "+branchInstruction.toString(offset)+" will be widened to "+replacementInstruction.toString()); 227 } 228 229 replaceInstruction(offset, replacementInstruction); 230 231 // Write out a dummy branch instruction for now. 232 branchInstruction.branchOffset = 0; 233 branchInstruction.write(codeAttribute, offset); 234 } 235 } 236 237 238 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 239 { 240 // Try to write out the instruction. 241 // Switch instructions should always fit. 242 switchInstruction.write(codeAttribute, offset); 243 } 244 245 246 // Implementations for AttributeVisitor. 247 248 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 249 { 250 // Avoid doing any work if nothing is changing anyway. 251 if (codeAttributeEditor != null) 252 { 253 if (DEBUG) 254 { 255 System.out.println("InstructionWriter: widening instructions in "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); 256 } 257 258 // Apply the collected expansions. 259 codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); 260 261 // Don't keep the editor around. We're assuming it won't be needed 262 // very often, so we don't want to be resetting it all the time. 263 codeAttributeEditor = null; 264 } 265 } 266 267 268 // Small utility methods. 269 270 /** 271 * Remembers to place the given instruction right before the instruction 272 * at the given offset. 273 */ 274 private void insertBeforeInstruction(int instructionOffset, Instruction instruction) 275 { 276 ensureCodeAttributeEditor(); 277 278 // Replace the instruction. 279 codeAttributeEditor.insertBeforeInstruction(instructionOffset, instruction); 280 } 281 282 283 /** 284 * Remembers to replace the instruction at the given offset by the given 285 * instruction. 286 */ 287 private void replaceInstruction(int instructionOffset, Instruction instruction) 288 { 289 ensureCodeAttributeEditor(); 290 291 // Replace the instruction. 292 codeAttributeEditor.replaceInstruction(instructionOffset, instruction); 293 } 294 295 296 /** 297 * Remembers to place the given instruction right after the instruction 298 * at the given offset. 299 */ 300 private void insertAfterInstruction(int instructionOffset, Instruction instruction) 301 { 302 ensureCodeAttributeEditor(); 303 304 // Replace the instruction. 305 codeAttributeEditor.insertAfterInstruction(instructionOffset, instruction); 306 } 307 308 309 /** 310 * Makes sure there is a code attribute editor for the given code attribute. 311 */ 312 private void ensureCodeAttributeEditor() 313 { 314 if (codeAttributeEditor == null) 315 { 316 codeAttributeEditor = new CodeAttributeEditor(false, true); 317 codeAttributeEditor.reset(codeLength); 318 } 319 } 320} 321