KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > proguard > classfile > editor > CodeAttributeEditor


1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  * of Java bytecode.
4  *
5  * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */

21 package proguard.classfile.editor;
22
23 import proguard.classfile.*;
24 import proguard.classfile.visitor.ClassPrinter;
25 import proguard.classfile.attribute.*;
26 import proguard.classfile.attribute.preverification.*;
27 import proguard.classfile.attribute.preverification.visitor.*;
28 import proguard.classfile.attribute.visitor.*;
29 import proguard.classfile.instruction.*;
30 import proguard.classfile.instruction.visitor.InstructionVisitor;
31 import proguard.classfile.util.SimplifiedVisitor;
32
33 /**
34  * This AttributeVisitor accumulates specified changes to code, and then applies
35  * these accumulated changes to the code attributes that it visits.
36  *
37  * @author Eric Lafortune
38  */

39 public class CodeAttributeEditor
40 extends SimplifiedVisitor
41 implements AttributeVisitor,
42              InstructionVisitor,
43              ExceptionInfoVisitor,
44              StackMapFrameVisitor,
45              VerificationTypeVisitor,
46              LineNumberInfoVisitor,
47              LocalVariableInfoVisitor,
48              LocalVariableTypeInfoVisitor
49 {
50     //*
51
private static final boolean DEBUG = false;
52     /*/
53     private static boolean DEBUG = true;
54     //*/

55
56
57     private int codeLength;
58     private boolean modified;
59     private boolean simple;
60
61     /*private*/public Instruction[] preInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
62     /*private*/public Instruction[] replacements = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
63     /*private*/public Instruction[] postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH];
64     private boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
65
66     private int[] instructionOffsetMap = new int[ClassConstants.TYPICAL_CODE_LENGTH];
67     private int newOffset;
68     private boolean lengthIncreased;
69
70     private int expectedStackMapFrameOffset;
71
72     private StackSizeUpdater stackSizeUpdater = new StackSizeUpdater();
73     private VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
74     private InstructionWriter instructionWriter = new InstructionWriter();
75
76
77     /**
78      * Resets the accumulated code changes.
79      * @param codeLength the length of the code that will be edited next.
80      */

81     public void reset(int codeLength)
82     {
83         this.codeLength = codeLength;
84
85         // Try to reuse the previous arrays.
86
if (preInsertions.length < codeLength)
87         {
88             preInsertions = new Instruction[codeLength];
89             replacements = new Instruction[codeLength];
90             postInsertions = new Instruction[codeLength];
91             deleted = new boolean[codeLength];
92         }
93         else
94         {
95             for (int index = 0; index < codeLength; index++)
96             {
97                 preInsertions[index] = null;
98                 replacements[index] = null;
99                 postInsertions[index] = null;
100                 deleted[index] = false;
101             }
102         }
103
104         modified = false;
105         simple = true;
106
107     }
108
109
110     /**
111      * Remembers to place the given instruction right before the instruction
112      * at the given offset.
113      * @param instructionOffset the offset of the instruction.
114      * @param instruction the new instruction.
115      */

