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 SparseSwitchDataPseudoInstruction extends Instruction implements MultiOffsetInstruction {
41    public static final Instruction.InstructionFactory Factory = new Factory();
42    private int[] keys;
43    private int[] targets;
44
45    @Override
46    public int getSize(int codeAddress) {
47        return getTargetCount() * 4 + 2 + (codeAddress % 2);
48    }
49
50    public SparseSwitchDataPseudoInstruction(int[] keys, int[] targets) {
51        super(Opcode.NOP);
52
53        if (keys.length != targets.length) {
54            throw new RuntimeException("The number of keys and targets don't match");
55        }
56
57        if (targets.length == 0) {
58            throw new RuntimeException("The sparse-switch data must contain at least 1 key/target");
59        }
60
61        if (targets.length > 0xFFFF) {
62            throw new RuntimeException("The sparse-switch data contains too many elements. " +
63                    "The maximum number of switch elements is 65535");
64        }
65
66        this.keys = keys;
67        this.targets = targets;
68    }
69
70    public SparseSwitchDataPseudoInstruction(byte[] buffer, int bufferIndex) {
71        super(Opcode.NOP);
72
73        byte opcodeByte = buffer[bufferIndex];
74        if (opcodeByte != 0x00) {
75            throw new RuntimeException("Invalid opcode byte for a SparseSwitchData pseudo-instruction");
76        }
77        byte subopcodeByte = buffer[bufferIndex+1];
78        if (subopcodeByte != 0x02) {
79            throw new RuntimeException("Invalid sub-opcode byte for a SparseSwitchData pseudo-instruction");
80        }
81
82        int targetCount = NumberUtils.decodeUnsignedShort(buffer, bufferIndex + 2);
83        keys = new int[targetCount];
84        targets = new int[targetCount];
85
86        for (int i=0; i<targetCount; i++) {
87            keys[i] = NumberUtils.decodeInt(buffer, bufferIndex + 4 + i*4);
88            targets[i] = NumberUtils.decodeInt(buffer, bufferIndex + 4 + targetCount*4 + i*4);
89        }
90    }
91
92    protected void writeInstruction(AnnotatedOutput out, int currentCodeAddress) {
93        out.alignTo(4);
94
95        out.writeByte(0x00);
96        out.writeByte(0x02);
97        out.writeShort(targets.length);
98
99        if (targets.length > 0) {
100            int key = keys[0];
101
102            out.writeInt(key);
103
104            for (int i = 1; i < keys.length; i++) {
105                key = keys[i];
106                assert key >= keys[i - 1];
107                out.writeInt(key);
108            }
109
110            for (int target : targets) {
111                out.writeInt(target);
112            }
113        }
114    }
115
116    protected void annotateInstruction(AnnotatedOutput out, int currentCodeAddress) {
117        out.annotate(getSize(currentCodeAddress)*2, "[0x" + Integer.toHexString(currentCodeAddress) + "] " +
118                "sparse-switch-data instruction");
119    }
120
121    public void updateTarget(int targetIndex, int targetAddressOffset) {
122        targets[targetIndex] = targetAddressOffset;
123    }
124
125    public Format getFormat() {
126        return Format.SparseSwitchData;
127    }
128
129    public int getTargetCount() {
130        return targets.length;
131    }
132
133    public int[] getTargets() {
134        return targets;
135    }
136
137    public int[] getKeys() {
138        return keys;
139    }
140
141    public static class SparseSwitchTarget {
142        public int key;
143        public int targetAddressOffset;
144    }
145
146    public Iterator<SparseSwitchTarget> iterateKeysAndTargets() {
147        return new Iterator<SparseSwitchTarget>() {
148            final int targetCount = getTargetCount();
149            int i = 0;
150
151            SparseSwitchTarget sparseSwitchTarget = new SparseSwitchTarget();
152
153            public boolean hasNext() {
154                return i<targetCount;
155            }
156
157            public SparseSwitchTarget next() {
158                sparseSwitchTarget.key = keys[i];
159                sparseSwitchTarget.targetAddressOffset = targets[i];
160                i++;
161                return sparseSwitchTarget;
162            }
163
164            public void remove() {
165            }
166        };
167    }
168
169    private static class Factory implements Instruction.InstructionFactory {
170        public Instruction makeInstruction(DexFile dexFile, Opcode opcode, byte[] buffer, int bufferIndex) {
171            if (opcode != Opcode.NOP) {
172                throw new RuntimeException("The opcode for a SparseSwitchDataPseudoInstruction must be NOP");
173            }
174            return new SparseSwitchDataPseudoInstruction(buffer, bufferIndex);
175        }
176    }
177}
178