• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.rop.type.Type;
20 import com.android.dx.rop.type.TypeBearer;
21 import com.android.dx.util.ExceptionWithContext;
22 import com.android.dx.util.Hex;
23 import com.android.dx.util.MutabilityControl;
24 
25 /**
26  * Representation of a Java method execution stack.
27  *
28  * <p><b>Note:</b> For the most part, the documentation for this class
29  * ignores the distinction between {@link Type} and {@link
30  * TypeBearer}.</p>
31  */
32 public final class ExecutionStack extends MutabilityControl {
33     /** {@code non-null;} array of stack contents */
34     private final TypeBearer[] stack;
35 
36     /**
37      * {@code >= 0;} stack pointer (points one past the end) / current stack
38      * size
39      */
40     private int stackPtr;
41 
42     /**
43      * Constructs an instance.
44      *
45      * @param maxStack {@code >= 0;} the maximum size of the stack for this
46      * instance
47      */
ExecutionStack(int maxStack)48     public ExecutionStack(int maxStack) {
49         super(maxStack != 0);
50         stack = new TypeBearer[maxStack];
51         stackPtr = 0;
52     }
53 
54     /**
55      * Makes and returns a mutable copy of this instance.
56      *
57      * @return {@code non-null;} the copy
58      */
copy()59     public ExecutionStack copy() {
60         ExecutionStack result = new ExecutionStack(stack.length);
61 
62         System.arraycopy(stack, 0, result.stack, 0, stack.length);
63         result.stackPtr = stackPtr;
64 
65         return result;
66     }
67 
68     /**
69      * Annotates (adds context to) the given exception with information
70      * about this instance.
71      *
72      * @param ex {@code non-null;} the exception to annotate
73      */
annotate(ExceptionWithContext ex)74     public void annotate(ExceptionWithContext ex) {
75         int limit = stackPtr - 1;
76 
77         for (int i = 0; i <= limit; i++) {
78             String idx = (i == limit) ? "top0" : Hex.u2(limit - i);
79 
80             ex.addContext("stack[" + idx + "]: " +
81                           stackElementString(stack[i]));
82         }
83     }
84 
85     /**
86      * Replaces all the occurrences of the given uninitialized type in
87      * this stack with its initialized equivalent.
88      *
89      * @param type {@code non-null;} type to replace
90      */
makeInitialized(Type type)91     public void makeInitialized(Type type) {
92         if (stackPtr == 0) {
93             // We have to check for this before checking for immutability.
94             return;
95         }
96 
97         throwIfImmutable();
98 
99         Type initializedType = type.getInitializedType();
100 
101         for (int i = 0; i < stackPtr; i++) {
102             if (stack[i] == type) {
103                 stack[i] = initializedType;
104             }
105         }
106     }
107 
108     /**
109      * Gets the maximum stack size for this instance.
110      *
111      * @return {@code >= 0;} the max stack size
112      */
getMaxStack()113     public int getMaxStack() {
114         return stack.length;
115     }
116 
117     /**
118      * Gets the current stack size.
119      *
120      * @return {@code >= 0, < getMaxStack();} the current stack size
121      */
size()122     public int size() {
123         return stackPtr;
124     }
125 
126     /**
127      * Clears the stack. (That is, this method pops everything off.)
128      */
clear()129     public void clear() {
130         throwIfImmutable();
131 
132         for (int i = 0; i < stackPtr; i++) {
133             stack[i] = null;
134         }
135 
136         stackPtr = 0;
137     }
138 
139     /**
140      * Pushes a value of the given type onto the stack.
141      *
142      * @param type {@code non-null;} type of the value
143      * @throws SimException thrown if there is insufficient room on the
144      * stack for the value
145      */
push(TypeBearer type)146     public void push(TypeBearer type) {
147         throwIfImmutable();
148 
149         int category;
150 
151         try {
152             type = type.getFrameType();
153             category = type.getType().getCategory();
154         } catch (NullPointerException ex) {
155             // Elucidate the exception.
156             throw new NullPointerException("type == null");
157         }
158 
159         if ((stackPtr + category) > stack.length) {
160             throwSimException("overflow");
161             return;
162         }
163 
164         if (category == 2) {
165             stack[stackPtr] = null;
166             stackPtr++;
167         }
168 
169         stack[stackPtr] = type;
170         stackPtr++;
171     }
172 
173     /**
174      * Peeks at the {@code n}th element down from the top of the stack.
175      * {@code n == 0} means to peek at the top of the stack. Note that
176      * this will return {@code null} if the indicated element is the
177      * deeper half of a category-2 value.
178      *
179      * @param n {@code >= 0;} which element to peek at
180      * @return {@code null-ok;} the type of value stored at that element
181      * @throws SimException thrown if {@code n >= size()}
182      */
peek(int n)183     public TypeBearer peek(int n) {
184         if (n < 0) {
185             throw new IllegalArgumentException("n < 0");
186         }
187 
188         if (n >= stackPtr) {
189             return throwSimException("underflow");
190         }
191 
192         return stack[stackPtr - n - 1];
193     }
194 
195     /**
196      * Peeks at the {@code n}th element down from the top of the
197      * stack, returning the type per se, as opposed to the
198      * <i>type-bearer</i>.  This method is just a convenient shorthand
199      * for {@code peek(n).getType()}.
200      *
201      * @see #peek
202      */
peekType(int n)203     public Type peekType(int n) {
204         return peek(n).getType();
205     }
206 
207     /**
208      * Pops the top element off of the stack.
209      *
210      * @return {@code non-null;} the type formerly on the top of the stack
211      * @throws SimException thrown if the stack is empty
212      */
pop()213     public TypeBearer pop() {
214         throwIfImmutable();
215 
216         TypeBearer result = peek(0);
217 
218         stack[stackPtr - 1] = null;
219         stackPtr -= result.getType().getCategory();
220 
221         return result;
222     }
223 
224     /**
225      * Changes an element already on a stack. This method is useful in limited
226      * contexts, particularly when merging two instances. As such, it places
227      * the following restriction on its behavior: You may only replace
228      * values with other values of the same category.
229      *
230      * @param n {@code >= 0;} which element to change, where {@code 0} is
231      * the top element of the stack
232      * @param type {@code non-null;} type of the new value
233      * @throws SimException thrown if {@code n >= size()} or
234      * the action is otherwise prohibited
235      */
change(int n, TypeBearer type)236     public void change(int n, TypeBearer type) {
237         throwIfImmutable();
238 
239         try {
240             type = type.getFrameType();
241         } catch (NullPointerException ex) {
242             // Elucidate the exception.
243             throw new NullPointerException("type == null");
244         }
245 
246         int idx = stackPtr - n - 1;
247         TypeBearer orig = stack[idx];
248 
249         if ((orig == null) ||
250             (orig.getType().getCategory() != type.getType().getCategory())) {
251             throwSimException("incompatible substitution: " +
252                               stackElementString(orig) + " -> " +
253                               stackElementString(type));
254         }
255 
256         stack[idx] = type;
257     }
258 
259     /**
260      * Merges this stack with another stack. A new instance is returned if
261      * this merge results in a change. If no change results, this instance is
262      * returned.  See {@link Merger#mergeStack(ExecutionStack,ExecutionStack)
263      * Merger.mergeStack()}
264      *
265      * @param other {@code non-null;} a stack to merge with
266      * @return {@code non-null;} the result of the merge
267      */
merge(ExecutionStack other)268     public ExecutionStack merge(ExecutionStack other) {
269         try {
270             return Merger.mergeStack(this, other);
271         } catch (SimException ex) {
272             ex.addContext("underlay stack:");
273             this.annotate(ex);
274             ex.addContext("overlay stack:");
275             other.annotate(ex);
276             throw ex;
277         }
278     }
279 
280     /**
281      * Gets the string form for a stack element. This is the same as
282      * {@code toString()} except that {@code null} is converted
283      * to {@code "<invalid>"}.
284      *
285      * @param type {@code null-ok;} the stack element
286      * @return {@code non-null;} the string form
287      */
stackElementString(TypeBearer type)288     private static String stackElementString(TypeBearer type) {
289         if (type == null) {
290             return "<invalid>";
291         }
292 
293         return type.toString();
294     }
295 
296     /**
297      * Throws a properly-formatted exception.
298      *
299      * @param msg {@code non-null;} useful message
300      * @return never (keeps compiler happy)
301      */
throwSimException(String msg)302     private static TypeBearer throwSimException(String msg) {
303         throw new SimException("stack: " + msg);
304     }
305 }
306