@@ -48,14 +48,28 @@ public class AbstractFrame<T> {
48
48
private final OperandStack <T > operandStack ;
49
49
private final LocalVariableTable <T > localVariableTable ;
50
50
51
+ /**
52
+ * Get the operand stack of this abstract frame.
53
+ */
51
54
public OperandStack <T > operandStack () {
52
55
return operandStack ;
53
56
}
54
57
58
+ /**
59
+ * Get the local variable table of this abstract frame.
60
+ */
55
61
public LocalVariableTable <T > localVariableTable () {
56
62
return localVariableTable ;
57
63
}
58
64
65
+ /**
66
+ * Transform the chosen values in the abstract frame. This affects both the values on the
67
+ * operand stack and in the local variable table.
68
+ *
69
+ * @param filterFunction Values which satisfy this predicate are subject to transformation with
70
+ * {@code transformFunction}.
71
+ * @param transformFunction The transformation function.
72
+ */
59
73
public void transform (Predicate <T > filterFunction , Function <T , T > transformFunction ) {
60
74
operandStack .transform (filterFunction , transformFunction );
61
75
localVariableTable .transform (filterFunction , transformFunction );
@@ -95,31 +109,53 @@ public int hashCode() {
95
109
96
110
@ Override
97
111
public String toString () {
98
- return operandStack + "" + localVariableTable ;
112
+ return operandStack + System . lineSeparator () + localVariableTable ;
99
113
}
100
114
115
+ /**
116
+ * Abstract representation of a bytecode operand stack.
117
+ */
101
118
public static final class OperandStack <T > {
102
119
103
120
/*
104
121
* An ArrayList is used in order to allow for efficient lookups at arbitrary stack
105
122
* positions, as well as to preserve memory in comparison to allocating a plain array with a
106
123
* max stack size.
107
124
*/
108
- private final ArrayList <SizedValue <T >> stack ;
125
+ private final ArrayList <ValueWithSlots <T >> stack ;
109
126
127
+ /**
128
+ * Get a value at the specified depth of the operand stack. The {@code depth} does not take
129
+ * into account the size of values, i.e., a {@link ValueWithSlots} with size equal to
130
+ * {@link ValueWithSlots.Slots#TWO_SLOTS TWO_SLOTS} contributes only as one value to the
131
+ * depth of the operand stack.
132
+ */
110
133
public T getOperand (int depth ) {
111
134
return peek (depth ).value ;
112
135
}
113
136
137
+ /**
138
+ * Get the number of values currently on the operand stack. This does not take into account
139
+ * the size of values, i.e., a {@link ValueWithSlots} with size equal to
140
+ * {@link ValueWithSlots.Slots#TWO_SLOTS TWO_SLOTS} contributes only as one value for this
141
+ * method.
142
+ */
114
143
public int size () {
115
144
return stack .size ();
116
145
}
117
146
147
+ /**
148
+ * Transform the chosen values on the operand stack.
149
+ *
150
+ * @param filterFunction Values which satisfy this predicate are subject to transformation
151
+ * with {@code transformFunction}.
152
+ * @param transformFunction The transformation function.
153
+ */
118
154
public void transform (Predicate <T > filterFunction , Function <T , T > transformFunction ) {
119
155
for (int i = 0 ; i < stack .size (); i ++) {
120
- SizedValue <T > value = stack .get (i );
156
+ ValueWithSlots <T > value = stack .get (i );
121
157
if (filterFunction .test (value .value ())) {
122
- stack .set (i , new SizedValue <>(transformFunction .apply (value .value ()), value .size ()));
158
+ stack .set (i , new ValueWithSlots <>(transformFunction .apply (value .value ()), value .size ()));
123
159
}
124
160
}
125
161
}
@@ -132,21 +168,21 @@ public void transform(Predicate<T> filterFunction, Function<T, T> transformFunct
132
168
this .stack = new ArrayList <>(stack .stack );
133
169
}
134
170
135
- void push (SizedValue <T > value ) {
171
+ void push (ValueWithSlots <T > value ) {
136
172
stack .add (value );
137
173
}
138
174
139
- SizedValue <T > pop () {
175
+ ValueWithSlots <T > pop () {
140
176
GraalError .guarantee (!stack .isEmpty (), "Cannot pop from empty stack" );
141
177
return stack .removeLast ();
142
178
}
143
179
144
- SizedValue <T > peek (int depth ) {
145
- GraalError .guarantee (depth < size (), "Operand stack doesn't contain enough values" );
180
+ ValueWithSlots <T > peek (int depth ) {
181
+ GraalError .guarantee (0 <= depth && depth < size (), "Operand stack doesn't contain enough values" );
146
182
return stack .get (stack .size () - depth - 1 );
147
183
}
148
184
149
- SizedValue <T > peek () {
185
+ ValueWithSlots <T > peek () {
150
186
return peek (0 );
151
187
}
152
188
@@ -157,10 +193,10 @@ void clear() {
157
193
void mergeWith (OperandStack <T > other , BiFunction <T , T , T > mergeFunction ) {
158
194
GraalError .guarantee (size () == other .size (), "Operand stack size must match upon merging" );
159
195
for (int i = 0 ; i < stack .size (); i ++) {
160
- SizedValue <T > thisValue = stack .get (i );
161
- SizedValue <T > thatValue = other .stack .get (i );
196
+ ValueWithSlots <T > thisValue = stack .get (i );
197
+ ValueWithSlots <T > thatValue = other .stack .get (i );
162
198
GraalError .guarantee (thisValue .size () == thatValue .size (), "The size of operand stack values must match upon merging" );
163
- SizedValue <T > mergedValue = new SizedValue <>(mergeFunction .apply (thisValue .value (), thatValue .value ()), thisValue .size ());
199
+ ValueWithSlots <T > mergedValue = new ValueWithSlots <>(mergeFunction .apply (thisValue .value (), thatValue .value ()), thisValue .size ());
164
200
stack .set (i , mergedValue );
165
201
}
166
202
}
@@ -186,30 +222,48 @@ public int hashCode() {
186
222
public String toString () {
187
223
StringBuilder builder = new StringBuilder ();
188
224
builder .append ("Operand stack:\n " );
189
- for (SizedValue <T > value : stack .reversed ()) {
190
- builder .append ("[" ).append (value .value ()).append ("]" ).append (" \n " );
225
+ for (ValueWithSlots <T > value : stack .reversed ()) {
226
+ builder .append ("[" ).append (value .value ()).append ("]" ).append (System . lineSeparator () );
191
227
}
192
228
return builder .toString ();
193
229
}
194
230
}
195
231
232
+ /**
233
+ * Abstract representation of a bytecode local variable table.
234
+ */
196
235
public static final class LocalVariableTable <T > {
197
236
198
- private final Map <Integer , SizedValue <T >> variables ;
237
+ private final Map <Integer , ValueWithSlots <T >> variables ;
199
238
239
+ /**
240
+ * Get the value at the {@code index} slot of the local variable table. The {@code index}
241
+ * must be valid, i.e., must be in the set which would be returned by
242
+ * {@link #getVariableIndices()}.
243
+ */
200
244
public T getVariable (int index ) {
201
245
return get (index ).value ;
202
246
}
203
247
248
+ /**
249
+ * Get the local variable table indices of entries currently stored in it.
250
+ */
204
251
public Set <Integer > getVariableIndices () {
205
252
return variables .keySet ();
206
253
}
207
254
255
+ /**
256
+ * Transform the chosen values in the local variable table.
257
+ *
258
+ * @param filterFunction Values which satisfy this predicate are subject to transformation
259
+ * with {@code transformFunction}.
260
+ * @param transformFunction The transformation function.
261
+ */
208
262
public void transform (Predicate <T > filterFunction , Function <T , T > transformFunction ) {
209
- for (Map .Entry <Integer , SizedValue <T >> entry : variables .entrySet ()) {
210
- SizedValue <T > value = entry .getValue ();
263
+ for (Map .Entry <Integer , ValueWithSlots <T >> entry : variables .entrySet ()) {
264
+ ValueWithSlots <T > value = entry .getValue ();
211
265
if (filterFunction .test (value .value ())) {
212
- entry .setValue (new SizedValue <>(transformFunction .apply (value .value ()), value .size ()));
266
+ entry .setValue (new ValueWithSlots <>(transformFunction .apply (value .value ()), value .size ()));
213
267
}
214
268
}
215
269
}
@@ -222,27 +276,27 @@ public void transform(Predicate<T> filterFunction, Function<T, T> transformFunct
222
276
this .variables = new HashMap <>(localVariableTable .variables );
223
277
}
224
278
225
- void put (int index , SizedValue <T > value ) {
279
+ void put (int index , ValueWithSlots <T > value ) {
226
280
variables .put (index , value );
227
281
}
228
282
229
- SizedValue <T > get (int index ) {
283
+ ValueWithSlots <T > get (int index ) {
230
284
GraalError .guarantee (variables .containsKey (index ), "Attempted to access non-existent variable in local variable table" );
231
285
return variables .get (index );
232
286
}
233
287
234
288
void mergeWith (LocalVariableTable <T > other , BiFunction <T , T , T > mergeFunction ) {
235
- for (Map .Entry <Integer , SizedValue <T >> entry : variables .entrySet ()) {
236
- SizedValue <T > thisValue = entry .getValue ();
237
- SizedValue <T > thatValue = other .variables .get (entry .getKey ());
289
+ for (Map .Entry <Integer , ValueWithSlots <T >> entry : variables .entrySet ()) {
290
+ ValueWithSlots <T > thisValue = entry .getValue ();
291
+ ValueWithSlots <T > thatValue = other .variables .get (entry .getKey ());
238
292
if (thatValue != null && thisValue .size () == thatValue .size ()) {
239
293
/*
240
294
* We can always merge matching values from the local variable table. If the
241
295
* merging makes no sense (i.e., the stored variable types do not match), we
242
296
* still allow it, as the resulting value should not be used during execution
243
297
* anyway (or else the method would fail bytecode verification).
244
298
*/
245
- SizedValue <T > mergedValue = new SizedValue <>(mergeFunction .apply (thisValue .value (), thatValue .value ()), thisValue .size ());
299
+ ValueWithSlots <T > mergedValue = new ValueWithSlots <>(mergeFunction .apply (thisValue .value (), thatValue .value ()), thisValue .size ());
246
300
entry .setValue (mergedValue );
247
301
}
248
302
}
@@ -272,21 +326,25 @@ public String toString() {
272
326
variables .entrySet ().stream ().sorted (Map .Entry .comparingByKey ()).forEach (
273
327
e -> {
274
328
Integer varIndex = e .getKey ();
275
- SizedValue <T > value = e .getValue ();
276
- builder .append (varIndex ).append (": " ).append (value .value ()).append (" \n " );
329
+ ValueWithSlots <T > value = e .getValue ();
330
+ builder .append (varIndex ).append (": " ).append (value .value ()).append (System . lineSeparator () );
277
331
});
278
332
return builder .toString ();
279
333
}
280
334
}
281
335
282
- record SizedValue <T >(T value , Slots size ) {
336
+ /**
337
+ * Wrapper which assigns a computational type category to a value, i.e., assigns the number of
338
+ * slots the value takes up in the local variable table or operand stack.
339
+ */
340
+ record ValueWithSlots <T >(T value , Slots size ) {
283
341
public enum Slots {
284
342
ONE_SLOT ,
285
343
TWO_SLOTS
286
344
}
287
345
288
- public static <T > SizedValue <T > wrap (T value , Slots size ) {
289
- return new SizedValue <>(value , size );
346
+ public static <T > ValueWithSlots <T > wrap (T value , Slots size ) {
347
+ return new ValueWithSlots <>(value , size );
290
348
}
291
349
}
292
350
}
0 commit comments