116     public void insertBeforeInstruction(int instructionOffset, Instruction instruction)
117     {
118         if (DEBUG)
119         {
120             System.out.println("Inserting instruction before ["+instructionOffset+"]: "+instruction);
121         }
122
123         if (instructionOffset < 0 ||
124             instructionOffset >= codeLength)
125         {
126             throw new IllegalArgumentException JavaDoc("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
127         }
128
129         preInsertions[instructionOffset] = instruction;
130
131         modified = true;
132         simple = false;
133
134     }
135
136
137     /**
138      * Remembers to replace the instruction at the given offset by the given
139      * instruction.
140      * @param instructionOffset the offset of the instruction to be replaced.
141      * @param instruction the new instruction.
142      */

143     public void replaceInstruction(int instructionOffset, Instruction instruction)
144     {
145         if (instructionOffset < 0 ||
146             instructionOffset >= codeLength)
147         {
148             throw new IllegalArgumentException JavaDoc("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
149         }
150
151         replacements[instructionOffset] = instruction;
152
153         modified = true;
154     }
155
156
157     /**
158      * Remembers to place the given instruction right after the instruction
159      * at the given offset.
160      * @param instructionOffset the offset of the instruction.
161      * @param instruction the new instruction.
162      */

163     public void insertAfterInstruction(int instructionOffset, Instruction instruction)
164     {
165         if (instructionOffset < 0 ||
166             instructionOffset >= codeLength)
167         {
168             throw new IllegalArgumentException JavaDoc("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
169         }
170
171         postInsertions[instructionOffset] = instruction;
172
173         modified = true;
174         simple = false;
175     }
176
177
178     /**
179      * Remembers to delete the instruction at the given offset.
180      * @param instructionOffset the offset of the instruction to be deleted.
181      */

182     public void deleteInstruction(int instructionOffset)
183     {
184         if (instructionOffset < 0 ||
185             instructionOffset >= codeLength)
186         {
187             throw new IllegalArgumentException JavaDoc("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
188         }
189
190         deleted[instructionOffset] = true;
191
192         modified = true;
193         simple = false;
194     }
195
196
197     /**
198      * Remembers not to delete the instruction at the given offset.
199      * @param instructionOffset the offset of the instruction not to be deleted.
200      */

201     public void undeleteInstruction(int instructionOffset)
202     {
203         if (instructionOffset < 0 ||
204             instructionOffset >= codeLength)
205         {
206             throw new IllegalArgumentException JavaDoc("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
207         }
208
209         deleted[instructionOffset] = false;
210     }
211
212
213     /**
214      * Returns whether the instruction at the given offset has been modified
215      * in any way.
216      */

217     public boolean isModified(int instructionOffset)
218     {
219         return preInsertions[instructionOffset] != null ||
220                replacements[instructionOffset] != null ||
221                postInsertions[instructionOffset] != null ||
222                deleted[instructionOffset];
223     }
224
225
226     // Implementations for AttributeVisitor.
227

228     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
229
230
231     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
232     {
233 // DEBUG =
234
// clazz.getName().equals("abc/Def") &&
235
// method.getName(clazz).equals("abc");
236

237         // TODO: Remove this when the code has stabilized.
238
// Catch any unexpected exceptions from the actual visiting method.
239
try
240         {
241             // Process the code.
242
visitCodeAttribute0(clazz, method, codeAttribute);
243         }
244         catch (RuntimeException JavaDoc ex)
245         {
246             System.err.println("Unexpected error while editing code:");
247             System.err.println(" Class = ["+clazz.getName()+"]");
248             System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
249             System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
250
251             throw ex;
252         }
253     }
254
255
256     public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
257     {
258         if (DEBUG)
259         {
260             System.out.println("CodeAttributeEditor: ["+clazz.getName()+"."+method.getName(clazz)+"]");
261         }
262
263         // Avoid doing any work if nothing is changing anyway.
264
if (!modified)
265         {
266             return;
267         }
268
269         // Check if we can perform a faster simple replacement of instructions.
270
if (canPerformSimpleReplacements(codeAttribute))
271         {
272             // Simply overwrite the instructions.
273
performSimpleReplacements(codeAttribute);
274
275             // Update the maximum stack size and local variable frame size.
276
stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
277             variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
278         }
279         else
280         {
281             // Move and remap the instructions.
282
codeAttribute.u4codeLength =
283                 updateInstructions(clazz, method, codeAttribute);
284
285             // Remap the exception table.
286
codeAttribute.exceptionsAccept(clazz, method, this);
287
288             // Remove exceptions with empty code blocks.
289
codeAttribute.u2exceptionTableLength =
290                 removeEmptyExceptions(codeAttribute.exceptionTable,
291                                       codeAttribute.u2exceptionTableLength);
292
293             // Update the maximum stack size and local variable frame size.
294
stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
295             variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
296
297             // Remap the line number table and the local variable table.
298
codeAttribute.attributesAccept(clazz, method, this);
299
300             // Make sure instructions are widened if necessary.
301
instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
302         }
303     }
304
305
306     public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
307     {
308         // Remap all stack map entries.
309
expectedStackMapFrameOffset = -1;
310         stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
311     }
312
313
314     public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
315     {
316         // Remap all stack map table entries.
317
expectedStackMapFrameOffset = 0;
318         stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
319     }
320
321
322     public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
323     {
324         // Remap all line number table entries.
325
lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
326
327         // Remove line numbers with empty code blocks.
328
lineNumberTableAttribute.u2lineNumberTableLength =
329            removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable,
330                                   lineNumberTableAttribute.u2lineNumberTableLength,
331                                   codeAttribute.u4codeLength);
332     }
333
334
335     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
336     {
337         // Remap all local variable table entries.
338
localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
339
340         // Remove local variables with empty code blocks.
341
localVariableTableAttribute.u2localVariableTableLength =
342             removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
343                                       localVariableTableAttribute.u2localVariableTableLength,
344                                       codeAttribute.u2maxLocals);
345     }
346
347
348     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
349     {
350         // Remap all local variable table entries.
351
localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
352
353         // Remove local variables with empty code blocks.
354
localVariableTypeTableAttribute.u2localVariableTypeTableLength =
355             removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
356                                           localVariableTypeTableAttribute.u2localVariableTypeTableLength,
357                                           codeAttribute.u2maxLocals);
358     }
359
360
361     /**
362      * Checks if it is possible to modifies the given code without having to
363      * update any offsets.
364      * @param codeAttribute the code to be changed.
365      * @return the new code length.
366      */

367     private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute)
368     {
369         if (!simple)
370         {
371             return false;
372         }
373
374         byte[] code = codeAttribute.code;
375         int codeLength = codeAttribute.u4codeLength;
376
377         // Go over all replacement instructions.
378
for (int offset = 0; offset < codeLength; offset++)
379         {
380             // Check if the replacement instruction, if any, has a different
381
// length than the original instruction.
382
Instruction replacementInstruction = replacements[offset];
383             if (replacementInstruction != null &&
384                 replacementInstruction.length(offset) !=
385                     InstructionFactory.create(code, offset).length(offset))
386             {
387                 return false;
388             }
389         }
390
391         return true;
392     }
393
394
395     /**
396      * Modifies the given code without updating any offsets.
397      * @param codeAttribute the code to be changed.
398      */

399     private void performSimpleReplacements(CodeAttribute codeAttribute)
400     {
401         int codeLength = codeAttribute.u4codeLength;
402
403         // Go over all replacement instructions.
404
for (int offset = 0; offset < codeLength; offset++)
405         {
406             // Overwrite the original instruction with the replacement
407
// instruction if any.
408
Instruction replacementInstruction = replacements[offset];
409             if (replacementInstruction != null)
410             {
411                 replacementInstruction.write(codeAttribute, offset);
412             }
413         }
414     }
415
416
417     /**
418      * Modifies the given code based on the previously specified changes.
419      * @param clazz the class file of the code to be changed.
420      * @param method the method of the code to be changed.
421      * @param codeAttribute the code to be changed.
422      * @return the new code length.
423      */

424     private int updateInstructions(Clazz clazz,
425                                    Method method,
426                                    CodeAttribute codeAttribute)
427     {
428         byte[] oldCode = codeAttribute.code;
429         int oldLength = codeAttribute.u4codeLength;
430
431         // Make sure there is a sufficiently large instruction offset map.
432
if (instructionOffsetMap == null ||
433             instructionOffsetMap.length < oldLength + 1)
434         {
435             instructionOffsetMap = new int[oldLength + 1];
436         }
437
438         // Fill out the instruction offset map.
439
int newLength = mapInstructions(oldCode,
440                                         oldLength);
441
442         // Create a new code array if necessary.
443
if (lengthIncreased)
444         {
445             codeAttribute.code = new byte[newLength];
446         }
447
448         // Prepare for possible widening of instructions.
449
instructionWriter.reset(newLength);
450
451         // Move the instructions into the new code array.
452
moveInstructions(clazz,
453                          method,
454                          codeAttribute,
455                          oldCode,
456                          oldLength);
457
458         // We can return the new length.
459
return newLength;
460     }
461
462
463     /**
464      * Fills out the instruction offset map for the given code block.
465      * @param oldCode the instructions to be moved.
466      * @param oldLength the code length.
467      * @return the new code length.
468      */

469     private int mapInstructions(byte[] oldCode, int oldLength)
470     {
471         // Start mapping instructions at the beginning.
472
newOffset = 0;
473         lengthIncreased = false;
474
475         int oldOffset = 0;
476         do
477         {
478             // Get the next instruction.
479
Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
480
481             // Compute the mapping of the instruction.
482
mapInstruction(oldOffset, instruction);
483
484             oldOffset += instruction.length(oldOffset);
485
486             if (newOffset > oldOffset)
487             {
488                 lengthIncreased = true;
489             }
490         }
491         while (oldOffset < oldLength);
492
493         // Also add an entry for the first offset after the code.
494
instructionOffsetMap[oldOffset] = newOffset;
495
496         return newOffset;
497     }
498
499
500     /**
501      * Fills out the instruction offset map for the given instruction.
502      * @param oldOffset the instruction's old offset.
503      * @param instruction the instruction to be moved.
504      */

505     private void mapInstruction(int oldOffset,
506                                 Instruction instruction)
507     {
508         instructionOffsetMap[oldOffset] = newOffset;
509
510         // Account for the pre-inserted instruction, if any.
511
Instruction preInstruction = preInsertions[oldOffset];
512         if (preInstruction != null)
513         {
514             newOffset += preInstruction.length(newOffset);
515         }
516
517         // Account for the replacement instruction, or for the current
518
// instruction, if it shouldn't be deleted.
519
Instruction replacementInstruction = replacements[oldOffset];
520         if (replacementInstruction != null)
521         {
522             newOffset += replacementInstruction.length(newOffset);
523         }
524         else if (!deleted[oldOffset])
525         {
526             // Note that the instruction's length may change at its new offset,
527
// e.g. if it is a switch instruction.
528
newOffset += instruction.length(newOffset);
529         }
530
531         // Account for the post-inserted instruction, if any.
532
Instruction postInstruction = postInsertions[oldOffset];
533         if (postInstruction != null)
534         {
535             newOffset += postInstruction.length(newOffset);
536         }
537     }
538
539
540     /**
541      * Moves the given code block to the new offsets.
542      * @param clazz the class file of the code to be changed.
543      * @param method the method of the code to be changed.
544      * @param codeAttribute the code to be changed.
545      * @param oldCode the original code to be moved.
546      * @param oldLength the original code length.
547      */

548     private void moveInstructions(Clazz clazz,
549                                   Method method,
550                                   CodeAttribute codeAttribute,
551                                   byte[] oldCode,
552                                   int oldLength)
553     {
554         // Start writing instructions at the beginning.
555
newOffset = 0;
556
557         int oldOffset = 0;
558         do
559         {
560             // Get the next instruction.
561
Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
562
563             // Move the instruction to its new offset.
564
moveInstruction(clazz,
565                             method,
566                             codeAttribute,
567                             oldOffset,
568                             instruction);
569
570             oldOffset += instruction.length(oldOffset);
571         }
572         while (oldOffset < oldLength);
573     }
574
575
576     /**
577      * Moves the given instruction to its new offset.
578      * @param clazz the class file of the code to be changed.
579      * @param method the method of the code to be changed.
580      * @param codeAttribute the code to be changed.
581      * @param oldOffset the original instruction offset.
582      * @param instruction the original instruction.
583      */

584     private void moveInstruction(Clazz clazz,
585                                  Method method,
586                                  CodeAttribute codeAttribute,
587                                  int oldOffset,
588                                  Instruction instruction)
589     {
590         // Remap and insert the pre-inserted instruction, if any.
591
Instruction preInstruction = preInsertions[oldOffset];
592         if (preInstruction != null)
593         {
594             // Remap the instruction.
595
preInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
596
597             if (DEBUG)
598             {
599                 System.out.println(" Pre-inserted "+preInstruction.toString(newOffset));
600             }
601
602             newOffset += preInstruction.length(newOffset);
603         }
604
605         // Remap and insert the replacement instruction, or the current
606
// instruction, if it shouldn't be deleted.
607
Instruction replacementInstruction = replacements[oldOffset];
608         if (replacementInstruction != null)
609         {
610             // Remap the instruction.
611
replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
612
613             if (DEBUG)
614             {
615                 System.out.println(" Replaced "+replacementInstruction.toString(newOffset));
616             }
617
618             newOffset += replacementInstruction.length(newOffset);
619         }
620         else if (!deleted[oldOffset])
621         {
622             // Remap the instruction.
623
instruction.accept(clazz, method, codeAttribute, oldOffset, this);
624
625             if (DEBUG)
626             {
627                 System.out.println(" Copied "+instruction.toString(newOffset));
628             }
629
630             newOffset += instruction.length(newOffset);
631         }
632
633         // Remap and insert the post-inserted instruction, if any.
634
Instruction postInstruction = postInsertions[oldOffset];
635         if (postInstruction != null)
636         {
637             // Remap the instruction.
638
postInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
639
640             if (DEBUG)
641             {
642                 System.out.println(" Post-inserted "+postInstruction.toString(newOffset));
643             }
644
645             newOffset += postInstruction.length(newOffset);
646         }
647     }
648
649
650     // Implementations for InstructionVisitor.
651

652     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
653     {
654         // Write out the instruction.
655
instructionWriter.visitSimpleInstruction(clazz,
656                                                  method,
657                                                  codeAttribute,
658                                                  newOffset,
659                                                  simpleInstruction);
660     }
661
662
663     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
664     {
665         // Write out the instruction.
666
instructionWriter.visitConstantInstruction(clazz,
667                                                    method,
668                                                    codeAttribute,
669                                                    newOffset,
670                                                    constantInstruction);
671     }
672
673
674     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
675     {
676         // Write out the instruction.
677
instructionWriter.visitVariableInstruction(clazz,
678                                                    method,
679                                                    codeAttribute,
680                                                    newOffset,
681                                                    variableInstruction);
682     }
683
684
685     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
686     {
687         // Adjust the branch offset.
688
branchInstruction.branchOffset = remapBranchOffset(offset,
689                                                            branchInstruction.branchOffset);
690
691         // Write out the instruction.
692
instructionWriter.visitBranchInstruction(clazz,
693                                                  method,
694                                                  codeAttribute,
695                                                  newOffset,
696                                                  branchInstruction);
697     }
698
699
700     public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
701     {
702         // Adjust the default jump offset.
703
tableSwitchInstruction.defaultOffset = remapBranchOffset(offset,
704                                                                  tableSwitchInstruction.defaultOffset);
705
706         // Adjust the jump offsets.
707
remapJumpOffsets(offset,
708                          tableSwitchInstruction.jumpOffsets);
709
710         // Write out the instruction.
711
instructionWriter.visitTableSwitchInstruction(clazz,
712                                                       method,
713                                                       codeAttribute,
714                                                       newOffset,
715                                                       tableSwitchInstruction);
716     }
717
718
719     public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
720     {
721         // Adjust the default jump offset.
722
lookUpSwitchInstruction.defaultOffset = remapBranchOffset(offset,
723                                                                   lookUpSwitchInstruction.defaultOffset);
724
725         // Adjust the jump offsets.
726
remapJumpOffsets(offset,
727                          lookUpSwitchInstruction.jumpOffsets);
728
729         // Write out the instruction.
730
instructionWriter.visitLookUpSwitchInstruction(clazz,
731                                                        method,
732                                                        codeAttribute,
733                                                        newOffset,
734                                                        lookUpSwitchInstruction);
735     }
736
737
738     // Implementations for ExceptionInfoVisitor.
739

740     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
741     {
742         // Remap the code offsets. Note that the instruction offset map also has
743
// an entry for the first offset after the code, for u2endPC.
744
exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC);
745         exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC);
746         exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC);
747     }
748
749
750     // Implementations for StackMapFrameVisitor.
751

