• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later,
9  * or the Apache License Version 2.0.
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  */
16 package javassist.bytecode.analysis;
17 
18 
19 /**
20  * Represents the stack frame and local variable table at a particular point in time.
21  *
22  * @author Jason T. Greene
23  */
24 public class Frame {
25     private Type[] locals;
26     private Type[] stack;
27     private int top;
28     private boolean jsrMerged;
29     private boolean retMerged;
30 
31     /**
32      * Create a new frame with the specified local variable table size, and max stack size
33      *
34      * @param locals the number of local variable table entries
35      * @param stack the maximum stack size
36      */
Frame(int locals, int stack)37     public Frame(int locals, int stack) {
38         this.locals = new Type[locals];
39         this.stack = new Type[stack];
40     }
41 
42     /**
43      * Returns the local varaible table entry at index.
44      *
45      * @param index the position in the table
46      * @return the type if one exists, or null if the position is empty
47      */
getLocal(int index)48     public Type getLocal(int index) {
49         return locals[index];
50     }
51 
52     /**
53      * Sets the local variable table entry at index to a type.
54      *
55      * @param index the position in the table
56      * @param type the type to set at the position
57      */
setLocal(int index, Type type)58     public void setLocal(int index, Type type) {
59         locals[index] = type;
60     }
61 
62 
63     /**
64      * Returns the type on the stack at the specified index.
65      *
66      * @param index the position on the stack
67      * @return the type of the stack position
68      */
getStack(int index)69     public Type getStack(int index) {
70         return stack[index];
71     }
72 
73     /**
74      * Sets the type of the stack position
75      *
76      * @param index the position on the stack
77      * @param type the type to set
78      */
setStack(int index, Type type)79     public void setStack(int index, Type type) {
80         stack[index] = type;
81     }
82 
83     /**
84      * Empties the stack
85      */
clearStack()86     public void clearStack() {
87         top = 0;
88     }
89 
90     /**
91      * Gets the index of the type sitting at the top of the stack.
92      * This is not to be confused with a length operation which
93      * would return the number of elements, not the position of
94      * the last element.
95      *
96      * @return the position of the element at the top of the stack
97      */
getTopIndex()98     public int getTopIndex() {
99         return top - 1;
100     }
101 
102     /**
103      * Returns the number of local variable table entries, specified
104      * at construction.
105      *
106      * @return the number of local variable table entries
107      */
localsLength()108     public int localsLength() {
109         return locals.length;
110     }
111 
112     /**
113      * Gets the top of the stack without altering it
114      *
115      * @return the top of the stack
116      */
peek()117     public Type peek() {
118         if (top < 1)
119             throw new IndexOutOfBoundsException("Stack is empty");
120 
121         return stack[top - 1];
122     }
123 
124     /**
125      * Alters the stack to contain one less element and return it.
126      *
127      * @return the element popped from the stack
128      */
pop()129     public Type pop() {
130         if (top < 1)
131             throw new IndexOutOfBoundsException("Stack is empty");
132         return stack[--top];
133     }
134 
135     /**
136      * Alters the stack by placing the passed type on the top
137      *
138      * @param type the type to add to the top
139      */
push(Type type)140     public void push(Type type) {
141         stack[top++] = type;
142     }
143 
144 
145     /**
146      * Makes a shallow copy of this frame, i.e. the type instances will
147      * remain the same.
148      *
149      * @return the shallow copy
150      */
copy()151     public Frame copy() {
152         Frame frame = new Frame(locals.length, stack.length);
153         System.arraycopy(locals, 0, frame.locals, 0, locals.length);
154         System.arraycopy(stack, 0, frame.stack, 0, stack.length);
155         frame.top = top;
156         return frame;
157     }
158 
159     /**
160      * Makes a shallow copy of the stack portion of this frame. The local
161      * variable table size will be copied, but its contents will be empty.
162      *
163      * @return the shallow copy of the stack
164      */
copyStack()165     public Frame copyStack() {
166         Frame frame = new Frame(locals.length, stack.length);
167         System.arraycopy(stack, 0, frame.stack, 0, stack.length);
168         frame.top = top;
169         return frame;
170     }
171 
172     /**
173      * Merges all types on the stack of this frame instance with that of the specified frame.
174      * The local variable table is left untouched.
175      *
176      * @param frame the frame to merge the stack from
177      * @return true if any changes where made
178      */
mergeStack(Frame frame)179     public boolean mergeStack(Frame frame) {
180         boolean changed = false;
181         if (top != frame.top)
182             throw new RuntimeException("Operand stacks could not be merged, they are different sizes!");
183 
184         for (int i = 0; i < top; i++) {
185             if (stack[i] != null) {
186                 Type prev = stack[i];
187                 Type merged = prev.merge(frame.stack[i]);
188                 if (merged == Type.BOGUS)
189                     throw new RuntimeException("Operand stacks could not be merged due to differing primitive types: pos = " + i);
190 
191                 stack[i] = merged;
192                 // always replace the instance in case a multi-interface type changes to a normal Type
193                 if ((! merged.equals(prev)) || merged.popChanged()) {
194                     changed = true;
195                 }
196             }
197         }
198 
199         return changed;
200     }
201 
202     /**
203      * Merges all types on the stack and local variable table of this frame with that of the specified
204      * type.
205      *
206      * @param frame the frame to merge with
207      * @return true if any changes to this frame where made by this merge
208      */
merge(Frame frame)209     public boolean merge(Frame frame) {
210         boolean changed = false;
211 
212         // Local variable table
213         for (int i = 0; i < locals.length; i++) {
214             if (locals[i] != null) {
215                 Type prev = locals[i];
216                 Type merged = prev.merge(frame.locals[i]);
217                 // always replace the instance in case a multi-interface type changes to a normal Type
218                 locals[i] = merged;
219                 if (! merged.equals(prev) || merged.popChanged()) {
220                     changed = true;
221                 }
222             } else if (frame.locals[i] != null) {
223                 locals[i] = frame.locals[i];
224                 changed = true;
225             }
226         }
227 
228         changed |= mergeStack(frame);
229         return changed;
230     }
231 
232     @Override
toString()233     public String toString() {
234         StringBuffer buffer = new StringBuffer();
235 
236         buffer.append("locals = [");
237         for (int i = 0; i < locals.length; i++) {
238             buffer.append(locals[i] == null ? "empty" : locals[i].toString());
239             if (i < locals.length - 1)
240                 buffer.append(", ");
241         }
242         buffer.append("] stack = [");
243         for (int i = 0; i < top; i++) {
244             buffer.append(stack[i]);
245             if (i < top - 1)
246                 buffer.append(", ");
247         }
248         buffer.append("]");
249 
250         return buffer.toString();
251     }
252 
253     /**
254      * Whether or not state from the source JSR instruction has been merged
255      *
256      * @return true if JSR state has been merged
257      */
isJsrMerged()258     boolean isJsrMerged() {
259         return jsrMerged;
260     }
261 
262     /**
263      * Sets whether of not the state from the source JSR instruction has been merged
264      *
265      * @param jsrMerged true if merged, otherwise false
266      */
setJsrMerged(boolean jsrMerged)267     void setJsrMerged(boolean jsrMerged) {
268         this.jsrMerged = jsrMerged;
269     }
270 
271     /**
272      * Whether or not state from the RET instruction, of the subroutine that was jumped
273      * to has been merged.
274      *
275      * @return true if RET state has been merged
276      */
isRetMerged()277     boolean isRetMerged() {
278         return retMerged;
279     }
280 
281     /**
282      * Sets whether or not state from the RET instruction, of the subroutine that was jumped
283      * to has been merged.
284      *
285      * @param retMerged true if RET state has been merged
286      */
setRetMerged(boolean retMerged)287     void setRetMerged(boolean retMerged) {
288         this.retMerged = retMerged;
289     }
290 }
291