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