1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.cf.code;
18 
19 import com.android.dx.util.IntList;
20 import com.android.dx.util.MutabilityControl;
21 
22 /**
23  * List of (value, target) mappings representing the choices of a
24  * {@code tableswitch} or {@code lookupswitch} instruction. It
25  * also holds the default target for the switch.
26  */
27 public final class SwitchList extends MutabilityControl {
28     /** {@code non-null;} list of test values */
29     private final IntList values;
30 
31     /**
32      * {@code non-null;} list of targets corresponding to the test values; there
33      * is always one extra element in the target list, to hold the
34      * default target
35      */
36     private final IntList targets;
37 
38     /** ultimate size of the list */
39     private int size;
40 
41     /**
42      * Constructs an instance.
43      *
44      * @param size {@code >= 0;} the number of elements to be in the table
45      */
SwitchList(int size)46     public SwitchList(int size) {
47         super(true);
48         this.values = new IntList(size);
49         this.targets = new IntList(size + 1);
50         this.size = size;
51     }
52 
53     /** {@inheritDoc} */
54     @Override
setImmutable()55     public void setImmutable() {
56         values.setImmutable();
57         targets.setImmutable();
58         super.setImmutable();
59     }
60 
61     /**
62      * Gets the size of the list.
63      *
64      * @return {@code >= 0;} the list size
65      */
size()66     public int size() {
67         return size;
68     }
69 
70     /**
71      * Gets the indicated test value.
72      *
73      * @param n {@code >= 0;}, < size(); which index
74      * @return the test value
75      */
getValue(int n)76     public int getValue(int n) {
77         return values.get(n);
78     }
79 
80     /**
81      * Gets the indicated target. Asking for the target at {@code size()}
82      * returns the default target.
83      *
84      * @param n {@code >= 0, <= size();} which index
85      * @return {@code >= 0;} the target
86      */
getTarget(int n)87     public int getTarget(int n) {
88         return targets.get(n);
89     }
90 
91     /**
92      * Gets the default target. This is just a shorthand for
93      * {@code getTarget(size())}.
94      *
95      * @return {@code >= 0;} the default target
96      */
getDefaultTarget()97     public int getDefaultTarget() {
98         return targets.get(size);
99     }
100 
101     /**
102      * Gets the list of all targets. This includes one extra element at the
103      * end of the list, which holds the default target.
104      *
105      * @return {@code non-null;} the target list
106      */
getTargets()107     public IntList getTargets() {
108         return targets;
109     }
110 
111     /**
112      * Gets the list of all case values.
113      *
114      * @return {@code non-null;} the case value list
115      */
getValues()116     public IntList getValues() {
117         return values;
118     }
119 
120     /**
121      * Sets the default target. It is only valid to call this method
122      * when all the non-default elements have been set.
123      *
124      * @param target {@code >= 0;} the absolute (not relative) default target
125      * address
126      */
setDefaultTarget(int target)127     public void setDefaultTarget(int target) {
128         throwIfImmutable();
129 
130         if (target < 0) {
131             throw new IllegalArgumentException("target < 0");
132         }
133 
134         if (targets.size() != size) {
135             throw new RuntimeException("non-default elements not all set");
136         }
137 
138         targets.add(target);
139     }
140 
141     /**
142      * Adds the given item.
143      *
144      * @param value the test value
145      * @param target {@code >= 0;} the absolute (not relative) target address
146      */
add(int value, int target)147     public void add(int value, int target) {
148         throwIfImmutable();
149 
150         if (target < 0) {
151             throw new IllegalArgumentException("target < 0");
152         }
153 
154         values.add(value);
155         targets.add(target);
156     }
157 
158     /**
159      * Shrinks this instance if possible, removing test elements that
160      * refer to the default target. This is only valid after the instance
161      * is fully populated, including the default target (naturally).
162      */
removeSuperfluousDefaults()163     public void removeSuperfluousDefaults() {
164         throwIfImmutable();
165 
166         int sz = size;
167 
168         if (sz != (targets.size() - 1)) {
169             throw new IllegalArgumentException("incomplete instance");
170         }
171 
172         int defaultTarget = targets.get(sz);
173         int at = 0;
174 
175         for (int i = 0; i < sz; i++) {
176             int target = targets.get(i);
177             if (target != defaultTarget) {
178                 if (i != at) {
179                     targets.set(at, target);
180                     values.set(at, values.get(i));
181                 }
182                 at++;
183             }
184         }
185 
186         if (at != sz) {
187             values.shrink(at);
188             targets.set(at, defaultTarget);
189             targets.shrink(at + 1);
190             size = at;
191         }
192     }
193 }
194