SimpleInstruction.java revision db267bc191f906f55eaef21a27110cce2ec57fdf
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 a simple instruction without variable arguments
29 * or constant pool references.
30 *
31 * @author Eric Lafortune
32 */
33public class SimpleInstruction extends Instruction
34{
35    public int constant;
36
37
38    /**
39     * Creates an uninitialized SimpleInstruction.
40     */
41    public SimpleInstruction() {}
42
43
44    /**
45     * Creates a new SimpleInstruction with the given opcode.
46     */
47    public SimpleInstruction(byte opcode)
48    {
49        this(opcode, embeddedConstant(opcode));
50    }
51
52
53    /**
54     * Creates a new SimpleInstruction with the given opcode and constant.
55     */
56    public SimpleInstruction(byte opcode, int constant)
57    {
58        this.opcode   = opcode;
59        this.constant = constant;
60    }
61
62
63    /**
64     * Copies the given instruction into this instruction.
65     * @param simpleInstruction the instruction to be copied.
66     * @return this instruction.
67     */
68    public SimpleInstruction copy(SimpleInstruction simpleInstruction)
69    {
70        this.opcode   = simpleInstruction.opcode;
71        this.constant = simpleInstruction.constant;
72
73        return this;
74    }
75
76
77    /**
78     * Return the embedded constant of the given opcode, or 0 if the opcode
79     * doesn't have one.
80     */
81    private static int embeddedConstant(byte opcode)
82    {
83        switch (opcode)
84        {
85            case InstructionConstants.OP_ICONST_M1: return -1;
86
87            case InstructionConstants.OP_ICONST_1:
88            case InstructionConstants.OP_LCONST_1:
89            case InstructionConstants.OP_FCONST_1:
90            case InstructionConstants.OP_DCONST_1: return 1;
91
92            case InstructionConstants.OP_ICONST_2:
93            case InstructionConstants.OP_FCONST_2: return 2;
94
95            case InstructionConstants.OP_ICONST_3: return 3;
96
97            case InstructionConstants.OP_ICONST_4: return 4;
98
99            case InstructionConstants.OP_ICONST_5: return 5;
100
101            default: return 0;
102        }
103    }
104
105
106    // Implementations for Instruction.
107
108    public byte canonicalOpcode()
109    {
110        // Replace any _1, _2, _3,... extension by _0.
111        switch (opcode)
112        {
113            case InstructionConstants.OP_ICONST_M1:
114            case InstructionConstants.OP_ICONST_0:
115            case InstructionConstants.OP_ICONST_1:
116            case InstructionConstants.OP_ICONST_2:
117            case InstructionConstants.OP_ICONST_3:
118            case InstructionConstants.OP_ICONST_4:
119            case InstructionConstants.OP_ICONST_5:
120            case InstructionConstants.OP_BIPUSH:
121            case InstructionConstants.OP_SIPUSH:   return InstructionConstants.OP_ICONST_0;
122
123            case InstructionConstants.OP_LCONST_0:
124            case InstructionConstants.OP_LCONST_1: return InstructionConstants.OP_LCONST_0;
125
126            case InstructionConstants.OP_FCONST_0:
127            case InstructionConstants.OP_FCONST_1:
128            case InstructionConstants.OP_FCONST_2: return InstructionConstants.OP_FCONST_0;
129
130            case InstructionConstants.OP_DCONST_0:
131            case InstructionConstants.OP_DCONST_1: return InstructionConstants.OP_DCONST_0;
132
133            default: return opcode;
134        }
135    }
136
137    public Instruction shrink()
138    {
139        // Reconstruct the opcode of the shortest instruction, if there are
140        // any alternatives.
141        switch (opcode)
142        {
143            case InstructionConstants.OP_ICONST_M1:
144            case InstructionConstants.OP_ICONST_0:
145            case InstructionConstants.OP_ICONST_1:
146            case InstructionConstants.OP_ICONST_2:
147            case InstructionConstants.OP_ICONST_3:
148            case InstructionConstants.OP_ICONST_4:
149            case InstructionConstants.OP_ICONST_5:
150            case InstructionConstants.OP_BIPUSH:
151            case InstructionConstants.OP_SIPUSH:
152                switch (requiredConstantSize())
153                {
154                    case 0:
155                        opcode = (byte)(InstructionConstants.OP_ICONST_0 + constant);
156                        break;
157                    case 1:
158                        opcode = InstructionConstants.OP_BIPUSH;
159                        break;
160                    case 2:
161                        opcode = InstructionConstants.OP_SIPUSH;
162                        break;
163                }
164                break;
165
166            case InstructionConstants.OP_LCONST_0:
167            case InstructionConstants.OP_LCONST_1:
168                opcode = (byte)(InstructionConstants.OP_LCONST_0 + constant);
169                break;
170
171            case InstructionConstants.OP_FCONST_0:
172            case InstructionConstants.OP_FCONST_1:
173            case InstructionConstants.OP_FCONST_2:
174                opcode = (byte)(InstructionConstants.OP_FCONST_0 + constant);
175                break;
176
177            case InstructionConstants.OP_DCONST_0:
178            case InstructionConstants.OP_DCONST_1:
179                opcode = (byte)(InstructionConstants.OP_DCONST_0 + constant);
180                break;
181        }
182
183        return this;
184    }
185
186    protected void readInfo(byte[] code, int offset)
187    {
188        int constantSize = constantSize();
189
190        // Also initialize embedded constants that are different from 0.
191        constant = constantSize == 0 ?
192            embeddedConstant(opcode) :
193            readSignedValue(code, offset, constantSize);
194    }
195
196
197    protected void writeInfo(byte[] code, int offset)
198    {
199        int constantSize = constantSize();
200
201        if (requiredConstantSize() > constantSize)
202        {
203            throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")");
204        }
205
206        writeSignedValue(code, offset, constant, constantSize);
207    }
208
209
210    public int length(int offset)
211    {
212        return 1 + constantSize();
213    }
214
215
216    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
217    {
218        instructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, this);
219    }
220
221
222    // Implementations for Object.
223
224    public String toString()
225    {
226        return getName() +
227               (constantSize() > 0 ? " "+constant : "");
228    }
229
230
231    // Small utility methods.
232
233    /**
234     * Returns the constant size for this instruction.
235     */
236    private int constantSize()
237    {
238        return opcode == InstructionConstants.OP_BIPUSH ||
239               opcode == InstructionConstants.OP_NEWARRAY ? 1 :
240               opcode == InstructionConstants.OP_SIPUSH   ? 2 :
241                                                            0;
242    }
243
244
245    /**
246     * Computes the required constant size for this instruction.
247     */
248    private int requiredConstantSize()
249    {
250        return constant >= -1 && constant <= 5  ? 0 :
251               constant << 24 >> 24 == constant ? 1 :
252               constant << 16 >> 16 == constant ? 2 :
253                                                  4;
254    }
255}
256