KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > jclasslib > bytecode > CodeInsertion


1 /*
2     This library is free software; you can redistribute it and/or
3     modify it under the terms of the GNU General Public
4     License as published by the Free Software Foundation; either
5     version 2 of the license, or (at your option) any later version.
6 */

7
8 package org.gjt.jclasslib.bytecode;
9
10 import org.gjt.jclasslib.structures.InvalidByteCodeException;
11 import org.gjt.jclasslib.structures.attributes.*;
12
13 import java.util.*;
14
15 /**
16     Contains all information necessary to insert code into a
17     method. Allows for pre and post insertions. The core method
18     to perform code insertions is the static <tt>apply</tt> method.
19
20     @author <a HREF="mailto:jclasslib@ej-technologies.com">Ingo Kegel</a>
21     @version $Revision: 1.8 $ $Date: 2003/08/18 07:58:35 $
22 */

23 public class CodeInsertion {
24
25
26     /**
27         Merge two code insertions into one.
28         @param position the position of the resulting <tt>CodeInsertion</tt>
29         @param shiftTarget should offsets of branch instructions pointing to
30                            the position of the resulting code insertion
31                            be shifted or point to the beginning of the
32                            inserted code.
33         @param inner the inner <tt>CodeInsertion</tt>
34         @param outer the outer <tt>CodeInsertion</tt>
35         @return the resulting <tt>CodeInsertion</tt>
36     */

37     public static CodeInsertion merge(int position,
38                                       boolean shiftTarget,
39                                       CodeInsertion inner,
40                                       CodeInsertion outer)
41     {
42
43         if (outer == null) {
44             return inner;
45         }
46         if (inner == null) {
47             return outer;
48         }
49
50         AbstractInstruction[] preInstructions = mergeInstructions(outer.preInstructions, inner.preInstructions);
51         AbstractInstruction[] postInstructions = mergeInstructions(inner.postInstructions, outer.postInstructions);
52
53         CodeInsertion codeInsertion =
54             new CodeInsertion(position,
55                               preInstructions,
56                               postInstructions,
57                               shiftTarget);
58
59         return codeInsertion;
60     }
61
62     /**
63         Merge two arrays of instructions into one.
64         @param firstInstructions the head array of type <tt>AbstractInstruction</tt>
65         @param lastInstructions the tail array of type <tt>AbstractInstruction</tt>
66         @return the merged array of type <tt>AbstractInstruction[]</tt>
67     */

68     public static AbstractInstruction[] mergeInstructions(AbstractInstruction[] firstInstructions,
69                                                           AbstractInstruction[] lastInstructions)
70     {
71         if (firstInstructions== null) {
72             return lastInstructions;
73         }
74         if (lastInstructions == null) {
75             return firstInstructions;
76         }
77
78         AbstractInstruction[] mergedInstructions = new AbstractInstruction[firstInstructions.length + lastInstructions.length];
79         System.arraycopy(firstInstructions, 0, mergedInstructions, 0, firstInstructions.length);
80         System.arraycopy(lastInstructions, 0, mergedInstructions, firstInstructions.length, lastInstructions.length);
81
82         return mergedInstructions;
83     }
84
85     /**
86         Apply a list of <tt>CodeInsertion</tt>s to a list of instructions
87         such as the one supplied by a <tt>ByteCodeReader</tt>. Offsets
88         of branch instructions will be adapted to point to the original
89         instructions. Exception and line number tables in the associated
90         <tt>CodeAttribute</tt>will also be updated.
91      
92         @param instructions the list of instructions which is to be treated
93                             with the <tt>CodeInsertion</tt>s. This list
94                             and the resulting instructions will not be usable
95                             after the method is finished. If you need to
96                             reuse the original instructions, you have to pass
97                             a deep-cloned list into this method.
98         @param codeInsertions the list of codeInsertions which is to be applied to
99                               the list of instructions.
100         @param codeAttribute the <tt>CodeAttribute</tt> pertaining to the supplied
101                              list of instructions.
102         @return the resulting list of instructions
103         @throws InvalidByteCodeException
104      */

105     public static List apply(List instructions,
106                              List codeInsertions,
107                              CodeAttribute codeAttribute)
108
109         throws InvalidByteCodeException
110     {
111         int instructionCount = instructions.size();
112         int[] transformedIndices = new int[instructionCount];
113         for (int i = 0; i < instructionCount; i++) {
114             transformedIndices[i] = i;
115         }
116
117         List newInstructions = insertCode(instructions,
118                                           codeInsertions,
119                                           transformedIndices);
120
121         int[] oldOffsets = new int[instructionCount];
122         for (int i = 0; i < instructionCount; i++) {
123             oldOffsets[i] = ((AbstractInstruction)instructions.get(i)).getOffset();
124         }
125         int[] newOffsets = new int[newInstructions.size()];
126         calculateOffsets(newInstructions, newOffsets);
127
128
129         adjustOffsets(instructions,
130                       newInstructions,
131                       oldOffsets,
132                       newOffsets,
133                       transformedIndices);
134
135         if (codeAttribute != null) {
136             adjustExceptionTable(oldOffsets,
137                                  newOffsets,
138                                  transformedIndices,
139                                  codeAttribute);
140
141             adjustLineNumberTable(oldOffsets,
142                                   newOffsets,
143                                   transformedIndices,
144                                   codeAttribute);
145         }
146
147         applyOffsets(newInstructions, newOffsets);
148         return newInstructions;
149     }
150
151     private static List insertCode(List instructions,
152                                    List codeInsertions,
153                                    int[] transformedIndices)
154     {
155         int instructionCount = instructions.size();
156         int insertionCount = codeInsertions.size();
157
158         int newSize = calculateNewSize(instructions, codeInsertions);
159         List newInstructions = new ArrayList(newSize);
160
161         int currentInsertionIndex = 0;
162         CodeInsertion currentInsertion = (CodeInsertion)codeInsertions.get(0);
163         for (int i = 0; i < instructionCount; i++) {
164             if (currentInsertion.getPosition() < i && currentInsertionIndex < insertionCount - 1) {
165                 ++currentInsertionIndex;
166                 currentInsertion = (CodeInsertion)codeInsertions.get(currentInsertionIndex);
167             }
168             int addedBefore = 0;
169             int addedAfter = 0;
170             if (currentInsertion.getPosition() == i) {
171                 addedBefore = addInstructions(newInstructions, currentInsertion.getPreInstructions());
172             }
173             newInstructions.add(instructions.get(i));
174             if (currentInsertion.getPosition() == i) {
175                 addedAfter = addInstructions(newInstructions, currentInsertion.getPostInstructions());
176             }
177             if (addedBefore > 0 || addedAfter > 0) {
178                 shiftIndices(i, addedBefore, addedAfter, transformedIndices, currentInsertion.isShiftTarget());
179             }
180         }
181
182         return newInstructions;
183     }
184
185     private static int calculateNewSize(List instructions, List codeInsertions) {
186
187         int insertionCount = codeInsertions.size();
188         int newSize = instructions.size();
189         for (int i = 0; i < insertionCount; i++) {
190             CodeInsertion insertion = (CodeInsertion)codeInsertions.get(i);
191
192             AbstractInstruction[] preInstructions = insertion.getPreInstructions();
193             if (preInstructions != null) {
194                 newSize += preInstructions.length;
195             }
196             AbstractInstruction[] postInstructions = insertion.getPostInstructions();
197             if (postInstructions != null) {
198                 newSize += postInstructions.length;
199             }
200         }
201         return newSize;
202     }
203
204     private static void shiftIndices(int currentIndex,
205                                      int addedBefore,
206                                      int addedAfter,
207                                      int[] transformedIndices,
208                                      boolean shiftTarget)
209     {
210         if (!shiftTarget) {
211             transformedIndices[currentIndex] += addedBefore;
212         }
213         for (int i = currentIndex + 1; i < transformedIndices.length; i++) {
214             transformedIndices[i] += addedBefore + addedAfter;
215         }
216     }
217
218     private static int addInstructions(List newInstructions,
219                                        AbstractInstruction[] insertedInstructions)
220     {
221         if (insertedInstructions != null) {
222             for (int i = 0; i < insertedInstructions.length; i++) {
223                 newInstructions.add(insertedInstructions[i]);
224             }
225             return insertedInstructions.length;
226         } else {
227             return 0;
228         }
229     }
230
231     private static void calculateOffsets(List instructions,
232                                          int[] offsets)
233     {
234         int instructionCount = instructions.size();
235         int currentOffset = 0;
236         for (int i = 0; i < instructionCount; i++) {
237             offsets[i] = currentOffset;
238
239             AbstractInstruction instr = (AbstractInstruction)instructions.get(i);
240
241             int currentSize;
242             if (instr instanceof PaddedInstruction) {
243                 currentSize = ((PaddedInstruction)instr).getPaddedSize(currentOffset);
244             } else {
245                 currentSize = instr.getSize();
246             }
247             currentOffset += currentSize;
248         }
249     }
250
251     private static void applyOffsets(List instructions, int[] offsets) {
252         int instructionCount = instructions.size();
253         for (int i = 0; i < instructionCount; i++) {
254             AbstractInstruction instr = (AbstractInstruction)instructions.get(i);
255             instr.setOffset(offsets[i]);
256         }
257     }
258
259     private static void adjustOffsets(List instructions,
260                                       List newInstructions,
261                                       int[] oldOffsets,
262                                       int[] newOffsets,
263                                       int[] transformedIndices)
264         throws InvalidByteCodeException
265     {
266         int instructionCount = instructions.size();
267         for (int sourceIndex = 0; sourceIndex < instructionCount; sourceIndex++) {
268             AbstractInstruction currentInstruction = (AbstractInstruction)instructions.get(sourceIndex);
269             int branchOffset = getBranchOffset(currentInstruction);
270             if (branchOffset == 0) {
271                 continue;
272             }
273             if (currentInstruction instanceof TableSwitchInstruction) {
274                 int[] jumpOffsets = ((TableSwitchInstruction)currentInstruction).getJumpOffsets();
275                 for (int i = 0; i < jumpOffsets.length; i++) {
276                     int targetIndex = getBranchTargetIndex(instructions, sourceIndex, jumpOffsets[i]);
277                     jumpOffsets[i] =
278                         calculateNewBranchOffset(newInstructions, sourceIndex, targetIndex, transformedIndices, newOffsets);
279                 }
280             } else if (currentInstruction instanceof LookupSwitchInstruction) {
281                 List matchOffsetPairs = ((LookupSwitchInstruction)currentInstruction).getMatchOffsetPairs();
282                 for (int i = 0; i < matchOffsetPairs.size(); i++) {
283                     MatchOffsetPair matchOffsetPair =
284                         (MatchOffsetPair)matchOffsetPairs.get(i);
285                     int targetIndex = getBranchTargetIndex(instructions, sourceIndex, matchOffsetPair.getOffset());
286                     matchOffsetPair.setOffset(
287                         calculateNewBranchOffset(newInstructions, sourceIndex, targetIndex, transformedIndices, newOffsets)
288                     );
289                 }
290             }
291             int targetIndex = getBranchTargetIndex(instructions, sourceIndex, branchOffset);
292
293             setBranchOffset(
294                 currentInstruction,
295                 calculateNewBranchOffset(newInstructions, sourceIndex, targetIndex, transformedIndices, newOffsets)
296             );
297
298         }
299     }
300
301     private static int calculateNewBranchOffset(List newInstructions,
302                                                 int sourceIndex,
303                                                 int targetIndex,
304                                                 int[] transformedIndices,
305                                                 int[] newOffsets)
306 {
307             int transformedSourceIndex = transformedIndices[sourceIndex];
308             int transformedTargetIndex = transformedIndices[targetIndex];
309
310             int newBranchOffset = newOffsets[transformedTargetIndex] - newOffsets[transformedSourceIndex];
311
312             return newBranchOffset;
313     }
314
315
316     private static int getBranchOffset(AbstractInstruction instruction) {
317
318         int branchOffset = 0;
319         if (instruction.getOpcode() == Opcodes.OPCODE_GOTO_W) {
320             branchOffset = ((ImmediateIntInstruction)instruction).getImmediateInt();
321         } else if (instruction instanceof TableSwitchInstruction) {
322             branchOffset = ((TableSwitchInstruction)instruction).getDefaultOffset();
323         } else if (instruction instanceof LookupSwitchInstruction) {
324             branchOffset = ((LookupSwitchInstruction)instruction).getDefaultOffset();
325         } else if (instruction instanceof BranchInstruction) {
326             branchOffset = ((BranchInstruction)instruction).getBranchOffset();
327         }
328         return branchOffset;
329     }
330
331     private static void setBranchOffset(AbstractInstruction instruction,
332                                         int branchOffset)
333     {
334         if (instruction.getOpcode() == Opcodes.OPCODE_GOTO_W) {
335             ((ImmediateIntInstruction)instruction).setImmediateInt(branchOffset);
336         } else if (instruction instanceof TableSwitchInstruction) {
337             ((TableSwitchInstruction)instruction).setDefaultOffset(branchOffset);
338         } else if (instruction instanceof LookupSwitchInstruction) {
339             ((LookupSwitchInstruction)instruction).setDefaultOffset(branchOffset);
340         } else if (instruction instanceof BranchInstruction) {
341             ((BranchInstruction)instruction).setBranchOffset(branchOffset);
342         }
343     }
344
345     private static int getBranchTargetIndex(List instructions,
346                                             int sourceIndex,
347                                             int branchOffset)
348         throws InvalidByteCodeException
349     {
350         int instructionsCount = instructions.size();
351         int startOffset = ((AbstractInstruction)instructions.get(sourceIndex)).getOffset();
352         int step = branchOffset > 0 ? 1 : -1;
353         for (int i = sourceIndex + step; i >= 0 && i < instructionsCount; i += step) {
354             int targetOffset = ((AbstractInstruction)instructions.get(i)).getOffset();
355             if (targetOffset - startOffset == branchOffset) {
356                 return i;
357             }
358         }
359         throw new InvalidByteCodeException("Invalid branch target");
360     }
361
362     private static void adjustExceptionTable(int[] oldOffsets,
363                                              int[] newOffsets,
364                                              int[] transformedIndices,
365                                              CodeAttribute codeAttribute)
366         throws InvalidByteCodeException
367     {
368
369         ExceptionTableEntry[] exceptionTable = codeAttribute.getExceptionTable();
370         if (exceptionTable == null) {
371             return;
372         }
373
374         for (int i = 0; i < exceptionTable.length; i++) {
375             ExceptionTableEntry currentEntry = exceptionTable[i];
376             int startPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getStartPc());
377             int endPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getEndPc());
378             int handlerPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getHandlerPc());
379             if (startPcIndex < 0 || endPcIndex < 0 || handlerPcIndex < 0 ||
380                 startPcIndex == oldOffsets.length ||
381                 endPcIndex == oldOffsets.length ||
382                 handlerPcIndex == oldOffsets.length)
383             {
384                 throw new InvalidByteCodeException("Invalid exception table");
385             }
386             currentEntry.setStartPc(newOffsets[transformedIndices[startPcIndex]]);
387             currentEntry.setEndPc(newOffsets[transformedIndices[endPcIndex]]);
388             currentEntry.setHandlerPc(newOffsets[transformedIndices[handlerPcIndex]]);
389         }
390
391    }
392
393     private static void adjustLineNumberTable(int[] oldOffsets,
394                                               int[] newOffsets,
395                                               int[] transformedIndices,
396                                               CodeAttribute codeAttribute)
397         throws InvalidByteCodeException
398     {
399
400         LineNumberTableAttribute lineNumberTableAttribute =
401             (LineNumberTableAttribute)codeAttribute.findAttribute(LineNumberTableAttribute.class);
402         if (lineNumberTableAttribute == null) {
403             return;
404         }
405         LineNumberTableEntry[] lineNumberTable = lineNumberTableAttribute.getLineNumberTable();
406
407         for (int i = 0; i < lineNumberTable.length; i++) {
408             LineNumberTableEntry currentEntry = lineNumberTable[i];
409             int startPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getStartPc());
410             if (startPcIndex < 0 || startPcIndex == oldOffsets.length) {
411                 throw new InvalidByteCodeException("Invalid line number table " + currentEntry.getStartPc());
412             }
413             currentEntry.setStartPc(newOffsets[transformedIndices[startPcIndex]]);
414         }
415
416    }
417
418     private int position;
419     private AbstractInstruction[] preInstructions;
420     private AbstractInstruction[] postInstructions;
421     private boolean shiftTarget;
422
423     /**
424         Create a code insertion.
425         @param position the instruction number to which this <tt>CodeInsertion</tt>
426                         is to be applied. Corresponds to the index in the list
427                         of instructions such as the one returned by a
428                         <tt>ByteCodeReader</tt>.
429         @param preInstructions the instructions to be inserted <b>before</b>
430                                the insertion point.
431         @param postInstructions the instructions to be inserted <b>after</b>
432                                 the insertion point.
433         @param shiftTarget should offsets of branch instructions pointing to
434                            the position of this code insertion
435                            be shifted or point to the beginning of the
436                            code inserted via the <tt>preInstructions</tt>
437                            parameters.
438      */

