1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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.constant.*;
26import proguard.classfile.constant.visitor.ConstantVisitor;
27import proguard.classfile.instruction.visitor.InstructionVisitor;
28import proguard.classfile.util.ClassUtil;
29
30/**
31 * This Instruction represents an instruction that refers to an entry in the
32 * constant pool.
33 *
34 * @author Eric Lafortune
35 */
36public class ConstantInstruction extends Instruction
37implements   ConstantVisitor
38{
39    public int constantIndex;
40    public int constant;
41
42
43    // Fields acting as return parameters for the ConstantVisitor methods.
44    private int parameterStackDelta;
45    private int typeStackDelta;
46
47
48    /**
49     * Creates an uninitialized ConstantInstruction.
50     */
51    public ConstantInstruction() {}
52
53
54    /**
55     * Creates a new ConstantInstruction with the given opcode and constant pool
56     * index.
57     */
58    public ConstantInstruction(byte opcode, int constantIndex)
59    {
60        this(opcode, constantIndex, 0);
61    }
62
63
64    /**
65     * Creates a new ConstantInstruction with the given opcode, constant pool
66     * index, and constant.
67     */
68    public ConstantInstruction(byte opcode, int constantIndex, int constant)
69    {
70        this.opcode        = opcode;
71        this.constantIndex = constantIndex;
72        this.constant      = constant;
73    }
74
75
76    /**
77     * Copies the given instruction into this instruction.
78     * @param constantInstruction the instruction to be copied.
79     * @return this instruction.
80     */
81    public ConstantInstruction copy(ConstantInstruction constantInstruction)
82    {
83        this.opcode        = constantInstruction.opcode;
84        this.constantIndex = constantInstruction.constantIndex;
85        this.constant      = constantInstruction.constant;
86
87        return this;
88    }
89
90
91    // Implementations for Instruction.
92
93    public byte canonicalOpcode()
94    {
95        // Remove the _w extension, if any.
96        return
97            opcode == InstructionConstants.OP_LDC_W ? InstructionConstants.OP_LDC :
98                                                      opcode;
99    }
100
101    public Instruction shrink()
102    {
103        // Do we need a short index or a long index?
104        if (requiredConstantIndexSize() == 1)
105        {
106            // Can we replace the long instruction by a short instruction?
107            if (opcode == InstructionConstants.OP_LDC_W)
108            {
109                opcode = InstructionConstants.OP_LDC;
110            }
111        }
112        else
113        {
114            // Should we replace the short instruction by a long instruction?
115            if (opcode == InstructionConstants.OP_LDC)
116            {
117                opcode = InstructionConstants.OP_LDC_W;
118            }
119        }
120
121        return this;
122    }
123
124    protected void readInfo(byte[] code, int offset)
125    {
126        int constantIndexSize = constantIndexSize();
127        int constantSize      = constantSize();
128
129        constantIndex = readValue(code, offset, constantIndexSize);  offset += constantIndexSize;
130        constant      = readValue(code, offset, constantSize);
131    }
132
133
134    protected void writeInfo(byte[] code, int offset)
135    {
136        int constantIndexSize = constantIndexSize();
137        int constantSize      = constantSize();
138
139        if (requiredConstantIndexSize() > constantIndexSize)
140        {
141            throw new IllegalArgumentException("Instruction has invalid constant index size ("+this.toString(offset)+")");
142        }
143
144        writeValue(code, offset, constantIndex, constantIndexSize); offset += constantIndexSize;
145        writeValue(code, offset, constant,      constantSize);
146    }
147
148
149    public int length(int offset)
150    {
151        return 1 + constantIndexSize() + constantSize();
152    }
153
154
155    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
156    {
157        instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, this);
158    }
159
160
161    public int stackPopCount(Clazz clazz)
162    {
163        int stackPopCount = super.stackPopCount(clazz);
164
165        // Some special cases.
166        switch (opcode)
167        {
168            case InstructionConstants.OP_MULTIANEWARRAY:
169                // For each dimension, an integer size is popped from the stack.
170                stackPopCount += constant;
171                break;
172
173            case InstructionConstants.OP_PUTSTATIC:
174            case InstructionConstants.OP_PUTFIELD:
175                // The field value is be popped from the stack.
176                clazz.constantPoolEntryAccept(constantIndex, this);
177                stackPopCount += typeStackDelta;
178                break;
179
180            case InstructionConstants.OP_INVOKEVIRTUAL:
181            case InstructionConstants.OP_INVOKESPECIAL:
182            case InstructionConstants.OP_INVOKESTATIC:
183            case InstructionConstants.OP_INVOKEINTERFACE:
184            case InstructionConstants.OP_INVOKEDYNAMIC:
185                // Some parameters may be popped from the stack.
186                clazz.constantPoolEntryAccept(constantIndex, this);
187                stackPopCount += parameterStackDelta;
188                break;
189        }
190
191        return stackPopCount;
192    }
193
194
195    public int stackPushCount(Clazz clazz)
196    {
197        int stackPushCount = super.stackPushCount(clazz);
198
199        // Some special cases.
200        switch (opcode)
201        {
202            case InstructionConstants.OP_GETSTATIC:
203            case InstructionConstants.OP_GETFIELD:
204            case InstructionConstants.OP_INVOKEVIRTUAL:
205            case InstructionConstants.OP_INVOKESPECIAL:
206            case InstructionConstants.OP_INVOKESTATIC:
207            case InstructionConstants.OP_INVOKEINTERFACE:
208            case InstructionConstants.OP_INVOKEDYNAMIC:
209                // The field value or a return value may be pushed onto the stack.
210                clazz.constantPoolEntryAccept(constantIndex, this);
211                stackPushCount += typeStackDelta;
212                break;
213        }
214
215        return stackPushCount;
216    }
217
218
219    // Implementations for ConstantVisitor.
220
221    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) {}
222    public void visitLongConstant(Clazz clazz, LongConstant longConstant) {}
223    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) {}
224    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) {}
225    public void visitStringConstant(Clazz clazz, StringConstant stringConstant) {}
226    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) {}
227    public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) {}
228    public void visitClassConstant(Clazz clazz, ClassConstant classConstant) {}
229    public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) {}
230
231
232    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
233    {
234        String type = fieldrefConstant.getType(clazz);
235
236        typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
237    }
238
239
240    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
241    {
242        clazz.constantPoolEntryAccept(invokeDynamicConstant.u2nameAndTypeIndex, this);
243    }
244
245
246    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
247    {
248        clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2nameAndTypeIndex, this);
249    }
250
251
252    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
253    {
254        clazz.constantPoolEntryAccept(methodrefConstant.u2nameAndTypeIndex, this);
255    }
256
257
258    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
259    {
260        String type = nameAndTypeConstant.getType(clazz);
261
262        parameterStackDelta = ClassUtil.internalMethodParameterSize(type);
263        typeStackDelta      = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
264    }
265
266
267    // Implementations for Object.
268
269    public String toString()
270    {
271        return getName()+" #"+constantIndex+(constantSize() == 0 ? "" : ", "+constant);
272    }
273
274
275    // Small utility methods.
276
277    /**
278     * Returns the constant pool index size for this instruction.
279     */
280    private int constantIndexSize()
281    {
282        return opcode == InstructionConstants.OP_LDC ? 1 :
283                                                       2;
284    }
285
286
287    /**
288     * Returns the constant size for this instruction.
289     */
290    private int constantSize()
291    {
292        return opcode == InstructionConstants.OP_MULTIANEWARRAY  ? 1 :
293               opcode == InstructionConstants.OP_INVOKEDYNAMIC ||
294               opcode == InstructionConstants.OP_INVOKEINTERFACE ? 2 :
295                                                                   0;
296    }
297
298
299    /**
300     * Computes the required constant pool index size for this instruction's
301     * constant pool index.
302     */
303    private int requiredConstantIndexSize()
304    {
305        return (constantIndex &   0xff) == constantIndex ? 1 :
306               (constantIndex & 0xffff) == constantIndex ? 2 :
307                                                           4;
308    }
309}
310