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.instruction; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.CodeAttribute; 25import proguard.classfile.instruction.visitor.InstructionVisitor; 26 27/** 28 * This Instruction represents an instruction that refers to a variable on the 29 * local variable stack. 30 * 31 * @author Eric Lafortune 32 */ 33public class VariableInstruction extends Instruction 34{ 35 public boolean wide; 36 public int variableIndex; 37 public int constant; 38 39 40 /** 41 * Creates an uninitialized VariableInstruction. 42 */ 43 public VariableInstruction() {} 44 45 46 public VariableInstruction(boolean wide) 47 { 48 this.wide = wide; 49 } 50 51 52 public VariableInstruction(byte opcode) 53 { 54 this(opcode, embeddedVariable(opcode), 0); 55 } 56 57 58 public VariableInstruction(byte opcode, 59 int variableIndex) 60 { 61 this(opcode, variableIndex, 0); 62 } 63 64 65 public VariableInstruction(byte opcode, 66 int variableIndex, 67 int constant) 68 { 69 this.opcode = opcode; 70 this.variableIndex = variableIndex; 71 this.constant = constant; 72 this.wide = requiredVariableIndexSize() > 1 || 73 requiredConstantSize() > 1; 74 } 75 76 77 /** 78 * Copies the given instruction into this instruction. 79 * @param variableInstruction the instruction to be copied. 80 * @return this instruction. 81 */ 82 public VariableInstruction copy(VariableInstruction variableInstruction) 83 { 84 this.opcode = variableInstruction.opcode; 85 this.variableIndex = variableInstruction.variableIndex; 86 this.constant = variableInstruction.constant; 87 this.wide = variableInstruction.wide; 88 89 return this; 90 } 91 92 93 /** 94 * Return the embedded variable of the given opcode, or 0 if the opcode 95 * doesn't have one. 96 */ 97 private static int embeddedVariable(byte opcode) 98 { 99 switch (opcode) 100 { 101 case InstructionConstants.OP_ILOAD_1: 102 case InstructionConstants.OP_LLOAD_1: 103 case InstructionConstants.OP_FLOAD_1: 104 case InstructionConstants.OP_DLOAD_1: 105 case InstructionConstants.OP_ALOAD_1: 106 case InstructionConstants.OP_ISTORE_1: 107 case InstructionConstants.OP_LSTORE_1: 108 case InstructionConstants.OP_FSTORE_1: 109 case InstructionConstants.OP_DSTORE_1: 110 case InstructionConstants.OP_ASTORE_1: return 1; 111 112 case InstructionConstants.OP_ILOAD_2: 113 case InstructionConstants.OP_LLOAD_2: 114 case InstructionConstants.OP_FLOAD_2: 115 case InstructionConstants.OP_DLOAD_2: 116 case InstructionConstants.OP_ALOAD_2: 117 case InstructionConstants.OP_ISTORE_2: 118 case InstructionConstants.OP_LSTORE_2: 119 case InstructionConstants.OP_FSTORE_2: 120 case InstructionConstants.OP_DSTORE_2: 121 case InstructionConstants.OP_ASTORE_2: return 2; 122 123 case InstructionConstants.OP_ILOAD_3: 124 case InstructionConstants.OP_LLOAD_3: 125 case InstructionConstants.OP_FLOAD_3: 126 case InstructionConstants.OP_DLOAD_3: 127 case InstructionConstants.OP_ALOAD_3: 128 case InstructionConstants.OP_ISTORE_3: 129 case InstructionConstants.OP_LSTORE_3: 130 case InstructionConstants.OP_FSTORE_3: 131 case InstructionConstants.OP_DSTORE_3: 132 case InstructionConstants.OP_ASTORE_3: return 3; 133 134 default: return 0; 135 } 136 } 137 138 139 /** 140 * Returns whether this instruction stores the value of a variable. 141 * The value is false for the ret instruction, but true for the iinc 142 * instruction. 143 */ 144 public boolean isStore() 145 { 146 // A store instruction can be recognized as follows. Note that this 147 // excludes the ret instruction, which has a negative opcode. 148 return opcode >= InstructionConstants.OP_ISTORE || 149 opcode == InstructionConstants.OP_IINC; 150 } 151 152 153 /** 154 * Returns whether this instruction loads the value of a variable. 155 * The value is true for the ret instruction and for the iinc 156 * instruction. 157 */ 158 public boolean isLoad() 159 { 160 // A load instruction can be recognized as follows. Note that this 161 // includes the ret instruction, which has a negative opcode. 162 return opcode < InstructionConstants.OP_ISTORE; 163 } 164 165 166 // Implementations for Instruction. 167 168 public byte canonicalOpcode() 169 { 170 // Remove the _0, _1, _2, _3 extension, if any. 171 switch (opcode) 172 { 173 case InstructionConstants.OP_ILOAD_0: 174 case InstructionConstants.OP_ILOAD_1: 175 case InstructionConstants.OP_ILOAD_2: 176 case InstructionConstants.OP_ILOAD_3: return InstructionConstants.OP_ILOAD; 177 case InstructionConstants.OP_LLOAD_0: 178 case InstructionConstants.OP_LLOAD_1: 179 case InstructionConstants.OP_LLOAD_2: 180 case InstructionConstants.OP_LLOAD_3: return InstructionConstants.OP_LLOAD; 181 case InstructionConstants.OP_FLOAD_0: 182 case InstructionConstants.OP_FLOAD_1: 183 case InstructionConstants.OP_FLOAD_2: 184 case InstructionConstants.OP_FLOAD_3: return InstructionConstants.OP_FLOAD; 185 case InstructionConstants.OP_DLOAD_0: 186 case InstructionConstants.OP_DLOAD_1: 187 case InstructionConstants.OP_DLOAD_2: 188 case InstructionConstants.OP_DLOAD_3: return InstructionConstants.OP_DLOAD; 189 case InstructionConstants.OP_ALOAD_0: 190 case InstructionConstants.OP_ALOAD_1: 191 case InstructionConstants.OP_ALOAD_2: 192 case InstructionConstants.OP_ALOAD_3: return InstructionConstants.OP_ALOAD; 193 194 case InstructionConstants.OP_ISTORE_0: 195 case InstructionConstants.OP_ISTORE_1: 196 case InstructionConstants.OP_ISTORE_2: 197 case InstructionConstants.OP_ISTORE_3: return InstructionConstants.OP_ISTORE; 198 case InstructionConstants.OP_LSTORE_0: 199 case InstructionConstants.OP_LSTORE_1: 200 case InstructionConstants.OP_LSTORE_2: 201 case InstructionConstants.OP_LSTORE_3: return InstructionConstants.OP_LSTORE; 202 case InstructionConstants.OP_FSTORE_0: 203 case InstructionConstants.OP_FSTORE_1: 204 case InstructionConstants.OP_FSTORE_2: 205 case InstructionConstants.OP_FSTORE_3: return InstructionConstants.OP_FSTORE; 206 case InstructionConstants.OP_DSTORE_0: 207 case InstructionConstants.OP_DSTORE_1: 208 case InstructionConstants.OP_DSTORE_2: 209 case InstructionConstants.OP_DSTORE_3: return InstructionConstants.OP_DSTORE; 210 case InstructionConstants.OP_ASTORE_0: 211 case InstructionConstants.OP_ASTORE_1: 212 case InstructionConstants.OP_ASTORE_2: 213 case InstructionConstants.OP_ASTORE_3: return InstructionConstants.OP_ASTORE; 214 215 default: return opcode; 216 } 217 } 218 219 public Instruction shrink() 220 { 221 opcode = canonicalOpcode(); 222 223 // Is this instruction pointing to a variable with index from 0 to 3? 224 if (variableIndex <= 3) 225 { 226 switch (opcode) 227 { 228 case InstructionConstants.OP_ILOAD: opcode = (byte)(InstructionConstants.OP_ILOAD_0 + variableIndex); break; 229 case InstructionConstants.OP_LLOAD: opcode = (byte)(InstructionConstants.OP_LLOAD_0 + variableIndex); break; 230 case InstructionConstants.OP_FLOAD: opcode = (byte)(InstructionConstants.OP_FLOAD_0 + variableIndex); break; 231 case InstructionConstants.OP_DLOAD: opcode = (byte)(InstructionConstants.OP_DLOAD_0 + variableIndex); break; 232 case InstructionConstants.OP_ALOAD: opcode = (byte)(InstructionConstants.OP_ALOAD_0 + variableIndex); break; 233 234 case InstructionConstants.OP_ISTORE: opcode = (byte)(InstructionConstants.OP_ISTORE_0 + variableIndex); break; 235 case InstructionConstants.OP_LSTORE: opcode = (byte)(InstructionConstants.OP_LSTORE_0 + variableIndex); break; 236 case InstructionConstants.OP_FSTORE: opcode = (byte)(InstructionConstants.OP_FSTORE_0 + variableIndex); break; 237 case InstructionConstants.OP_DSTORE: opcode = (byte)(InstructionConstants.OP_DSTORE_0 + variableIndex); break; 238 case InstructionConstants.OP_ASTORE: opcode = (byte)(InstructionConstants.OP_ASTORE_0 + variableIndex); break; 239 } 240 } 241 242 // Only make the instruction wide if necessary. 243 wide = requiredVariableIndexSize() > 1 || 244 requiredConstantSize() > 1; 245 246 return this; 247 } 248 249 250 protected boolean isWide() 251 { 252 return wide; 253 } 254 255 256 protected void readInfo(byte[] code, int offset) 257 { 258 int variableIndexSize = variableIndexSize(); 259 int constantSize = constantSize(); 260 261 // Also initialize embedded variable indexes. 262 if (variableIndexSize == 0) 263 { 264 // An embedded variable index can be decoded as follows. 265 variableIndex = opcode < InstructionConstants.OP_ISTORE_0 ? 266 (opcode - InstructionConstants.OP_ILOAD_0 ) & 3 : 267 (opcode - InstructionConstants.OP_ISTORE_0) & 3; 268 } 269 else 270 { 271 variableIndex = readValue(code, offset, variableIndexSize); offset += variableIndexSize; 272 } 273 274 constant = readSignedValue(code, offset, constantSize); 275 } 276 277 278 protected void writeInfo(byte[] code, int offset) 279 { 280 int variableIndexSize = variableIndexSize(); 281 int constantSize = constantSize(); 282 283 if (requiredVariableIndexSize() > variableIndexSize) 284 { 285 throw new IllegalArgumentException("Instruction has invalid variable index size ("+this.toString(offset)+")"); 286 } 287 288 if (requiredConstantSize() > constantSize) 289 { 290 throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")"); 291 } 292 293 writeValue(code, offset, variableIndex, variableIndexSize); offset += variableIndexSize; 294 writeSignedValue(code, offset, constant, constantSize); 295 } 296 297 298 public int length(int offset) 299 { 300 return (wide ? 2 : 1) + variableIndexSize() + constantSize(); 301 } 302 303 304 public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) 305 { 306 instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, this); 307 } 308 309 310 // Implementations for Object. 311 312 public String toString() 313 { 314 return getName() + 315 (wide ? "_w" : "") + 316 " v"+variableIndex + 317 (constantSize() > 0 ? ", "+constant : ""); 318 } 319 320 321 // Small utility methods. 322 323 /** 324 * Returns the variable index size for this instruction. 325 */ 326 private int variableIndexSize() 327 { 328 return (opcode >= InstructionConstants.OP_ILOAD_0 && 329 opcode <= InstructionConstants.OP_ALOAD_3) || 330 (opcode >= InstructionConstants.OP_ISTORE_0 && 331 opcode <= InstructionConstants.OP_ASTORE_3) ? 0 : 332 wide ? 2 : 333 1; 334 } 335 336 337 /** 338 * Computes the required variable index size for this instruction's variable 339 * index. 340 */ 341 private int requiredVariableIndexSize() 342 { 343 return (variableIndex & 0x3) == variableIndex ? 0 : 344 (variableIndex & 0xff) == variableIndex ? 1 : 345 (variableIndex & 0xffff) == variableIndex ? 2 : 346 4; 347 348 } 349 350 351 /** 352 * Returns the constant size for this instruction. 353 */ 354 private int constantSize() 355 { 356 return opcode != InstructionConstants.OP_IINC ? 0 : 357 wide ? 2 : 358 1; 359 } 360 361 362 /** 363 * Computes the required constant size for this instruction's constant. 364 */ 365 private int requiredConstantSize() 366 { 367 return opcode != InstructionConstants.OP_IINC ? 0 : 368 constant << 24 >> 24 == constant ? 1 : 369 constant << 16 >> 16 == constant ? 2 : 370 4; 371 } 372} 373