752     public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
753     {
754         // Remap the stack map frame offset.
755
int stackMapFrameOffset = remapInstructionOffset(offset);
756
757         int offsetDelta = stackMapFrameOffset;
758
759         // Compute the offset delta if the frame is part of a stack map frame
760
// table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
761
if (expectedStackMapFrameOffset >= 0)
762         {
763             offsetDelta -= expectedStackMapFrameOffset;
764
765             expectedStackMapFrameOffset = stackMapFrameOffset + 1;
766         }
767
768         stackMapFrame.u2offsetDelta = offsetDelta;
769     }
770
771
772     public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
773     {
774         // Remap the stack map frame offset.
775
visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
776
777         // Remap the verification type offset.
778
sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
779     }
780
781
782     public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
783     {
784         // Remap the stack map frame offset.
785
visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
786
787         // Remap the verification type offsets.
788
moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
789     }
790
791
792     public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
793     {
794         // Remap the stack map frame offset.
795
visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
796
797         // Remap the verification type offsets.
798
fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
799         fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
800     }
801
802
803     // Implementations for VerificationTypeVisitor.
804

805     public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
806
807
808     public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
809     {
810         // Remap the offset of the 'new' instruction.
811
uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
812     }
813
814
815     // Implementations for LineNumberInfoVisitor.
816

