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