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