817     public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
818     {
819         // Remap the code offset.
820
lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
821     }
822
823
824     // Implementations for LocalVariableInfoVisitor.
825

826     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
827     {
828         // Remap the code offset and length.
829
// TODO: The local variable frame might not be strictly preserved.
830
localVariableInfo.u2length = remapBranchOffset(localVariableInfo.u2startPC,
831                                                         localVariableInfo.u2length);
832         localVariableInfo.u2startPC = remapInstructionOffset(localVariableInfo.u2startPC);
833     }
834
835
836     // Implementations for LocalVariableTypeInfoVisitor.
837

838     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
839     {
840         // Remap the code offset and length.
841
// TODO: The local variable frame might not be strictly preserved.
842
localVariableTypeInfo.u2length = remapBranchOffset(localVariableTypeInfo.u2startPC,
843                                                             localVariableTypeInfo.u2length);
844         localVariableTypeInfo.u2startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
845     }
846
847
848     // Small utility methods.
849

850     /**
851      * Adjusts the given jump offsets for the instruction at the given offset.
852      */

853     private void remapJumpOffsets(int offset, int[] jumpOffsets)
854     {
855         for (int index = 0; index < jumpOffsets.length; index++)
856         {
857             jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
858         }
859     }
860
861
862     /**
863      * Computes the new branch offset for the instruction at the given offset
864      * with the given branch offset.
865      */

