author | joehw |
Wed, 26 Jun 2019 05:49:59 +0000 | |
changeset 55496 | 8e0ae3830fca |
parent 47216 | 71c04702a3d5 |
permissions | -rw-r--r-- |
6 | 1 |
/* |
55496 | 2 |
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. |
6 | 3 |
*/ |
55496 | 4 |
/* |
44797
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
5 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
6 |
* contributor license agreements. See the NOTICE file distributed with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
7 |
* this work for additional information regarding copyright ownership. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
8 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
9 |
* (the "License"); you may not use this file except in compliance with |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
10 |
* the License. You may obtain a copy of the License at |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
11 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
12 |
* http://www.apache.org/licenses/LICENSE-2.0 |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
13 |
* |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
14 |
* Unless required by applicable law or agreed to in writing, software |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
15 |
* distributed under the License is distributed on an "AS IS" BASIS, |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
16 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
17 |
* See the License for the specific language governing permissions and |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
18 |
* limitations under the License. |
8b3b3b911b8a
8162572: Update License Header for all JAXP sources
joehw
parents:
25868
diff
changeset
|
19 |
*/ |
6 | 20 |
package com.sun.org.apache.bcel.internal.generic; |
21 |
||
46174 | 22 |
import com.sun.org.apache.bcel.internal.Const; |
6 | 23 |
import com.sun.org.apache.bcel.internal.classfile.Constant; |
24 |
import com.sun.org.apache.bcel.internal.util.ByteSequence; |
|
46174 | 25 |
import java.io.ByteArrayOutputStream; |
26 |
import java.io.DataOutputStream; |
|
27 |
import java.io.IOException; |
|
28 |
import java.util.ArrayList; |
|
29 |
import java.util.HashMap; |
|
6 | 30 |
import java.util.Iterator; |
46174 | 31 |
import java.util.List; |
32 |
import java.util.Map; |
|
33 |
import java.util.NoSuchElementException; |
|
6 | 34 |
|
35 |
/** |
|
36 |
* This class is a container for a list of <a |
|
46174 | 37 |
* href="Instruction.html">Instruction</a> objects. Instructions can be |
38 |
* appended, inserted, moved, deleted, etc.. Instructions are being wrapped into |
|
39 |
* <a href="InstructionHandle.html">InstructionHandles</a> objects that are |
|
40 |
* returned upon append/insert operations. They give the user (read only) access |
|
41 |
* to the list structure, such that it can be traversed and manipulated in a |
|
42 |
* controlled way. |
|
6 | 43 |
* |
44 |
* A list is finally dumped to a byte code array with <a |
|
45 |
* href="#getByteCode()">getByteCode</a>. |
|
46 |
* |
|
55496 | 47 |
* @version $Id$ |
46174 | 48 |
* @see Instruction |
49 |
* @see InstructionHandle |
|
6 | 50 |
* @see BranchHandle |
55496 | 51 |
* @LastModified: Jun 2019 |
6 | 52 |
*/ |
46174 | 53 |
public class InstructionList implements Iterable<InstructionHandle> { |
55496 | 54 |
|
46174 | 55 |
private InstructionHandle start = null; |
56 |
private InstructionHandle end = null; |
|
57 |
private int length = 0; // number of elements in list |
|
58 |
private int[] byte_positions; // byte code offsets corresponding to instructions |
|
6 | 59 |
|
46174 | 60 |
/** |
61 |
* Create (empty) instruction list. |
|
62 |
*/ |
|
63 |
public InstructionList() { |
|
64 |
} |
|
6 | 65 |
|
46174 | 66 |
/** |
67 |
* Create instruction list containing one instruction. |
|
68 |
* |
|
69 |
* @param i initial instruction |
|
70 |
*/ |
|
71 |
public InstructionList(final Instruction i) { |
|
72 |
append(i); |
|
73 |
} |
|
6 | 74 |
|
46174 | 75 |
/** |
76 |
* Create instruction list containing one instruction. |
|
77 |
* |
|
78 |
* @param i initial instruction |
|
79 |
*/ |
|
80 |
public InstructionList(final BranchInstruction i) { |
|
81 |
append(i); |
|
82 |
} |
|
6 | 83 |
|
46174 | 84 |
/** |
85 |
* Initialize list with (nonnull) compound instruction. Consumes argument |
|
86 |
* list, i.e., it becomes empty. |
|
87 |
* |
|
88 |
* @param c compound instruction (list) |
|
89 |
*/ |
|
90 |
public InstructionList(final CompoundInstruction c) { |
|
91 |
append(c.getInstructionList()); |
|
92 |
} |
|
6 | 93 |
|
46174 | 94 |
/** |
95 |
* Test for empty list. |
|
6 | 96 |
*/ |
46174 | 97 |
public boolean isEmpty() { |
98 |
return start == null; |
|
99 |
} // && end == null |
|
6 | 100 |
|
46174 | 101 |
/** |
102 |
* Find the target instruction (handle) that corresponds to the given target |
|
103 |
* position (byte code offset). |
|
104 |
* |
|
105 |
* @param ihs array of instruction handles, i.e. il.getInstructionHandles() |
|
106 |
* @param pos array of positions corresponding to ihs, i.e. |
|
107 |
* il.getInstructionPositions() |
|
108 |
* @param count length of arrays |
|
109 |
* @param target target position to search for |
|
110 |
* @return target position's instruction handle if available |
|
111 |
*/ |
|
112 |
public static InstructionHandle findHandle(final InstructionHandle[] ihs, final int[] pos, final int count, final int target) { |
|
113 |
int l = 0; |
|
114 |
int r = count - 1; |
|
115 |
/* |
|
116 |
* Do a binary search since the pos array is orderd. |
|
117 |
*/ |
|
118 |
do { |
|
119 |
final int i = (l + r) / 2; |
|
120 |
final int j = pos[i]; |
|
121 |
if (j == target) { |
|
122 |
return ihs[i]; |
|
123 |
} else if (target < j) { |
|
124 |
r = i - 1; |
|
125 |
} else { |
|
126 |
l = i + 1; |
|
127 |
} |
|
128 |
} while (l <= r); |
|
129 |
return null; |
|
130 |
} |
|
6 | 131 |
|
46174 | 132 |
/** |
133 |
* Get instruction handle for instruction at byte code position pos. This |
|
134 |
* only works properly, if the list is freshly initialized from a byte array |
|
135 |
* or setPositions() has been called before this method. |
|
136 |
* |
|
137 |
* @param pos byte code position to search for |
|
138 |
* @return target position's instruction handle if available |
|
6 | 139 |
*/ |
46174 | 140 |
public InstructionHandle findHandle(final int pos) { |
141 |
final int[] positions = byte_positions; |
|
142 |
InstructionHandle ih = start; |
|
143 |
for (int i = 0; i < length; i++) { |
|
144 |
if (positions[i] == pos) { |
|
145 |
return ih; |
|
146 |
} |
|
147 |
ih = ih.getNext(); |
|
148 |
} |
|
149 |
return null; |
|
150 |
} |
|
6 | 151 |
|
46174 | 152 |
/** |
153 |
* Initialize instruction list from byte array. |
|
154 |
* |
|
155 |
* @param code byte array containing the instructions |
|
6 | 156 |
*/ |
46174 | 157 |
public InstructionList(final byte[] code) { |
158 |
int count = 0; // Contains actual length |
|
159 |
int[] pos; |
|
160 |
InstructionHandle[] ihs; |
|
161 |
try (ByteSequence bytes = new ByteSequence(code)) { |
|
162 |
ihs = new InstructionHandle[code.length]; |
|
163 |
pos = new int[code.length]; // Can't be more than that |
|
164 |
/* |
|
165 |
* Pass 1: Create an object for each byte code and append them to the list. |
|
166 |
*/ |
|
167 |
while (bytes.available() > 0) { |
|
168 |
// Remember byte offset and associate it with the instruction |
|
169 |
final int off = bytes.getIndex(); |
|
170 |
pos[count] = off; |
|
171 |
/* |
|
172 |
* Read one instruction from the byte stream, the byte position is set accordingly. |
|
173 |
*/ |
|
174 |
final Instruction i = Instruction.readInstruction(bytes); |
|
175 |
InstructionHandle ih; |
|
176 |
if (i instanceof BranchInstruction) { |
|
177 |
ih = append((BranchInstruction) i); |
|
178 |
} else { |
|
179 |
ih = append(i); |
|
180 |
} |
|
181 |
ih.setPosition(off); |
|
182 |
ihs[count] = ih; |
|
183 |
count++; |
|
184 |
} |
|
185 |
} catch (final IOException e) { |
|
186 |
throw new ClassGenException(e.toString(), e); |
|
6 | 187 |
} |
46174 | 188 |
byte_positions = new int[count]; // Trim to proper size |
189 |
System.arraycopy(pos, 0, byte_positions, 0, count); |
|
190 |
/* |
|
191 |
* Pass 2: Look for BranchInstruction and update their targets, i.e., convert offsets to instruction handles. |
|
192 |
*/ |
|
193 |
for (int i = 0; i < count; i++) { |
|
194 |
if (ihs[i] instanceof BranchHandle) { |
|
195 |
final BranchInstruction bi = (BranchInstruction) ihs[i].getInstruction(); |
|
55496 | 196 |
int target = bi.getPosition() + bi.getIndex(); |
197 |
/* |
|
198 |
* Byte code position: relative -> absolute. |
|
46174 | 199 |
*/ |
200 |
// Search for target position |
|
201 |
InstructionHandle ih = findHandle(ihs, pos, count, target); |
|
202 |
if (ih == null) { |
|
203 |
throw new ClassGenException("Couldn't find target for branch: " + bi); |
|
204 |
} |
|
205 |
bi.setTarget(ih); // Update target |
|
206 |
// If it is a Select instruction, update all branch targets |
|
207 |
if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH |
|
208 |
final Select s = (Select) bi; |
|
209 |
final int[] indices = s.getIndices(); |
|
210 |
for (int j = 0; j < indices.length; j++) { |
|
211 |
target = bi.getPosition() + indices[j]; |
|
212 |
ih = findHandle(ihs, pos, count, target); |
|
213 |
if (ih == null) { |
|
214 |
throw new ClassGenException("Couldn't find target for switch: " + bi); |
|
215 |
} |
|
216 |
s.setTarget(j, ih); // Update target |
|
217 |
} |
|
218 |
} |
|
219 |
} |
|
220 |
} |
|
221 |
} |
|
6 | 222 |
|
46174 | 223 |
/** |
224 |
* Append another list after instruction (handle) ih contained in this list. |
|
225 |
* Consumes argument list, i.e., it becomes empty. |
|
226 |
* |
|
227 |
* @param ih where to append the instruction list |
|
228 |
* @param il Instruction list to append to this one |
|
229 |
* @return instruction handle pointing to the <B>first</B> appended |
|
230 |
* instruction |
|
231 |
*/ |
|
232 |
public InstructionHandle append(final InstructionHandle ih, final InstructionList il) { |
|
233 |
if (il == null) { |
|
234 |
throw new ClassGenException("Appending null InstructionList"); |
|
235 |
} |
|
236 |
if (il.isEmpty()) { |
|
237 |
return ih; |
|
238 |
} |
|
239 |
final InstructionHandle next = ih.getNext(); |
|
240 |
final InstructionHandle ret = il.start; |
|
241 |
ih.setNext(il.start); |
|
242 |
il.start.setPrev(ih); |
|
243 |
il.end.setNext(next); |
|
244 |
if (next != null) { |
|
245 |
next.setPrev(il.end); |
|
246 |
} else { |
|
247 |
end = il.end; // Update end ... |
|
248 |
} |
|
249 |
length += il.length; // Update length |
|
250 |
il.clear(); |
|
251 |
return ret; |
|
252 |
} |
|
6 | 253 |
|
46174 | 254 |
/** |
255 |
* Append another list after instruction i contained in this list. Consumes |
|
256 |
* argument list, i.e., it becomes empty. |
|
257 |
* |
|
258 |
* @param i where to append the instruction list |
|
259 |
* @param il Instruction list to append to this one |
|
260 |
* @return instruction handle pointing to the <B>first</B> appended |
|
261 |
* instruction |
|
262 |
*/ |
|
263 |
public InstructionHandle append(final Instruction i, final InstructionList il) { |
|
264 |
InstructionHandle ih; |
|
265 |
if ((ih = findInstruction2(i)) == null) { |
|
266 |
throw new ClassGenException("Instruction " + i + " is not contained in this list."); |
|
267 |
} |
|
268 |
return append(ih, il); |
|
269 |
} |
|
6 | 270 |
|
46174 | 271 |
/** |
272 |
* Append another list to this one. Consumes argument list, i.e., it becomes |
|
273 |
* empty. |
|
274 |
* |
|
275 |
* @param il list to append to end of this list |
|
276 |
* @return instruction handle of the <B>first</B> appended instruction |
|
277 |
*/ |
|
278 |
public InstructionHandle append(final InstructionList il) { |
|
279 |
if (il == null) { |
|
280 |
throw new ClassGenException("Appending null InstructionList"); |
|
281 |
} |
|
282 |
if (il.isEmpty()) { |
|
283 |
return null; |
|
284 |
} |
|
285 |
if (isEmpty()) { |
|
286 |
start = il.start; |
|
287 |
end = il.end; |
|
288 |
length = il.length; |
|
289 |
il.clear(); |
|
290 |
return start; |
|
291 |
} |
|
292 |
return append(end, il); // was end.instruction |
|
293 |
} |
|
6 | 294 |
|
46174 | 295 |
/** |
296 |
* Append an instruction to the end of this list. |
|
297 |
* |
|
298 |
* @param ih instruction to append |
|
299 |
*/ |
|
300 |
private void append(final InstructionHandle ih) { |
|
301 |
if (isEmpty()) { |
|
302 |
start = end = ih; |
|
303 |
ih.setNext(ih.setPrev(null)); |
|
304 |
} else { |
|
305 |
end.setNext(ih); |
|
306 |
ih.setPrev(end); |
|
307 |
ih.setNext(null); |
|
308 |
end = ih; |
|
309 |
} |
|
310 |
||
311 |
length++; // Update length |
|
312 |
} |
|
6 | 313 |
|
46174 | 314 |
/** |
315 |
* Append an instruction to the end of this list. |
|
316 |
* |
|
317 |
* @param i instruction to append |
|
318 |
* @return instruction handle of the appended instruction |
|
319 |
*/ |
|
320 |
public InstructionHandle append(final Instruction i) { |
|
321 |
final InstructionHandle ih = InstructionHandle.getInstructionHandle(i); |
|
322 |
append(ih); |
|
323 |
return ih; |
|
6 | 324 |
} |
46174 | 325 |
|
326 |
/** |
|
327 |
* Append a branch instruction to the end of this list. |
|
328 |
* |
|
329 |
* @param i branch instruction to append |
|
330 |
* @return branch instruction handle of the appended instruction |
|
331 |
*/ |
|
332 |
public BranchHandle append(final BranchInstruction i) { |
|
333 |
final BranchHandle ih = BranchHandle.getBranchHandle(i); |
|
334 |
append(ih); |
|
335 |
return ih; |
|
6 | 336 |
} |
337 |
||
46174 | 338 |
/** |
339 |
* Append a single instruction j after another instruction i, which must be |
|
340 |
* in this list of course! |
|
341 |
* |
|
342 |
* @param i Instruction in list |
|
343 |
* @param j Instruction to append after i in list |
|
344 |
* @return instruction handle of the first appended instruction |
|
345 |
*/ |
|
346 |
public InstructionHandle append(final Instruction i, final Instruction j) { |
|
347 |
return append(i, new InstructionList(j)); |
|
348 |
} |
|
6 | 349 |
|
46174 | 350 |
/** |
351 |
* Append a compound instruction, after instruction i. |
|
352 |
* |
|
353 |
* @param i Instruction in list |
|
354 |
* @param c The composite instruction (containing an InstructionList) |
|
355 |
* @return instruction handle of the first appended instruction |
|
356 |
*/ |
|
357 |
public InstructionHandle append(final Instruction i, final CompoundInstruction c) { |
|
358 |
return append(i, c.getInstructionList()); |
|
359 |
} |
|
6 | 360 |
|
46174 | 361 |
/** |
362 |
* Append a compound instruction. |
|
363 |
* |
|
364 |
* @param c The composite instruction (containing an InstructionList) |
|
365 |
* @return instruction handle of the first appended instruction |
|
366 |
*/ |
|
367 |
public InstructionHandle append(final CompoundInstruction c) { |
|
368 |
return append(c.getInstructionList()); |
|
369 |
} |
|
6 | 370 |
|
46174 | 371 |
/** |
372 |
* Append a compound instruction. |
|
373 |
* |
|
374 |
* @param ih where to append the instruction list |
|
375 |
* @param c The composite instruction (containing an InstructionList) |
|
376 |
* @return instruction handle of the first appended instruction |
|
377 |
*/ |
|
378 |
public InstructionHandle append(final InstructionHandle ih, final CompoundInstruction c) { |
|
379 |
return append(ih, c.getInstructionList()); |
|
380 |
} |
|
6 | 381 |
|
46174 | 382 |
/** |
383 |
* Append an instruction after instruction (handle) ih contained in this |
|
384 |
* list. |
|
385 |
* |
|
386 |
* @param ih where to append the instruction list |
|
387 |
* @param i Instruction to append |
|
388 |
* @return instruction handle pointing to the <B>first</B> appended |
|
389 |
* instruction |
|
390 |
*/ |
|
391 |
public InstructionHandle append(final InstructionHandle ih, final Instruction i) { |
|
392 |
return append(ih, new InstructionList(i)); |
|
393 |
} |
|
6 | 394 |
|
46174 | 395 |
/** |
396 |
* Append an instruction after instruction (handle) ih contained in this |
|
397 |
* list. |
|
398 |
* |
|
399 |
* @param ih where to append the instruction list |
|
400 |
* @param i Instruction to append |
|
401 |
* @return instruction handle pointing to the <B>first</B> appended |
|
402 |
* instruction |
|
403 |
*/ |
|
404 |
public BranchHandle append(final InstructionHandle ih, final BranchInstruction i) { |
|
405 |
final BranchHandle bh = BranchHandle.getBranchHandle(i); |
|
406 |
final InstructionList il = new InstructionList(); |
|
407 |
il.append(bh); |
|
408 |
append(ih, il); |
|
409 |
return bh; |
|
410 |
} |
|
6 | 411 |
|
46174 | 412 |
/** |
413 |
* Insert another list before Instruction handle ih contained in this list. |
|
414 |
* Consumes argument list, i.e., it becomes empty. |
|
415 |
* |
|
416 |
* @param ih where to append the instruction list |
|
417 |
* @param il Instruction list to insert |
|
418 |
* @return instruction handle of the first inserted instruction |
|
419 |
*/ |
|
420 |
public InstructionHandle insert(final InstructionHandle ih, final InstructionList il) { |
|
421 |
if (il == null) { |
|
422 |
throw new ClassGenException("Inserting null InstructionList"); |
|
423 |
} |
|
424 |
if (il.isEmpty()) { |
|
425 |
return ih; |
|
426 |
} |
|
427 |
final InstructionHandle prev = ih.getPrev(); |
|
428 |
final InstructionHandle ret = il.start; |
|
429 |
ih.setPrev(il.end); |
|
430 |
il.end.setNext(ih); |
|
431 |
il.start.setPrev(prev); |
|
432 |
if (prev != null) { |
|
433 |
prev.setNext(il.start); |
|
434 |
} else { |
|
435 |
start = il.start; // Update start ... |
|
436 |
} |
|
437 |
length += il.length; // Update length |
|
438 |
il.clear(); |
|
439 |
return ret; |
|
440 |
} |
|
6 | 441 |
|
46174 | 442 |
/** |
443 |
* Insert another list. |
|
444 |
* |
|
445 |
* @param il list to insert before start of this list |
|
446 |
* @return instruction handle of the first inserted instruction |
|
447 |
*/ |
|
448 |
public InstructionHandle insert(final InstructionList il) { |
|
449 |
if (isEmpty()) { |
|
450 |
append(il); // Code is identical for this case |
|
451 |
return start; |
|
452 |
} |
|
453 |
return insert(start, il); |
|
454 |
} |
|
6 | 455 |
|
46174 | 456 |
/** |
457 |
* Insert an instruction at start of this list. |
|
458 |
* |
|
459 |
* @param ih instruction to insert |
|
460 |
*/ |
|
461 |
private void insert(final InstructionHandle ih) { |
|
462 |
if (isEmpty()) { |
|
463 |
start = end = ih; |
|
464 |
ih.setNext(ih.setPrev(null)); |
|
465 |
} else { |
|
466 |
start.setPrev(ih); |
|
467 |
ih.setNext(start); |
|
468 |
ih.setPrev(null); |
|
469 |
start = ih; |
|
470 |
} |
|
471 |
length++; |
|
472 |
} |
|
473 |
||
474 |
/** |
|
475 |
* Insert another list before Instruction i contained in this list. Consumes |
|
476 |
* argument list, i.e., it becomes empty. |
|
477 |
* |
|
478 |
* @param i where to append the instruction list |
|
479 |
* @param il Instruction list to insert |
|
480 |
* @return instruction handle pointing to the first inserted instruction, |
|
481 |
* i.e., il.getStart() |
|
482 |
*/ |
|
483 |
public InstructionHandle insert(final Instruction i, final InstructionList il) { |
|
484 |
InstructionHandle ih; |
|
485 |
if ((ih = findInstruction1(i)) == null) { |
|
486 |
throw new ClassGenException("Instruction " + i + " is not contained in this list."); |
|
487 |
} |
|
488 |
return insert(ih, il); |
|
6 | 489 |
} |
490 |
||
46174 | 491 |
/** |
492 |
* Insert an instruction at start of this list. |
|
493 |
* |
|
494 |
* @param i instruction to insert |
|
495 |
* @return instruction handle of the inserted instruction |
|
496 |
*/ |
|
497 |
public InstructionHandle insert(final Instruction i) { |
|
498 |
final InstructionHandle ih = InstructionHandle.getInstructionHandle(i); |
|
499 |
insert(ih); |
|
500 |
return ih; |
|
501 |
} |
|
6 | 502 |
|
46174 | 503 |
/** |
504 |
* Insert a branch instruction at start of this list. |
|
505 |
* |
|
506 |
* @param i branch instruction to insert |
|
507 |
* @return branch instruction handle of the appended instruction |
|
508 |
*/ |
|
509 |
public BranchHandle insert(final BranchInstruction i) { |
|
510 |
final BranchHandle ih = BranchHandle.getBranchHandle(i); |
|
511 |
insert(ih); |
|
512 |
return ih; |
|
513 |
} |
|
6 | 514 |
|
46174 | 515 |
/** |
516 |
* Insert a single instruction j before another instruction i, which must be |
|
517 |
* in this list of course! |
|
518 |
* |
|
519 |
* @param i Instruction in list |
|
520 |
* @param j Instruction to insert before i in list |
|
521 |
* @return instruction handle of the first inserted instruction |
|
522 |
*/ |
|
523 |
public InstructionHandle insert(final Instruction i, final Instruction j) { |
|
524 |
return insert(i, new InstructionList(j)); |
|
525 |
} |
|
6 | 526 |
|
46174 | 527 |
/** |
528 |
* Insert a compound instruction before instruction i. |
|
529 |
* |
|
530 |
* @param i Instruction in list |
|
531 |
* @param c The composite instruction (containing an InstructionList) |
|
532 |
* @return instruction handle of the first inserted instruction |
|
533 |
*/ |
|
534 |
public InstructionHandle insert(final Instruction i, final CompoundInstruction c) { |
|
535 |
return insert(i, c.getInstructionList()); |
|
536 |
} |
|
6 | 537 |
|
46174 | 538 |
/** |
539 |
* Insert a compound instruction. |
|
540 |
* |
|
541 |
* @param c The composite instruction (containing an InstructionList) |
|
542 |
* @return instruction handle of the first inserted instruction |
|
543 |
*/ |
|
544 |
public InstructionHandle insert(final CompoundInstruction c) { |
|
545 |
return insert(c.getInstructionList()); |
|
546 |
} |
|
547 |
||
548 |
/** |
|
549 |
* Insert an instruction before instruction (handle) ih contained in this |
|
550 |
* list. |
|
551 |
* |
|
552 |
* @param ih where to insert to the instruction list |
|
553 |
* @param i Instruction to insert |
|
554 |
* @return instruction handle of the first inserted instruction |
|
555 |
*/ |
|
556 |
public InstructionHandle insert(final InstructionHandle ih, final Instruction i) { |
|
557 |
return insert(ih, new InstructionList(i)); |
|
558 |
} |
|
6 | 559 |
|
46174 | 560 |
/** |
561 |
* Insert a compound instruction. |
|
562 |
* |
|
563 |
* @param ih where to insert the instruction list |
|
564 |
* @param c The composite instruction (containing an InstructionList) |
|
565 |
* @return instruction handle of the first inserted instruction |
|
566 |
*/ |
|
567 |
public InstructionHandle insert(final InstructionHandle ih, final CompoundInstruction c) { |
|
568 |
return insert(ih, c.getInstructionList()); |
|
569 |
} |
|
6 | 570 |
|
46174 | 571 |
/** |
572 |
* Insert an instruction before instruction (handle) ih contained in this |
|
573 |
* list. |
|
574 |
* |
|
575 |
* @param ih where to insert to the instruction list |
|
576 |
* @param i Instruction to insert |
|
577 |
* @return instruction handle of the first inserted instruction |
|
578 |
*/ |
|
579 |
public BranchHandle insert(final InstructionHandle ih, final BranchInstruction i) { |
|
580 |
final BranchHandle bh = BranchHandle.getBranchHandle(i); |
|
581 |
final InstructionList il = new InstructionList(); |
|
582 |
il.append(bh); |
|
583 |
insert(ih, il); |
|
584 |
return bh; |
|
585 |
} |
|
6 | 586 |
|
46174 | 587 |
/** |
588 |
* Take all instructions (handles) from "start" to "end" and append them |
|
589 |
* after the new location "target". Of course, "end" must be after "start" |
|
590 |
* and target must not be located withing this range. If you want to move |
|
591 |
* something to the start of the list use null as value for target.<br> |
|
592 |
* Any instruction targeters pointing to handles within the block, keep |
|
593 |
* their targets. |
|
594 |
* |
|
595 |
* @param start of moved block |
|
596 |
* @param end of moved block |
|
597 |
* @param target of moved block |
|
598 |
*/ |
|
599 |
public void move(final InstructionHandle start, final InstructionHandle end, final InstructionHandle target) { |
|
600 |
// Step 1: Check constraints |
|
601 |
if ((start == null) || (end == null)) { |
|
602 |
throw new ClassGenException("Invalid null handle: From " + start + " to " + end); |
|
603 |
} |
|
604 |
if ((target == start) || (target == end)) { |
|
605 |
throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target); |
|
606 |
} |
|
607 |
for (InstructionHandle ih = start; ih != end.getNext(); ih = ih.getNext()) { |
|
608 |
if (ih == null) { |
|
609 |
throw new ClassGenException("Invalid range: From " + start + " to " + end); |
|
610 |
} else if (ih == target) { |
|
611 |
throw new ClassGenException("Invalid range: From " + start + " to " + end + " contains target " + target); |
|
612 |
} |
|
613 |
} |
|
614 |
// Step 2: Temporarily remove the given instructions from the list |
|
615 |
final InstructionHandle prev = start.getPrev(); |
|
616 |
InstructionHandle next = end.getNext(); |
|
617 |
if (prev != null) { |
|
618 |
prev.setNext(next); |
|
619 |
} else { |
|
620 |
this.start = next; |
|
621 |
} |
|
622 |
if (next != null) { |
|
623 |
next.setPrev(prev); |
|
624 |
} else { |
|
625 |
this.end = prev; |
|
626 |
} |
|
627 |
start.setPrev(end.setNext(null)); |
|
628 |
// Step 3: append after target |
|
629 |
if (target == null) { // append to start of list |
|
630 |
if (this.start != null) { |
|
631 |
this.start.setPrev(end); |
|
632 |
} |
|
633 |
end.setNext(this.start); |
|
634 |
this.start = start; |
|
635 |
} else { |
|
636 |
next = target.getNext(); |
|
637 |
target.setNext(start); |
|
638 |
start.setPrev(target); |
|
639 |
end.setNext(next); |
|
640 |
if (next != null) { |
|
641 |
next.setPrev(end); |
|
642 |
} else { |
|
643 |
this.end = end; |
|
644 |
} |
|
645 |
} |
|
6 | 646 |
} |
647 |
||
46174 | 648 |
/** |
649 |
* Move a single instruction (handle) to a new location. |
|
650 |
* |
|
651 |
* @param ih moved instruction |
|
652 |
* @param target new location of moved instruction |
|
653 |
*/ |
|
654 |
public void move(final InstructionHandle ih, final InstructionHandle target) { |
|
655 |
move(ih, ih, target); |
|
6 | 656 |
} |
657 |
||
46174 | 658 |
/** |
659 |
* Remove from instruction `prev' to instruction `next' both contained in |
|
660 |
* this list. Throws TargetLostException when one of the removed instruction |
|
661 |
* handles is still being targeted. |
|
662 |
* |
|
663 |
* @param prev where to start deleting (predecessor, exclusive) |
|
664 |
* @param next where to end deleting (successor, exclusive) |
|
665 |
*/ |
|
666 |
private void remove(final InstructionHandle prev, InstructionHandle next) throws TargetLostException { |
|
667 |
InstructionHandle first; |
|
668 |
InstructionHandle last; // First and last deleted instruction |
|
669 |
if ((prev == null) && (next == null)) { |
|
670 |
first = start; |
|
671 |
last = end; |
|
672 |
start = end = null; |
|
673 |
} else { |
|
674 |
if (prev == null) { // At start of list |
|
675 |
first = start; |
|
676 |
start = next; |
|
677 |
} else { |
|
678 |
first = prev.getNext(); |
|
679 |
prev.setNext(next); |
|
680 |
} |
|
681 |
if (next == null) { // At end of list |
|
682 |
last = end; |
|
683 |
end = prev; |
|
684 |
} else { |
|
685 |
last = next.getPrev(); |
|
686 |
next.setPrev(prev); |
|
687 |
} |
|
688 |
} |
|
689 |
first.setPrev(null); // Completely separated from rest of list |
|
690 |
last.setNext(null); |
|
691 |
final List<InstructionHandle> target_vec = new ArrayList<>(); |
|
692 |
for (InstructionHandle ih = first; ih != null; ih = ih.getNext()) { |
|
693 |
ih.getInstruction().dispose(); // e.g. BranchInstructions release their targets |
|
694 |
} |
|
695 |
final StringBuilder buf = new StringBuilder("{ "); |
|
696 |
for (InstructionHandle ih = first; ih != null; ih = next) { |
|
697 |
next = ih.getNext(); |
|
698 |
length--; |
|
699 |
if (ih.hasTargeters()) { // Still got targeters? |
|
700 |
target_vec.add(ih); |
|
701 |
buf.append(ih.toString(true)).append(" "); |
|
702 |
ih.setNext(ih.setPrev(null)); |
|
703 |
} else { |
|
704 |
ih.dispose(); |
|
705 |
} |
|
706 |
} |
|
707 |
buf.append("}"); |
|
708 |
if (!target_vec.isEmpty()) { |
|
709 |
final InstructionHandle[] targeted = new InstructionHandle[target_vec.size()]; |
|
710 |
target_vec.toArray(targeted); |
|
711 |
throw new TargetLostException(targeted, buf.toString()); |
|
712 |
} |
|
6 | 713 |
} |
714 |
||
46174 | 715 |
/** |
716 |
* Remove instruction from this list. The corresponding Instruction handles |
|
717 |
* must not be reused! |
|
718 |
* |
|
719 |
* @param ih instruction (handle) to remove |
|
720 |
*/ |
|
721 |
public void delete(final InstructionHandle ih) throws TargetLostException { |
|
722 |
remove(ih.getPrev(), ih.getNext()); |
|
723 |
} |
|
6 | 724 |
|
46174 | 725 |
/** |
726 |
* Remove instruction from this list. The corresponding Instruction handles |
|
727 |
* must not be reused! |
|
728 |
* |
|
729 |
* @param i instruction to remove |
|
730 |
*/ |
|
731 |
public void delete(final Instruction i) throws TargetLostException { |
|
732 |
InstructionHandle ih; |
|
733 |
if ((ih = findInstruction1(i)) == null) { |
|
734 |
throw new ClassGenException("Instruction " + i + " is not contained in this list."); |
|
735 |
} |
|
736 |
delete(ih); |
|
737 |
} |
|
6 | 738 |
|
46174 | 739 |
/** |
740 |
* Remove instructions from instruction `from' to instruction `to' contained |
|
741 |
* in this list. The user must ensure that `from' is an instruction before |
|
742 |
* `to', or risk havoc. The corresponding Instruction handles must not be |
|
743 |
* reused! |
|
744 |
* |
|
745 |
* @param from where to start deleting (inclusive) |
|
746 |
* @param to where to end deleting (inclusive) |
|
747 |
*/ |
|
748 |
public void delete(final InstructionHandle from, final InstructionHandle to) throws TargetLostException { |
|
749 |
remove(from.getPrev(), to.getNext()); |
|
750 |
} |
|
6 | 751 |
|
46174 | 752 |
/** |
753 |
* Remove instructions from instruction `from' to instruction `to' contained |
|
754 |
* in this list. The user must ensure that `from' is an instruction before |
|
755 |
* `to', or risk havoc. The corresponding Instruction handles must not be |
|
756 |
* reused! |
|
757 |
* |
|
758 |
* @param from where to start deleting (inclusive) |
|
759 |
* @param to where to end deleting (inclusive) |
|
760 |
*/ |
|
761 |
public void delete(final Instruction from, final Instruction to) throws TargetLostException { |
|
762 |
InstructionHandle from_ih; |
|
763 |
InstructionHandle to_ih; |
|
764 |
if ((from_ih = findInstruction1(from)) == null) { |
|
765 |
throw new ClassGenException("Instruction " + from + " is not contained in this list."); |
|
766 |
} |
|
767 |
if ((to_ih = findInstruction2(to)) == null) { |
|
768 |
throw new ClassGenException("Instruction " + to + " is not contained in this list."); |
|
769 |
} |
|
770 |
delete(from_ih, to_ih); |
|
771 |
} |
|
772 |
||
773 |
/** |
|
774 |
* Search for given Instruction reference, start at beginning of list. |
|
775 |
* |
|
776 |
* @param i instruction to search for |
|
777 |
* @return instruction found on success, null otherwise |
|
778 |
*/ |
|
779 |
private InstructionHandle findInstruction1(final Instruction i) { |
|
780 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
781 |
if (ih.getInstruction() == i) { |
|
782 |
return ih; |
|
783 |
} |
|
784 |
} |
|
785 |
return null; |
|
6 | 786 |
} |
787 |
||
46174 | 788 |
/** |
789 |
* Search for given Instruction reference, start at end of list |
|
790 |
* |
|
791 |
* @param i instruction to search for |
|
792 |
* @return instruction found on success, null otherwise |
|
793 |
*/ |
|
794 |
private InstructionHandle findInstruction2(final Instruction i) { |
|
795 |
for (InstructionHandle ih = end; ih != null; ih = ih.getPrev()) { |
|
796 |
if (ih.getInstruction() == i) { |
|
797 |
return ih; |
|
798 |
} |
|
799 |
} |
|
800 |
return null; |
|
6 | 801 |
} |
802 |
||
46174 | 803 |
public boolean contains(final InstructionHandle i) { |
804 |
if (i == null) { |
|
805 |
return false; |
|
806 |
} |
|
807 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
808 |
if (ih == i) { |
|
809 |
return true; |
|
810 |
} |
|
811 |
} |
|
812 |
return false; |
|
813 |
} |
|
6 | 814 |
|
46174 | 815 |
public boolean contains(final Instruction i) { |
816 |
return findInstruction1(i) != null; |
|
817 |
} |
|
6 | 818 |
|
46174 | 819 |
public void setPositions() { // TODO could be package-protected? (some test code would need to be repackaged) |
820 |
setPositions(false); |
|
6 | 821 |
} |
822 |
||
46174 | 823 |
/** |
824 |
* Give all instructions their position number (offset in byte stream), |
|
825 |
* i.e., make the list ready to be dumped. |
|
826 |
* |
|
827 |
* @param check Perform sanity checks, e.g. if all targeted instructions |
|
828 |
* really belong to this list |
|
6 | 829 |
*/ |
46174 | 830 |
public void setPositions(final boolean check) { // called by code in other packages |
831 |
int max_additional_bytes = 0; |
|
832 |
int additional_bytes = 0; |
|
833 |
int index = 0; |
|
834 |
int count = 0; |
|
835 |
final int[] pos = new int[length]; |
|
836 |
/* |
|
837 |
* Pass 0: Sanity checks |
|
838 |
*/ |
|
839 |
if (check) { |
|
840 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
841 |
final Instruction i = ih.getInstruction(); |
|
842 |
if (i instanceof BranchInstruction) { // target instruction within list? |
|
843 |
Instruction inst = ((BranchInstruction) i).getTarget().getInstruction(); |
|
844 |
if (!contains(inst)) { |
|
55496 | 845 |
throw new ClassGenException("Branch target of " |
846 |
+ Const.getOpcodeName(i.getOpcode()) + ":" |
|
847 |
+ inst + " not in instruction list"); |
|
46174 | 848 |
} |
849 |
if (i instanceof Select) { |
|
850 |
final InstructionHandle[] targets = ((Select) i).getTargets(); |
|
851 |
for (final InstructionHandle target : targets) { |
|
852 |
inst = target.getInstruction(); |
|
853 |
if (!contains(inst)) { |
|
55496 | 854 |
throw new ClassGenException("Branch target of " |
855 |
+ Const.getOpcodeName(i.getOpcode()) + ":" |
|
856 |
+ inst + " not in instruction list"); |
|
46174 | 857 |
} |
858 |
} |
|
859 |
} |
|
860 |
if (!(ih instanceof BranchHandle)) { |
|
861 |
throw new ClassGenException( |
|
55496 | 862 |
"Branch instruction " |
863 |
+ Const.getOpcodeName(i.getOpcode()) + ":" |
|
864 |
+ inst + " not contained in BranchHandle."); |
|
46174 | 865 |
} |
866 |
} |
|
867 |
} |
|
868 |
} |
|
869 |
/* |
|
870 |
* Pass 1: Set position numbers and sum up the maximum number of bytes an instruction may be shifted. |
|
871 |
*/ |
|
872 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
873 |
final Instruction i = ih.getInstruction(); |
|
874 |
ih.setPosition(index); |
|
875 |
pos[count++] = index; |
|
876 |
/* |
|
877 |
* Get an estimate about how many additional bytes may be added, |
|
878 |
* because BranchInstructions may have variable length depending on the target offset |
|
879 |
* (short vs. int) or alignment issues (TABLESWITCH and LOOKUPSWITCH). |
|
880 |
*/ |
|
881 |
switch (i.getOpcode()) { |
|
882 |
case Const.JSR: |
|
883 |
case Const.GOTO: |
|
884 |
max_additional_bytes += 2; |
|
885 |
break; |
|
886 |
case Const.TABLESWITCH: |
|
887 |
case Const.LOOKUPSWITCH: |
|
888 |
max_additional_bytes += 3; |
|
889 |
break; |
|
890 |
} |
|
891 |
index += i.getLength(); |
|
892 |
} |
|
6 | 893 |
|
46174 | 894 |
/* Pass 2: Expand the variable-length (Branch)Instructions depending on |
895 |
* the target offset (short or int) and ensure that branch targets are |
|
896 |
* within this list. |
|
897 |
*/ |
|
898 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
899 |
additional_bytes += ih.updatePosition(additional_bytes, max_additional_bytes); |
|
900 |
} |
|
901 |
/* |
|
902 |
* Pass 3: Update position numbers (which may have changed due to the |
|
903 |
* preceding expansions), like pass 1. |
|
904 |
*/ |
|
905 |
index = count = 0; |
|
906 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
907 |
final Instruction i = ih.getInstruction(); |
|
908 |
ih.setPosition(index); |
|
909 |
pos[count++] = index; |
|
910 |
index += i.getLength(); |
|
911 |
} |
|
912 |
if (length == count) { |
|
913 |
byte_positions = pos; |
|
914 |
} else { |
|
915 |
byte_positions = new int[count]; // Trim to proper size |
|
916 |
System.arraycopy(pos, 0, byte_positions, 0, count); |
|
917 |
} |
|
6 | 918 |
} |
919 |
||
46174 | 920 |
/** |
921 |
* When everything is finished, use this method to convert the instruction |
|
922 |
* list into an array of bytes. |
|
923 |
* |
|
924 |
* @return the byte code ready to be dumped |
|
6 | 925 |
*/ |
46174 | 926 |
public byte[] getByteCode() { |
927 |
// Update position indices of instructions |
|
928 |
setPositions(); |
|
929 |
final ByteArrayOutputStream b = new ByteArrayOutputStream(); |
|
930 |
final DataOutputStream out = new DataOutputStream(b); |
|
931 |
try { |
|
932 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
933 |
final Instruction i = ih.getInstruction(); |
|
934 |
i.dump(out); // Traverse list |
|
935 |
} |
|
936 |
out.flush(); |
|
937 |
} catch (final IOException e) { |
|
938 |
System.err.println(e); |
|
939 |
return new byte[0]; |
|
940 |
} |
|
941 |
return b.toByteArray(); |
|
6 | 942 |
} |
943 |
||
46174 | 944 |
/** |
945 |
* @return an array of instructions without target information for branch |
|
946 |
* instructions. |
|
947 |
*/ |
|
948 |
public Instruction[] getInstructions() { |
|
949 |
final List<Instruction> instructions = new ArrayList<>(); |
|
950 |
try (ByteSequence bytes = new ByteSequence(getByteCode())) { |
|
951 |
while (bytes.available() > 0) { |
|
952 |
instructions.add(Instruction.readInstruction(bytes)); |
|
953 |
} |
|
954 |
} catch (final IOException e) { |
|
955 |
throw new ClassGenException(e.toString(), e); |
|
956 |
} |
|
957 |
return instructions.toArray(new Instruction[instructions.size()]); |
|
958 |
} |
|
6 | 959 |
|
46174 | 960 |
@Override |
961 |
public String toString() { |
|
962 |
return toString(true); |
|
6 | 963 |
} |
964 |
||
46174 | 965 |
/** |
966 |
* @param verbose toggle output format |
|
967 |
* @return String containing all instructions in this list. |
|
968 |
*/ |
|
969 |
public String toString(final boolean verbose) { |
|
970 |
final StringBuilder buf = new StringBuilder(); |
|
971 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
972 |
buf.append(ih.toString(verbose)).append("\n"); |
|
973 |
} |
|
974 |
return buf.toString(); |
|
975 |
} |
|
6 | 976 |
|
46174 | 977 |
/** |
978 |
* @return iterator that lists all instructions (handles) |
|
979 |
*/ |
|
980 |
@Override |
|
981 |
public Iterator<InstructionHandle> iterator() { |
|
982 |
return new Iterator<InstructionHandle>() { |
|
6 | 983 |
|
46174 | 984 |
private InstructionHandle ih = start; |
6 | 985 |
|
46174 | 986 |
@Override |
987 |
public InstructionHandle next() throws NoSuchElementException { |
|
988 |
if (ih == null) { |
|
989 |
throw new NoSuchElementException(); |
|
990 |
} |
|
991 |
final InstructionHandle i = ih; |
|
992 |
ih = ih.getNext(); |
|
993 |
return i; |
|
994 |
} |
|
6 | 995 |
|
46174 | 996 |
@Override |
997 |
public void remove() { |
|
998 |
throw new UnsupportedOperationException(); |
|
999 |
} |
|
6 | 1000 |
|
46174 | 1001 |
@Override |
1002 |
public boolean hasNext() { |
|
1003 |
return ih != null; |
|
1004 |
} |
|
1005 |
}; |
|
6 | 1006 |
} |
1007 |
||
46174 | 1008 |
/** |
1009 |
* @return array containing all instructions (handles) |
|
1010 |
*/ |
|
1011 |
public InstructionHandle[] getInstructionHandles() { |
|
1012 |
final InstructionHandle[] ihs = new InstructionHandle[length]; |
|
1013 |
InstructionHandle ih = start; |
|
1014 |
for (int i = 0; i < length; i++) { |
|
1015 |
ihs[i] = ih; |
|
1016 |
ih = ih.getNext(); |
|
1017 |
} |
|
1018 |
return ihs; |
|
1019 |
} |
|
6 | 1020 |
|
46174 | 1021 |
/** |
1022 |
* Get positions (offsets) of all instructions in the list. This relies on |
|
1023 |
* that the list has been freshly created from an byte code array, or that |
|
1024 |
* setPositions() has been called. Otherwise this may be inaccurate. |
|
1025 |
* |
|
1026 |
* @return array containing all instruction's offset in byte code |
|
1027 |
*/ |
|
1028 |
public int[] getInstructionPositions() { |
|
1029 |
return byte_positions; |
|
6 | 1030 |
} |
1031 |
||
46174 | 1032 |
/** |
1033 |
* @return complete, i.e., deep copy of this list |
|
6 | 1034 |
*/ |
46174 | 1035 |
public InstructionList copy() { |
1036 |
final Map<InstructionHandle, InstructionHandle> map = new HashMap<>(); |
|
1037 |
final InstructionList il = new InstructionList(); |
|
1038 |
/* |
|
55496 | 1039 |
* Pass 1: Make copies of all instructions, append them to the new list |
1040 |
* and associate old instruction references with the new ones, i.e., a 1:1 mapping. |
|
46174 | 1041 |
*/ |
1042 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
1043 |
final Instruction i = ih.getInstruction(); |
|
1044 |
final Instruction c = i.copy(); // Use clone for shallow copy |
|
1045 |
if (c instanceof BranchInstruction) { |
|
1046 |
map.put(ih, il.append((BranchInstruction) c)); |
|
1047 |
} else { |
|
1048 |
map.put(ih, il.append(c)); |
|
1049 |
} |
|
1050 |
} |
|
1051 |
/* |
|
1052 |
* Pass 2: Update branch targets. |
|
1053 |
*/ |
|
1054 |
InstructionHandle ih = start; |
|
1055 |
InstructionHandle ch = il.start; |
|
1056 |
while (ih != null) { |
|
1057 |
final Instruction i = ih.getInstruction(); |
|
1058 |
final Instruction c = ch.getInstruction(); |
|
1059 |
if (i instanceof BranchInstruction) { |
|
1060 |
final BranchInstruction bi = (BranchInstruction) i; |
|
1061 |
final BranchInstruction bc = (BranchInstruction) c; |
|
1062 |
final InstructionHandle itarget = bi.getTarget(); // old target |
|
1063 |
// New target is in hash map |
|
1064 |
bc.setTarget(map.get(itarget)); |
|
1065 |
if (bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH |
|
1066 |
final InstructionHandle[] itargets = ((Select) bi).getTargets(); |
|
1067 |
final InstructionHandle[] ctargets = ((Select) bc).getTargets(); |
|
1068 |
for (int j = 0; j < itargets.length; j++) { // Update all targets |
|
1069 |
ctargets[j] = map.get(itargets[j]); |
|
1070 |
} |
|
1071 |
} |
|
1072 |
} |
|
1073 |
ih = ih.getNext(); |
|
1074 |
ch = ch.getNext(); |
|
1075 |
} |
|
1076 |
return il; |
|
6 | 1077 |
} |
1078 |
||
46174 | 1079 |
/** |
1080 |
* Replace all references to the old constant pool with references to the |
|
1081 |
* new constant pool |
|
6 | 1082 |
*/ |
46174 | 1083 |
public void replaceConstantPool(final ConstantPoolGen old_cp, final ConstantPoolGen new_cp) { |
1084 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
1085 |
final Instruction i = ih.getInstruction(); |
|
1086 |
if (i instanceof CPInstruction) { |
|
1087 |
final CPInstruction ci = (CPInstruction) i; |
|
1088 |
final Constant c = old_cp.getConstant(ci.getIndex()); |
|
1089 |
ci.setIndex(new_cp.addConstant(c, old_cp)); |
|
1090 |
} |
|
1091 |
} |
|
1092 |
} |
|
6 | 1093 |
|
46174 | 1094 |
private void clear() { |
1095 |
start = end = null; |
|
1096 |
length = 0; |
|
6 | 1097 |
} |
1098 |
||
46174 | 1099 |
/** |
1100 |
* Delete contents of list. Provides better memory utilization, because the |
|
1101 |
* system then may reuse the instruction handles. This method is typically |
|
1102 |
* called right after {@link MethodGen#getMethod()}. |
|
1103 |
*/ |
|
1104 |
public void dispose() { |
|
1105 |
// Traverse in reverse order, because ih.next is overwritten |
|
1106 |
for (InstructionHandle ih = end; ih != null; ih = ih.getPrev()) { |
|
1107 |
/* |
|
1108 |
* Causes BranchInstructions to release target and targeters, |
|
1109 |
* because it calls dispose() on the contained instruction. |
|
1110 |
*/ |
|
1111 |
ih.dispose(); |
|
1112 |
} |
|
1113 |
clear(); |
|
6 | 1114 |
} |
1115 |
||
46174 | 1116 |
/** |
1117 |
* @return start of list |
|
1118 |
*/ |
|
1119 |
public InstructionHandle getStart() { |
|
1120 |
return start; |
|
1121 |
} |
|
6 | 1122 |
|
46174 | 1123 |
/** |
1124 |
* @return end of list |
|
1125 |
*/ |
|
1126 |
public InstructionHandle getEnd() { |
|
1127 |
return end; |
|
1128 |
} |
|
6 | 1129 |
|
46174 | 1130 |
/** |
1131 |
* @return length of list (Number of instructions, not bytes) |
|
1132 |
*/ |
|
1133 |
public int getLength() { |
|
1134 |
return length; |
|
1135 |
} |
|
6 | 1136 |
|
46174 | 1137 |
/** |
1138 |
* @return length of list (Number of instructions, not bytes) |
|
1139 |
*/ |
|
1140 |
public int size() { |
|
1141 |
return length; |
|
1142 |
} |
|
6 | 1143 |
|
46174 | 1144 |
/** |
1145 |
* Redirect all references from old_target to new_target, i.e., update |
|
1146 |
* targets of branch instructions. |
|
1147 |
* |
|
1148 |
* @param old_target the old target instruction handle |
|
1149 |
* @param new_target the new target instruction handle |
|
1150 |
*/ |
|
1151 |
public void redirectBranches(final InstructionHandle old_target, |
|
1152 |
final InstructionHandle new_target) { |
|
1153 |
for (InstructionHandle ih = start; ih != null; ih = ih.getNext()) { |
|
1154 |
final Instruction i = ih.getInstruction(); |
|
1155 |
if (i instanceof BranchInstruction) { |
|
1156 |
final BranchInstruction b = (BranchInstruction) i; |
|
1157 |
final InstructionHandle target = b.getTarget(); |
|
1158 |
if (target == old_target) { |
|
1159 |
b.setTarget(new_target); |
|
1160 |
} |
|
1161 |
if (b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH |
|
1162 |
final InstructionHandle[] targets = ((Select) b).getTargets(); |
|
1163 |
for (int j = 0; j < targets.length; j++) { |
|
1164 |
if (targets[j] == old_target) { |
|
1165 |
((Select) b).setTarget(j, new_target); |
|
1166 |
} |
|
1167 |
} |
|
1168 |
} |
|
1169 |
} |
|
1170 |
} |
|
1171 |
} |
|
6 | 1172 |
|
46174 | 1173 |
/** |
1174 |
* Redirect all references of local variables from old_target to new_target. |
|
1175 |
* |
|
1176 |
* @param lg array of local variables |
|
1177 |
* @param old_target the old target instruction handle |
|
1178 |
* @param new_target the new target instruction handle |
|
1179 |
* @see MethodGen |
|
1180 |
*/ |
|
1181 |
public void redirectLocalVariables(final LocalVariableGen[] lg, |
|
1182 |
final InstructionHandle old_target, final InstructionHandle new_target) { |
|
1183 |
for (final LocalVariableGen element : lg) { |
|
1184 |
final InstructionHandle start = element.getStart(); |
|
1185 |
final InstructionHandle end = element.getEnd(); |
|
1186 |
if (start == old_target) { |
|
1187 |
element.setStart(new_target); |
|
1188 |
} |
|
1189 |
if (end == old_target) { |
|
1190 |
element.setEnd(new_target); |
|
1191 |
} |
|
6 | 1192 |
} |
1193 |
} |
|
1194 |
||
46174 | 1195 |
/** |
1196 |
* Redirect all references of exception handlers from old_target to |
|
1197 |
* new_target. |
|
1198 |
* |
|
1199 |
* @param exceptions array of exception handlers |
|
1200 |
* @param old_target the old target instruction handle |
|
1201 |
* @param new_target the new target instruction handle |
|
1202 |
* @see MethodGen |
|
1203 |
*/ |
|
1204 |
public void redirectExceptionHandlers(final CodeExceptionGen[] exceptions, |
|
1205 |
final InstructionHandle old_target, final InstructionHandle new_target) { |
|
1206 |
for (final CodeExceptionGen exception : exceptions) { |
|
1207 |
if (exception.getStartPC() == old_target) { |
|
1208 |
exception.setStartPC(new_target); |
|
1209 |
} |
|
1210 |
if (exception.getEndPC() == old_target) { |
|
1211 |
exception.setEndPC(new_target); |
|
1212 |
} |
|
1213 |
if (exception.getHandlerPC() == old_target) { |
|
1214 |
exception.setHandlerPC(new_target); |
|
1215 |
} |
|
1216 |
} |
|
1217 |
} |
|
6 | 1218 |
|
46174 | 1219 |
private List<InstructionListObserver> observers; |
6 | 1220 |
|
46174 | 1221 |
/** |
1222 |
* Add observer for this object. |
|
1223 |
*/ |
|
1224 |
public void addObserver(final InstructionListObserver o) { |
|
1225 |
if (observers == null) { |
|
1226 |
observers = new ArrayList<>(); |
|
1227 |
} |
|
1228 |
observers.add(o); |
|
1229 |
} |
|
6 | 1230 |
|
46174 | 1231 |
/** |
1232 |
* Remove observer for this object. |
|
1233 |
*/ |
|
1234 |
public void removeObserver(final InstructionListObserver o) { |
|
1235 |
if (observers != null) { |
|
1236 |
observers.remove(o); |
|
1237 |
} |
|
1238 |
} |
|
6 | 1239 |
|
46174 | 1240 |
/** |
1241 |
* Call notify() method on all observers. This method is not called |
|
1242 |
* automatically whenever the state has changed, but has to be called by the |
|
1243 |
* user after he has finished editing the object. |
|
1244 |
*/ |
|
1245 |
public void update() { |
|
1246 |
if (observers != null) { |
|
1247 |
for (final InstructionListObserver observer : observers) { |
|
1248 |
observer.notify(this); |
|
1249 |
} |
|
1250 |
} |
|
1251 |
} |
|
6 | 1252 |
} |