439     public CodeInsertion(int position,
440                          AbstractInstruction[] preInstructions,
441                          AbstractInstruction[] postInstructions,
442                          boolean shiftTarget)
443     {
444         this.position = position;
445         this.preInstructions = preInstructions;
446         this.postInstructions = postInstructions;
447         this.shiftTarget = shiftTarget;
448     }
449
450     /**
451         Get the insertion position, i.e the instruction number to which
452         this <tt>CodeInsertion</tt> is to be applied. Corresponds to the
453         index in the list of instructions such as the one returned by a
454         <tt>ByteCodeReader</tt>.
455         @return the insertion position
456      */

457     public int getPosition() {
458         return position;
459     }
460
461     /**
462         Set the insertion position, i.e the instruction number to which
463         this <tt>CodeInsertion</tt> is to be applied. Corresponds to the
464         index in the list of instructions such as the one returned by a
465         <tt>ByteCodeReader</tt>.
466         @param position the insertion position
467      */

468     public void setPosition(int position) {
469         this.position = position;
470     }
471
472     /**
473         Get the instructions to be inserted <b>before</b> the insertion point.
474         @return array of instructions
475      */

476     public AbstractInstruction[] getPreInstructions() {
477         return preInstructions;
478     }
479
480     /**
481         Set the instructions to be inserted <b>before</b> the insertion point.
482         @param preInstructions array of instructions
483      */

484     public void setPreInstructions(AbstractInstruction[] preInstructions) {
485         this.preInstructions = preInstructions;
486     }
487
488     /**
489         Get the instructions to be inserted <b>after</b> the insertion point.
490         @return array of instructions
491      */

492     public AbstractInstruction[] getPostInstructions() {
493         return postInstructions;
494     }
495
496     /**
497         Set the instructions to be inserted <b>after</b> the insertion point.
498         @param postInstructions array of instructions
499      */

500     public void setPostInstructions(AbstractInstruction[] postInstructions) {
501         this.postInstructions = postInstructions;
502     }
503
504     /**
505         Get whether offsets of branch instructions pointing to the position of
506         the resulting code insertion should be shifted to the first pre-instruction
507         or continue to point at the original instruction.
508         @return the boolean value
509      */

510     public boolean isShiftTarget() {
511         return shiftTarget;
512     }
513
514     /**
515         Set whether offsets of branch instructions pointing to the position of
516         the resulting code insertion should be shifted to the first pre-instruction
517         or continue to point at the original instruction.
518         @param shiftTarget the boolean value.
519      */

520     public void setShiftTarget(boolean shiftTarget) {
521         this.shiftTarget = shiftTarget;
522     }
523 }
524
Popular Tags