866     private int remapBranchOffset(int offset, int branchOffset)
867     {
868         return remapInstructionOffset(offset + branchOffset) -
869                remapInstructionOffset(offset);
870     }
871
872
873     /**
874      * Computes the new instruction offset for the instruction at the given offset.
875      */

876     private int remapInstructionOffset(int offset)
877     {
878         if (offset < 0 ||
879             offset > codeLength)
880         {
881             throw new IllegalArgumentException JavaDoc("Invalid instruction offset ["+offset+"] in code with length ["+codeLength+"]");
882         }
883
884         return instructionOffsetMap[offset];
885     }
886
887
888     /**
889      * Returns the given list of exceptions, without the ones that have empty
890      * code blocks.
891      */

892     private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
893                                       int exceptionInfoCount)
894     {
895         // Overwrite all empty exceptions.
896
int newIndex = 0;
897         for (int index = 0; index < exceptionInfoCount; index++)
898         {
899             ExceptionInfo exceptionInfo = exceptionInfos[index];
900             if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
901             {
902                 exceptionInfos[newIndex++] = exceptionInfo;
903             }
904         }
905
906         return newIndex;
907     }
908
909
910     /**
911      * Returns the given list of line numbers, without the ones that have empty
912      * code blocks or that exceed the code size.
913      */

