Instruction.java revision 281b510a9c2b4ae914ab28b9a4f4d622e5861da6
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib.Code;
30
31import org.jf.dexlib.*;
32import org.jf.dexlib.Util.NumberUtils;
33import org.jf.dexlib.Code.Format.Format;
34
35public abstract class Instruction {
36    private DexFile dexFile;
37    private Opcode opcode;
38    private IndexedItem referencedItem;
39    protected byte[] encodedInstruction;
40
41    public int getSize() {
42        return encodedInstruction.length;
43    }
44
45    public Opcode getOpcode() {
46        return opcode;
47    }
48
49    public IndexedItem getReferencedItem() {
50        return referencedItem;
51    }
52
53    protected void setReferencedItem(IndexedItem referencedItem) {
54        checkReferenceType(referencedItem, this.opcode);
55        this.referencedItem = referencedItem;
56    }
57
58    protected Instruction(DexFile dexFile, Opcode opcode, IndexedItem referencedItem) {
59        this.dexFile = dexFile;
60        this.opcode = opcode;
61        this.referencedItem = referencedItem;
62
63        checkFormat(opcode.format);
64        checkReferenceType(referencedItem, this.opcode);
65    }
66
67    protected void checkFormat(Format format) {
68        if (format != getFormat()) {
69            throw new RuntimeException(opcode.name + " does not use " + getFormat().toString());
70        }
71    }
72
73    protected Instruction(DexFile dexFile, Opcode opcode, byte[] rest) {
74        this.dexFile = dexFile;
75        this.opcode = opcode;
76
77        if ((rest.length + 1) != opcode.format.size) {
78            throw new RuntimeException("Invalid instruction size. This opcode is " +
79                    Integer.toString(rest.length + 1) + " bytes, but the opcode should be " +
80                    Integer.toString(opcode.format.size) + " bytes.");
81        }
82
83        this.encodedInstruction = new byte[rest.length + 1];
84        encodedInstruction[0] = opcode.value;
85        System.arraycopy(rest, 0, encodedInstruction, 1, rest.length);
86
87        if (opcode.referenceType != ReferenceType.none) {
88            int itemIndex = NumberUtils.decodeUnsignedShort(encodedInstruction[2], encodedInstruction[3]);
89            getReferencedItem(dexFile, opcode, itemIndex);
90        }
91    }
92
93    protected Instruction() {
94        //this should only be used to make a blank clone within cloneTo()
95    }
96
97    private void checkReferenceType(IndexedItem referencedItem, Opcode opcode) {
98        switch (opcode.referenceType) {
99            case field:
100                if (!(referencedItem instanceof FieldIdItem)) {
101                    throw new RuntimeException(referencedItem.getClass().getSimpleName() +
102                            " is the wrong item type for opcode " + opcode.name + ". Expecting FieldIdItem.");
103                }
104                return;
105            case method:
106                if (!(referencedItem instanceof MethodIdItem)) {
107                    throw new RuntimeException(referencedItem.getClass().getSimpleName() +
108                            " is the wrong item type for opcode " + opcode.name + ". Expecting MethodIdItem.");
109                }
110                return;
111            case type:
112                if (!(referencedItem instanceof TypeIdItem)) {
113                    throw new RuntimeException(referencedItem.getClass().getSimpleName() +
114                            " is the wrong item type for opcode " + opcode.name + ". Expecting TypeIdItem.");
115                }
116                return;
117            case string:
118                if (!(referencedItem instanceof StringIdItem)) {
119                    throw new RuntimeException(referencedItem.getClass().getSimpleName() +
120                            " is the wrong item type for opcode " + opcode.name + ". Expecting StringIdItem.");
121                }
122                return;
123            default:
124                if (referencedItem != null) {
125                    throw new RuntimeException(referencedItem.getClass().getSimpleName() +
126                            " is invalid for opcode " + opcode.name + ". This opcode does not reference an item");
127                }
128                return;
129        }
130    }
131
132    private void getReferencedItem(DexFile dexFile, Opcode opcode, int itemIndex) {
133        switch (opcode.referenceType) {
134            case field:
135                referencedItem = dexFile.FieldIdsSection.getByIndex(itemIndex);
136                return;
137            case method:
138                referencedItem = dexFile.MethodIdsSection.getByIndex(itemIndex);
139                return;
140            case type:
141                referencedItem = dexFile.TypeIdsSection.getByIndex(itemIndex);
142                return;
143            case string:
144                referencedItem = dexFile.StringIdsSection.getByIndex(itemIndex);
145                return;
146        }
147        return;
148    }
149
150    public abstract Format getFormat();
151
152    public static interface InstructionFactory {
153        public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] rest);
154    }
155
156    public Instruction cloneTo(DexFile dexFile) {
157        Instruction clone = makeClone();
158        clone.encodedInstruction = encodedInstruction.clone();
159        clone.dexFile = dexFile;
160        clone.opcode = opcode;
161        if (referencedItem != null) {
162        switch (opcode.referenceType) {
163                case string:
164                    clone.referencedItem = dexFile.StringIdsSection.intern((StringIdItem)referencedItem);
165                    break;
166                case type:
167                    clone.referencedItem = dexFile.TypeIdsSection.intern((TypeIdItem)referencedItem);
168                    break;
169                case field:
170                    clone.referencedItem = dexFile.FieldIdsSection.intern((FieldIdItem)referencedItem);
171                    break;
172                case method:
173                    clone.referencedItem = dexFile.MethodIdsSection.intern((MethodIdItem)referencedItem);
174                    break;
175                case none:
176                    break;
177            }
178        }
179
180        return clone;
181    }
182
183    protected abstract Instruction makeClone();
184
185//    public void readFrom(Input in) {
186//        int startPos = in.getCursor();
187//
188//        byte opByte = in.readByte();
189//
190//        if (opByte == 0x00) {
191//            reference = null;
192//            byte secondByte = in.readByte();
193//
194//            int count;
195//
196//
197//            switch (secondByte) {
198//                case 0x00:
199//                    /** nop */
200//                    bytes = new byte[] { 0x00, 0x00 };
201//                    return;
202//                case 0x01:
203//                    /** packed switch */
204//                    count = in.readShort();
205//                    in.setCursor(startPos);
206//                    bytes = in.readBytes((count * 4) + 8);
207//                    return;
208//                case 0x02:
209//                    /** sparse switch */
210//                    count = in.readShort();
211//                    in.setCursor(startPos);
212//                    bytes = in.readBytes((count * 8) + 4);
213//                    return;
214//                case 0x03:
215//                    /** fill array data */
216//                    int elementWidth = in.readShort();
217//                    count = in.readInt();
218//                    in.setCursor(startPos);
219//                    bytes = in.readBytes(((elementWidth * count + 1)/2 + 4) * 2);
220//                    return;
221//                default:
222//                    throw new RuntimeException("Invalid 2nd byte for opcode 0x00");
223//            }
224//        }
225//
226//        this.opcode = Opcode.getOpcodeByValue(opByte);
227//
228//        if (opcode.referenceType != ReferenceType.none) {
229//            in.skipBytes(1);
230//            int referenceIndex = in.readShort();
231//
232//            //handle const-string/jumbo as a special case
233//            if (opByte == 0x1b) {
234//                int hiWord = in.readShort();
235//                if (hiWord != 0) {
236//                    referenceIndex += (hiWord<<16);
237//                }
238//            }
239//
240//            switch (opcode.referenceType) {
241//                case string:
242//                    reference = dexFile.StringIdsSection.getByIndex(referenceIndex);
243//                    break;
244//                case type:
245//                    reference = dexFile.TypeIdsSection.getByIndex(referenceIndex);
246//                    break;
247//                case field:
248//                    reference = dexFile.FieldIdsSection.getByIndex(referenceIndex);
249//                    break;
250//                case method:
251//                    reference = dexFile.MethodIdsSection.getByIndex(referenceIndex);
252//                    break;
253//            }
254//        } else {
255//            reference = null;
256//        }
257//
258//        in.setCursor(startPos);
259//        bytes = in.readBytes(opcode.numBytes);
260//    }
261//
262//    public void writeTo(AnnotatedOutput out) {
263//        out.annotate(bytes.length, "instruction");
264//        if (needsAlign()) {
265//            //the "special instructions" must be 4 byte aligned
266//            out.alignTo(4);
267//            out.write(bytes);
268//        } else if (reference == null) {
269//            out.write(bytes);
270//        } else {
271//            out.write(bytes,0,2);
272//            //handle const-string/jumbo as a special case
273//            if (bytes[0] == 0x1b) {
274//                out.writeInt(reference.getIndex());
275//            } else {
276//                int index = reference.getIndex();
277//                if (index > 0xFFFF) {
278//                    throw new RuntimeException("String index doesn't fit.");
279//                }
280//                out.writeShort(reference.getIndex());
281//                out.write(bytes, 4, bytes.length - 4);
282//            }
283//        }
284//    }
285//
286//    public void copyTo(DexFile dexFile, Instruction copy) {
287//        copy.bytes = bytes;
288//        copy.opcode = opcode;
289//
290//        switch (opcode.referenceType) {
291//                case string:
292//                    copy.reference = dexFile.StringIdsSection.intern(dexFile, (StringIdItem)reference);
293//                    break;
294//                case type:
295//                    copy.reference = dexFile.TypeIdsSection.intern(dexFile, (TypeIdItem)reference);
296//                    break;
297//                case field:
298//                    copy.reference = dexFile.FieldIdsSection.intern(dexFile, (FieldIdItem)reference);
299//                    break;
300//                case method:
301//                    copy.reference = dexFile.MethodIdsSection.intern(dexFile, (MethodIdItem)reference);
302//                    break;
303//                case none:
304//                    break;
305//            }
306//    }
307//
308//    public int place(int offset) {
309//        return offset + getSize(offset);
310//    }
311//
312//    public int getSize(int offset) {
313//        if (this.needsAlign() && (offset % 4) != 0) {
314//            return bytes.length + 2;
315//        } else {
316//            return bytes.length;
317//        }
318//    }
319//
320//    private boolean needsAlign() {
321//        //true if the opcode is one of the "special format" opcodes
322//        return bytes[0] == 0 && bytes[1] > 0;
323//    }
324}
325