1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2010 Ben Gruver (JesusFreke)
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.Format;
30
31import org.jf.dexlib.Code.Instruction;
32import org.jf.dexlib.Code.MultiOffsetInstruction;
33import org.jf.dexlib.Code.Opcode;
34import org.jf.dexlib.DexFile;
35import org.jf.dexlib.Util.AnnotatedOutput;
36import org.jf.dexlib.Util.NumberUtils;
37
38import java.util.Iterator;
39
40public class PackedSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction {
41    public static final Instruction.InstructionFactory Factory = new Factory();
42    private int firstKey;
43    private int[] targets;
44
45    @Override
46    public int getSize(int codeAddress) {
47        return getTargetCount() * 2 + 4 + (codeAddress % 2);
48    }
49
50    public PackedSwitchDataPseudoInstruction(int firstKey, int[] targets) {
51        super(Opcode.NOP);
52
53        if (targets.length > 0xFFFF) {
54            throw new RuntimeException("The packed-switch data contains too many elements. " +
55                    "The maximum number of switch elements is 65535");
56        }
57
58        this.firstKey = firstKey;
59        this.targets = targets;
60    }
61
62    public PackedSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
63        super(Opcode.NOP);
64
65        byte opcodeByte = buffer[bufferIndex];
66        if (opcodeByte != 0x00) {
67            throw new RuntimeException("Invalid opcode byte for a PackedSwitchData pseudo-instruction");
68        }
69        byte subopcodeByte = buffer[bufferIndex+1];
70        if (subopcodeByte != 0x01) {
71            throw new RuntimeException("Invalid sub-opcode byte for a PackedSwitchData pseudo-instruction");
72        }
73
74        int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
75        this.firstKey = NumberUtils.decodeInt(buffer, bufferIndex + 4);
76        this.targets = new int[targetCount];
77
78        for (int i = 0; i<targetCount; i++) {
79            targets[i] = NumberUtils.decodeInt(buffer, bufferIndex + 8 + 4*i);
80        }
81    }
82
83    protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
84        out.alignTo(4);
85
86        out.writeByte(0x00);
87        out.writeByte(0x01);
88        out.writeShort(targets.length);
89        out.writeInt(firstKey);
90
91        for (int target : targets) {
92            out.writeInt(target);
93        }
94    }
95
96    protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) {
97        out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " +
98                "packed-switch-data instruction");
99    }
100
101    public void updateTarget(int targetIndex, int targetAddressOffset) {
102        targets[targetIndex] = targetAddressOffset;
103    }
104
105    public Format getFormat() {
106        return Format.PackedSwitchData;
107    }
108
109    public int getTargetCount() {
110        return targets.length;
111    }
112
113    public int getFirstKey() {
114        return firstKey;
115    }
116
117    public int[] getTargets() {
118        return targets;
119    }
120
121    public static class PackedSwitchTarget {
122        public int value;
123        public int targetAddressOffset;
124    }
125
126    public Iterator<PackedSwitchTarget> iterateKeysAndTargets() {
127        return new Iterator<PackedSwitchTarget>() {
128            final int targetCount = getTargetCount();
129            int i = 0;
130            int value = getFirstKey();
131
132            PackedSwitchTarget packedSwitchTarget = new PackedSwitchTarget();
133
134            public boolean hasNext() {
135                return i<targetCount;
136            }
137
138            public PackedSwitchTarget next() {
139                packedSwitchTarget.value = value++;
140                packedSwitchTarget.targetAddressOffset = targets[i];
141                i++;
142                return packedSwitchTarget;
143            }
144
145            public void remove() {
146            }
147        };
148    }
149
150    private static class Factory implements Instruction.InstructionFactory {
151        public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
152            if (opcode != Opcode.NOP) {
153                throw new RuntimeException("The opcode for a PackedSwitchDataPseudoInstruction must be NOP");
154            }
155            return new PackedSwitchDataPseudoInstruction(buffer, bufferIndex);
156        }
157    }
158}
159