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.classfile.instruction;
022:
023: import proguard.classfile.*;
024: import proguard.classfile.attribute.CodeAttribute;
025: import proguard.classfile.instruction.visitor.InstructionVisitor;
026:
027: /**
028: * This Instruction represents an instruction that refers to a variable on the
029: * local variable stack.
030: *
031: * @author Eric Lafortune
032: */
033: public class VariableInstruction extends Instruction {
034: public boolean wide;
035: public int variableIndex;
036: public int constant;
037:
038: /**
039: * Creates an uninitialized VariableInstruction.
040: */
041: public VariableInstruction() {
042: }
043:
044: public VariableInstruction(boolean wide) {
045: this .wide = wide;
046: }
047:
048: public VariableInstruction(byte opcode) {
049: this (opcode, embeddedVariable(opcode), 0);
050: }
051:
052: public VariableInstruction(byte opcode, int variableIndex) {
053: this (opcode, variableIndex, 0);
054: }
055:
056: public VariableInstruction(byte opcode, int variableIndex,
057: int constant) {
058: this .opcode = opcode;
059: this .variableIndex = variableIndex;
060: this .constant = constant;
061: this .wide = requiredVariableIndexSize() > 1
062: || requiredConstantSize() > 1;
063: }
064:
065: /**
066: * Copies the given instruction into this instruction.
067: * @param variableInstruction the instruction to be copied.
068: * @return this instruction.
069: */
070: public VariableInstruction copy(
071: VariableInstruction variableInstruction) {
072: this .opcode = variableInstruction.opcode;
073: this .variableIndex = variableInstruction.variableIndex;
074: this .constant = variableInstruction.constant;
075: this .wide = variableInstruction.wide;
076:
077: return this ;
078: }
079:
080: /**
081: * Return the embedded variable of the given opcode, or 0 if the opcode
082: * doesn't have one.
083: */
084: private static int embeddedVariable(byte opcode) {
085: switch (opcode) {
086: case InstructionConstants.OP_ILOAD_1:
087: case InstructionConstants.OP_LLOAD_1:
088: case InstructionConstants.OP_FLOAD_1:
089: case InstructionConstants.OP_DLOAD_1:
090: case InstructionConstants.OP_ALOAD_1:
091: case InstructionConstants.OP_ISTORE_1:
092: case InstructionConstants.OP_LSTORE_1:
093: case InstructionConstants.OP_FSTORE_1:
094: case InstructionConstants.OP_DSTORE_1:
095: case InstructionConstants.OP_ASTORE_1:
096: return 1;
097:
098: case InstructionConstants.OP_ILOAD_2:
099: case InstructionConstants.OP_LLOAD_2:
100: case InstructionConstants.OP_FLOAD_2:
101: case InstructionConstants.OP_DLOAD_2:
102: case InstructionConstants.OP_ALOAD_2:
103: case InstructionConstants.OP_ISTORE_2:
104: case InstructionConstants.OP_LSTORE_2:
105: case InstructionConstants.OP_FSTORE_2:
106: case InstructionConstants.OP_DSTORE_2:
107: case InstructionConstants.OP_ASTORE_2:
108: return 2;
109:
110: case InstructionConstants.OP_ILOAD_3:
111: case InstructionConstants.OP_LLOAD_3:
112: case InstructionConstants.OP_FLOAD_3:
113: case InstructionConstants.OP_DLOAD_3:
114: case InstructionConstants.OP_ALOAD_3:
115: case InstructionConstants.OP_ISTORE_3:
116: case InstructionConstants.OP_LSTORE_3:
117: case InstructionConstants.OP_FSTORE_3:
118: case InstructionConstants.OP_DSTORE_3:
119: case InstructionConstants.OP_ASTORE_3:
120: return 3;
121:
122: default:
123: return 0;
124: }
125: }
126:
127: /**
128: * Returns whether this instruction loads the value of a variable.
129: * The value is true for the ret instruction, but false for the iinc
130: * instruction.
131: */
132: public boolean isLoad() {
133: // A load instruction can be recognized as follows. Note that this
134: // includes the ret instruction, which has a negative opcode.
135: return opcode < InstructionConstants.OP_ISTORE
136: && opcode != InstructionConstants.OP_IINC;
137: }
138:
139: // Implementations for Instruction.
140:
141: public byte canonicalOpcode() {
142: // Remove the _0, _1, _2, _3 extension, if any.
143: switch (opcode) {
144: case InstructionConstants.OP_ILOAD_0:
145: case InstructionConstants.OP_ILOAD_1:
146: case InstructionConstants.OP_ILOAD_2:
147: case InstructionConstants.OP_ILOAD_3:
148: return InstructionConstants.OP_ILOAD;
149: case InstructionConstants.OP_LLOAD_0:
150: case InstructionConstants.OP_LLOAD_1:
151: case InstructionConstants.OP_LLOAD_2:
152: case InstructionConstants.OP_LLOAD_3:
153: return InstructionConstants.OP_LLOAD;
154: case InstructionConstants.OP_FLOAD_0:
155: case InstructionConstants.OP_FLOAD_1:
156: case InstructionConstants.OP_FLOAD_2:
157: case InstructionConstants.OP_FLOAD_3:
158: return InstructionConstants.OP_FLOAD;
159: case InstructionConstants.OP_DLOAD_0:
160: case InstructionConstants.OP_DLOAD_1:
161: case InstructionConstants.OP_DLOAD_2:
162: case InstructionConstants.OP_DLOAD_3:
163: return InstructionConstants.OP_DLOAD;
164: case InstructionConstants.OP_ALOAD_0:
165: case InstructionConstants.OP_ALOAD_1:
166: case InstructionConstants.OP_ALOAD_2:
167: case InstructionConstants.OP_ALOAD_3:
168: return InstructionConstants.OP_ALOAD;
169:
170: case InstructionConstants.OP_ISTORE_0:
171: case InstructionConstants.OP_ISTORE_1:
172: case InstructionConstants.OP_ISTORE_2:
173: case InstructionConstants.OP_ISTORE_3:
174: return InstructionConstants.OP_ISTORE;
175: case InstructionConstants.OP_LSTORE_0:
176: case InstructionConstants.OP_LSTORE_1:
177: case InstructionConstants.OP_LSTORE_2:
178: case InstructionConstants.OP_LSTORE_3:
179: return InstructionConstants.OP_LSTORE;
180: case InstructionConstants.OP_FSTORE_0:
181: case InstructionConstants.OP_FSTORE_1:
182: case InstructionConstants.OP_FSTORE_2:
183: case InstructionConstants.OP_FSTORE_3:
184: return InstructionConstants.OP_FSTORE;
185: case InstructionConstants.OP_DSTORE_0:
186: case InstructionConstants.OP_DSTORE_1:
187: case InstructionConstants.OP_DSTORE_2:
188: case InstructionConstants.OP_DSTORE_3:
189: return InstructionConstants.OP_DSTORE;
190: case InstructionConstants.OP_ASTORE_0:
191: case InstructionConstants.OP_ASTORE_1:
192: case InstructionConstants.OP_ASTORE_2:
193: case InstructionConstants.OP_ASTORE_3:
194: return InstructionConstants.OP_ASTORE;
195:
196: default:
197: return opcode;
198: }
199: }
200:
201: public Instruction shrink() {
202: opcode = canonicalOpcode();
203:
204: // Is this instruction pointing to a variable with index from 0 to 3?
205: if (variableIndex <= 3) {
206: switch (opcode) {
207: case InstructionConstants.OP_ILOAD:
208: opcode = (byte) (InstructionConstants.OP_ILOAD_0 + variableIndex);
209: break;
210: case InstructionConstants.OP_LLOAD:
211: opcode = (byte) (InstructionConstants.OP_LLOAD_0 + variableIndex);
212: break;
213: case InstructionConstants.OP_FLOAD:
214: opcode = (byte) (InstructionConstants.OP_FLOAD_0 + variableIndex);
215: break;
216: case InstructionConstants.OP_DLOAD:
217: opcode = (byte) (InstructionConstants.OP_DLOAD_0 + variableIndex);
218: break;
219: case InstructionConstants.OP_ALOAD:
220: opcode = (byte) (InstructionConstants.OP_ALOAD_0 + variableIndex);
221: break;
222:
223: case InstructionConstants.OP_ISTORE:
224: opcode = (byte) (InstructionConstants.OP_ISTORE_0 + variableIndex);
225: break;
226: case InstructionConstants.OP_LSTORE:
227: opcode = (byte) (InstructionConstants.OP_LSTORE_0 + variableIndex);
228: break;
229: case InstructionConstants.OP_FSTORE:
230: opcode = (byte) (InstructionConstants.OP_FSTORE_0 + variableIndex);
231: break;
232: case InstructionConstants.OP_DSTORE:
233: opcode = (byte) (InstructionConstants.OP_DSTORE_0 + variableIndex);
234: break;
235: case InstructionConstants.OP_ASTORE:
236: opcode = (byte) (InstructionConstants.OP_ASTORE_0 + variableIndex);
237: break;
238: }
239: }
240:
241: // Only make the instruction wide if necessary.
242: wide = requiredVariableIndexSize() > 1
243: || requiredConstantSize() > 1;
244:
245: return this ;
246: }
247:
248: protected boolean isWide() {
249: return wide;
250: }
251:
252: protected void readInfo(byte[] code, int offset) {
253: int variableIndexSize = variableIndexSize();
254: int constantSize = constantSize();
255:
256: // Also initialize embedded variable indexes.
257: if (variableIndexSize == 0) {
258: // An embedded variable index can be decoded as follows.
259: variableIndex = opcode < InstructionConstants.OP_ISTORE_0 ? (opcode - InstructionConstants.OP_ILOAD_0) & 3
260: : (opcode - InstructionConstants.OP_ISTORE_0) & 3;
261: } else {
262: variableIndex = readValue(code, offset, variableIndexSize);
263: offset += variableIndexSize;
264: }
265:
266: constant = readSignedValue(code, offset, constantSize);
267: }
268:
269: protected void writeInfo(byte[] code, int offset) {
270: int variableIndexSize = variableIndexSize();
271: int constantSize = constantSize();
272:
273: if (requiredVariableIndexSize() > variableIndexSize) {
274: throw new IllegalArgumentException(
275: "Instruction has invalid variable index size ("
276: + this .toString(offset) + ")");
277: }
278:
279: if (requiredConstantSize() > constantSize) {
280: throw new IllegalArgumentException(
281: "Instruction has invalid constant size ("
282: + this .toString(offset) + ")");
283: }
284:
285: writeValue(code, offset, variableIndex, variableIndexSize);
286: offset += variableIndexSize;
287: writeSignedValue(code, offset, constant, constantSize);
288: }
289:
290: public int length(int offset) {
291: return (wide ? 2 : 1) + variableIndexSize() + constantSize();
292: }
293:
294: public void accept(Clazz clazz, Method method,
295: CodeAttribute codeAttribute, int offset,
296: InstructionVisitor instructionVisitor) {
297: instructionVisitor.visitVariableInstruction(clazz, method,
298: codeAttribute, offset, this );
299: }
300:
301: // Implementations for Object.
302:
303: public String toString() {
304: return getName() + (wide ? "_w" : "") + " v" + variableIndex
305: + (constantSize() > 0 ? ", " + constant : "");
306: }
307:
308: // Small utility methods.
309:
310: /**
311: * Returns the variable index size for this instruction.
312: */
313: private int variableIndexSize() {
314: return (opcode >= InstructionConstants.OP_ILOAD_0 && opcode <= InstructionConstants.OP_ALOAD_3)
315: || (opcode >= InstructionConstants.OP_ISTORE_0 && opcode <= InstructionConstants.OP_ASTORE_3) ? 0
316: : wide ? 2 : 1;
317: }
318:
319: /**
320: * Computes the required variable index size for this instruction's variable
321: * index.
322: */
323: private int requiredVariableIndexSize() {
324: return (variableIndex & 0x3) == variableIndex ? 0
325: : (variableIndex & 0xff) == variableIndex ? 1
326: : (variableIndex & 0xffff) == variableIndex ? 2
327: : 4;
328:
329: }
330:
331: /**
332: * Returns the constant size for this instruction.
333: */
334: private int constantSize() {
335: return opcode != InstructionConstants.OP_IINC ? 0 : wide ? 2
336: : 1;
337: }
338:
339: /**
340: * Computes the required constant size for this instruction's constant.
341: */
342: private int requiredConstantSize() {
343: return opcode != InstructionConstants.OP_IINC ? 0
344: : constant << 24 >> 24 == constant ? 1
345: : constant << 16 >> 16 == constant ? 2 : 4;
346: }
347: }
|