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