914     private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
915                                        int lineNumberInfoCount,
916                                        int codeLength)
917     {
918         // Overwrite all empty line number entries.
919
int newIndex = 0;
920         for (int index = 0; index < lineNumberInfoCount; index++)
921         {
922             LineNumberInfo lineNumberInfo = lineNumberInfos[index];
923             int startPC = lineNumberInfo.u2startPC;
924             if (startPC < codeLength &&
925                 (index == 0 || startPC > lineNumberInfos[index-1].u2startPC))
926             {
927                 lineNumberInfos[newIndex++] = lineNumberInfo;
928             }
929         }
930
931         return newIndex;
932     }
933
934
935     /**
936      * Returns the given list of local variables, without the ones that have empty
937      * code blocks or that exceed the actual number of local variables.
938      */

939     private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
940                                           int localVariableInfoCount,
941                                           int maxLocals)
942     {
943         // Overwrite all empty local variable entries.
944
int newIndex = 0;
945         for (int index = 0; index < localVariableInfoCount; index++)
946         {
947             LocalVariableInfo localVariableInfo = localVariableInfos[index];
948             if (localVariableInfo.u2length > 0 &&
949                 localVariableInfo.u2index < maxLocals)
950             {
951                 localVariableInfos[newIndex++] = localVariableInfo;
952             }
953         }
954
955         return newIndex;
956     }
957
958
959     /**
960      * Returns the given list of local variable types, without the ones that
961      * have empty code blocks or that exceed the actual number of local variables.
962      */

963     private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
964                                               int localVariableTypeInfoCount,
965                                               int maxLocals)
966     {
967         // Overwrite all empty local variable type entries.
968
int newIndex = 0;
969         for (int index = 0; index < localVariableTypeInfoCount; index++)
970         {
971             LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
972             if (localVariableTypeInfo.u2length > 0 &&
973                 localVariableTypeInfo.u2index < maxLocals)
974             {
975                 localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
976             }
977         }
978
979         return newIndex;
980     }
981 }
982
Popular Tags