2
* ProGuard -- shrinking, optimization, obfuscation, and preverification
5
* Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu)
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)
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
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
21
package proguard.classfile.editor;
23
import proguard.classfile.*;
24
import proguard.classfile.attribute.*;
25
import proguard.classfile.attribute.preverification.*;
26
import proguard.classfile.attribute.preverification.visitor.*;
27
import proguard.classfile.attribute.visitor.*;
28
import proguard.classfile.instruction.*;
29
import proguard.classfile.instruction.visitor.InstructionVisitor;
30
import proguard.classfile.util.SimplifiedVisitor;
33
* This AttributeVisitor accumulates specified changes to code, and then applies
34
* these accumulated changes to the code attributes that it visits.
36
* @author Eric Lafortune
38
public class CodeAttributeEditor
39
extends SimplifiedVisitor
40
implements AttributeVisitor,
44
VerificationTypeVisitor,
45
LineNumberInfoVisitor,
46
LocalVariableInfoVisitor,
47
LocalVariableTypeInfoVisitor
50
private static final boolean DEBUG = false;
52
private static boolean DEBUG = true;
55
private boolean updateFrameSizes;
57
private int codeLength;
58
private boolean modified;
59
private boolean simple;
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*/public boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH];
66
private int[] instructionOffsetMap = new int[ClassConstants.TYPICAL_CODE_LENGTH];
67
private int newOffset;
68
private boolean lengthIncreased;
70
private int expectedStackMapFrameOffset;
72
private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater();
73
private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
74
private final InstructionWriter instructionWriter = new InstructionWriter();
77
public CodeAttributeEditor()
83
public CodeAttributeEditor(boolean updateFrameSizes)
85
this.updateFrameSizes = updateFrameSizes;
90
* Resets the accumulated code changes.
91
* @param codeLength the length of the code that will be edited next.
93
public void reset(int codeLength)
95
this.codeLength = codeLength;
97
// Try to reuse the previous arrays.
98
if (preInsertions.length < codeLength)
100
preInsertions = new Instruction[codeLength];
101
replacements = new Instruction[codeLength];
102
postInsertions = new Instruction[codeLength];
103
deleted = new boolean[codeLength];
107
for (int index = 0; index < codeLength; index++)
109
preInsertions[index] = null;
110
replacements[index] = null;
111
postInsertions[index] = null;
112
deleted[index] = false;
123
* Remembers to place the given instruction right before the instruction
124
* at the given offset.
125
* @param instructionOffset the offset of the instruction.
126
* @param instruction the new instruction.
128
public void insertBeforeInstruction(int instructionOffset, Instruction instruction)
130
if (instructionOffset < 0 ||
131
instructionOffset >= codeLength)
133
throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
136
preInsertions[instructionOffset] = instruction;
145
* Remembers to replace the instruction at the given offset by the given
147
* @param instructionOffset the offset of the instruction to be replaced.
148
* @param instruction the new instruction.
150
public void replaceInstruction(int instructionOffset, Instruction instruction)
152
if (instructionOffset < 0 ||
153
instructionOffset >= codeLength)
155
throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
158
replacements[instructionOffset] = instruction;
165
* Remembers to place the given instruction right after the instruction
166
* at the given offset.
167
* @param instructionOffset the offset of the instruction.
168
* @param instruction the new instruction.
170
public void insertAfterInstruction(int instructionOffset, Instruction instruction)
172
if (instructionOffset < 0 ||
173
instructionOffset >= codeLength)
175
throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
178
postInsertions[instructionOffset] = instruction;
186
* Remembers to delete the instruction at the given offset.
187
* @param instructionOffset the offset of the instruction to be deleted.
189
public void deleteInstruction(int instructionOffset)
191
if (instructionOffset < 0 ||
192
instructionOffset >= codeLength)
194
throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
197
deleted[instructionOffset] = true;
205
* Remembers not to delete the instruction at the given offset.
206
* @param instructionOffset the offset of the instruction not to be deleted.
208
public void undeleteInstruction(int instructionOffset)
210
if (instructionOffset < 0 ||
211
instructionOffset >= codeLength)
213
throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]");
216
deleted[instructionOffset] = false;
221
* Returns whether the instruction at the given offset has been modified
224
public boolean isModified(int instructionOffset)
226
return preInsertions[instructionOffset] != null ||
227
replacements[instructionOffset] != null ||
228
postInsertions[instructionOffset] != null ||
229
deleted[instructionOffset];
233
// Implementations for AttributeVisitor.
235
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
238
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
241
// clazz.getName().equals("abc/Def") &&
242
// method.getName(clazz).equals("abc");
244
// TODO: Remove this when the code has stabilized.
245
// Catch any unexpected exceptions from the actual visiting method.
249
visitCodeAttribute0(clazz, method, codeAttribute);
251
catch (RuntimeException ex)
253
System.err.println("Unexpected error while editing code:");
254
System.err.println(" Class = ["+clazz.getName()+"]");
255
System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
256
System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
263
public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
267
System.out.println("CodeAttributeEditor: ["+clazz.getName()+"."+method.getName(clazz)+"]");
270
// Avoid doing any work if nothing is changing anyway.
276
// Check if we can perform a faster simple replacement of instructions.
277
if (canPerformSimpleReplacements(codeAttribute))
279
// Simply overwrite the instructions.
280
performSimpleReplacements(codeAttribute);
282
// Update the maximum stack size and local variable frame size.
283
updateFrameSizes(clazz, method, codeAttribute);
287
// Move and remap the instructions.
288
codeAttribute.u4codeLength =
289
updateInstructions(clazz, method, codeAttribute);
291
// Remap the exception table.
292
codeAttribute.exceptionsAccept(clazz, method, this);
294
// Remove exceptions with empty code blocks.
295
codeAttribute.u2exceptionTableLength =
296
removeEmptyExceptions(codeAttribute.exceptionTable,
297
codeAttribute.u2exceptionTableLength);
299
// Update the maximum stack size and local variable frame size.
300
updateFrameSizes(clazz, method, codeAttribute);
302
// Remap the line number table and the local variable table.
303
codeAttribute.attributesAccept(clazz, method, this);
305
// Make sure instructions are widened if necessary.
306
instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
311
private void updateFrameSizes(Clazz clazz, Method method, CodeAttribute codeAttribute)
313
if (updateFrameSizes)
315
stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
316
variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
321
public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
323
// Remap all stack map entries.
324
expectedStackMapFrameOffset = -1;
325
stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
329
public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
331
// Remap all stack map table entries.
332
expectedStackMapFrameOffset = 0;
333
stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
337
public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
339
// Remap all line number table entries.
340
lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this);
342
// Remove line numbers with empty code blocks.
343
lineNumberTableAttribute.u2lineNumberTableLength =
344
removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable,
345
lineNumberTableAttribute.u2lineNumberTableLength,
346
codeAttribute.u4codeLength);
350
public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
352
// Remap all local variable table entries.
353
localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
355
// Remove local variables with empty code blocks.
356
localVariableTableAttribute.u2localVariableTableLength =
357
removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
358
localVariableTableAttribute.u2localVariableTableLength,
359
codeAttribute.u2maxLocals);
363
public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
365
// Remap all local variable table entries.
366
localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
368
// Remove local variables with empty code blocks.
369
localVariableTypeTableAttribute.u2localVariableTypeTableLength =
370
removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
371
localVariableTypeTableAttribute.u2localVariableTypeTableLength,
372
codeAttribute.u2maxLocals);
377
* Checks if it is possible to modifies the given code without having to
378
* update any offsets.
379
* @param codeAttribute the code to be changed.
380
* @return the new code length.
382
private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute)
389
byte[] code = codeAttribute.code;
390
int codeLength = codeAttribute.u4codeLength;
392
// Go over all replacement instructions.
393
for (int offset = 0; offset < codeLength; offset++)
395
// Check if the replacement instruction, if any, has a different
396
// length than the original instruction.
397
Instruction replacementInstruction = replacements[offset];
398
if (replacementInstruction != null &&
399
replacementInstruction.length(offset) !=
400
InstructionFactory.create(code, offset).length(offset))
411
* Modifies the given code without updating any offsets.
412
* @param codeAttribute the code to be changed.
414
private void performSimpleReplacements(CodeAttribute codeAttribute)
416
int codeLength = codeAttribute.u4codeLength;
418
// Go over all replacement instructions.
419
for (int offset = 0; offset < codeLength; offset++)
421
// Overwrite the original instruction with the replacement
422
// instruction if any.
423
Instruction replacementInstruction = replacements[offset];
424
if (replacementInstruction != null)
426
replacementInstruction.write(codeAttribute, offset);
430
System.out.println(" Replaced "+replacementInstruction.toString(newOffset));
438
* Modifies the given code based on the previously specified changes.
439
* @param clazz the class file of the code to be changed.
440
* @param method the method of the code to be changed.
441
* @param codeAttribute the code to be changed.
442
* @return the new code length.
444
private int updateInstructions(Clazz clazz,
446
CodeAttribute codeAttribute)
448
byte[] oldCode = codeAttribute.code;
449
int oldLength = codeAttribute.u4codeLength;
451
// Make sure there is a sufficiently large instruction offset map.
452
if (instructionOffsetMap.length < oldLength + 1)
454
instructionOffsetMap = new int[oldLength + 1];
457
// Fill out the instruction offset map.
458
int newLength = mapInstructions(oldCode,
461
// Create a new code array if necessary.
464
codeAttribute.code = new byte[newLength];
467
// Prepare for possible widening of instructions.
468
instructionWriter.reset(newLength);
470
// Move the instructions into the new code array.
471
moveInstructions(clazz,
477
// We can return the new length.
483
* Fills out the instruction offset map for the given code block.
484
* @param oldCode the instructions to be moved.
485
* @param oldLength the code length.
486
* @return the new code length.
488
private int mapInstructions(byte[] oldCode, int oldLength)
490
// Start mapping instructions at the beginning.
492
lengthIncreased = false;
497
// Get the next instruction.
498
Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
500
// Compute the mapping of the instruction.
501
mapInstruction(oldOffset, instruction);
503
oldOffset += instruction.length(oldOffset);
505
if (newOffset > oldOffset)
507
lengthIncreased = true;
510
while (oldOffset < oldLength);
512
// Also add an entry for the first offset after the code.
513
instructionOffsetMap[oldOffset] = newOffset;
520
* Fills out the instruction offset map for the given instruction.
521
* @param oldOffset the instruction's old offset.
522
* @param instruction the instruction to be moved.
524
private void mapInstruction(int oldOffset,
525
Instruction instruction)
527
instructionOffsetMap[oldOffset] = newOffset;
529
// Account for the pre-inserted instruction, if any.
530
Instruction preInstruction = preInsertions[oldOffset];
531
if (preInstruction != null)
533
newOffset += preInstruction.length(newOffset);
536
// Account for the replacement instruction, or for the current
537
// instruction, if it shouldn't be deleted.
538
Instruction replacementInstruction = replacements[oldOffset];
539
if (replacementInstruction != null)
541
newOffset += replacementInstruction.length(newOffset);
543
else if (!deleted[oldOffset])
545
// Note that the instruction's length may change at its new offset,
546
// e.g. if it is a switch instruction.
547
newOffset += instruction.length(newOffset);
550
// Account for the post-inserted instruction, if any.
551
Instruction postInstruction = postInsertions[oldOffset];
552
if (postInstruction != null)
554
newOffset += postInstruction.length(newOffset);
560
* Moves the given code block to the new offsets.
561
* @param clazz the class file of the code to be changed.
562
* @param method the method of the code to be changed.
563
* @param codeAttribute the code to be changed.
564
* @param oldCode the original code to be moved.
565
* @param oldLength the original code length.
567
private void moveInstructions(Clazz clazz,
569
CodeAttribute codeAttribute,
573
// Start writing instructions at the beginning.
579
// Get the next instruction.
580
Instruction instruction = InstructionFactory.create(oldCode, oldOffset);
582
// Move the instruction to its new offset.
583
moveInstruction(clazz,
589
oldOffset += instruction.length(oldOffset);
591
while (oldOffset < oldLength);
596
* Moves the given instruction to its new offset.
597
* @param clazz the class file of the code to be changed.
598
* @param method the method of the code to be changed.
599
* @param codeAttribute the code to be changed.
600
* @param oldOffset the original instruction offset.
601
* @param instruction the original instruction.
603
private void moveInstruction(Clazz clazz,
605
CodeAttribute codeAttribute,
607
Instruction instruction)
609
// Remap and insert the pre-inserted instruction, if any.
610
Instruction preInstruction = preInsertions[oldOffset];
611
if (preInstruction != null)
613
// Remap the instruction.
614
preInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
618
System.out.println(" Pre-inserted "+preInstruction.toString(newOffset));
621
newOffset += preInstruction.length(newOffset);
624
// Remap and insert the replacement instruction, or the current
625
// instruction, if it shouldn't be deleted.
626
Instruction replacementInstruction = replacements[oldOffset];
627
if (replacementInstruction != null)
629
// Remap the instruction.
630
replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
634
System.out.println(" Replaced "+replacementInstruction.toString(newOffset));
637
newOffset += replacementInstruction.length(newOffset);
639
else if (!deleted[oldOffset])
641
// Remap the instruction.
642
instruction.accept(clazz, method, codeAttribute, oldOffset, this);
646
System.out.println(" Copied "+instruction.toString(newOffset));
649
newOffset += instruction.length(newOffset);
652
// Remap and insert the post-inserted instruction, if any.
653
Instruction postInstruction = postInsertions[oldOffset];
654
if (postInstruction != null)
656
// Remap the instruction.
657
postInstruction.accept(clazz, method, codeAttribute, oldOffset, this);
661
System.out.println(" Post-inserted "+postInstruction.toString(newOffset));
664
newOffset += postInstruction.length(newOffset);
669
// Implementations for InstructionVisitor.
671
public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
673
// Write out the instruction.
674
instructionWriter.visitSimpleInstruction(clazz,
682
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
684
// Write out the instruction.
685
instructionWriter.visitConstantInstruction(clazz,
689
constantInstruction);
693
public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
695
// Write out the instruction.
696
instructionWriter.visitVariableInstruction(clazz,
700
variableInstruction);
704
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
706
// Adjust the branch offset.
707
branchInstruction.branchOffset = remapBranchOffset(offset,
708
branchInstruction.branchOffset);
710
// Write out the instruction.
711
instructionWriter.visitBranchInstruction(clazz,
719
public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
721
// Adjust the default jump offset.
722
tableSwitchInstruction.defaultOffset = remapBranchOffset(offset,
723
tableSwitchInstruction.defaultOffset);
725
// Adjust the jump offsets.
726
remapJumpOffsets(offset,
727
tableSwitchInstruction.jumpOffsets);
729
// Write out the instruction.
730
instructionWriter.visitTableSwitchInstruction(clazz,
734
tableSwitchInstruction);
738
public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
740
// Adjust the default jump offset.
741
lookUpSwitchInstruction.defaultOffset = remapBranchOffset(offset,
742
lookUpSwitchInstruction.defaultOffset);
744
// Adjust the jump offsets.
745
remapJumpOffsets(offset,
746
lookUpSwitchInstruction.jumpOffsets);
748
// Write out the instruction.
749
instructionWriter.visitLookUpSwitchInstruction(clazz,
753
lookUpSwitchInstruction);
757
// Implementations for ExceptionInfoVisitor.
759
public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
761
// Remap the code offsets. Note that the instruction offset map also has
762
// an entry for the first offset after the code, for u2endPC.
763
exceptionInfo.u2startPC = remapInstructionOffset(exceptionInfo.u2startPC);
764
exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.u2endPC);
765
exceptionInfo.u2handlerPC = remapInstructionOffset(exceptionInfo.u2handlerPC);
769
// Implementations for StackMapFrameVisitor.
771
public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
773
// Remap the stack map frame offset.
774
int stackMapFrameOffset = remapInstructionOffset(offset);
776
int offsetDelta = stackMapFrameOffset;
778
// Compute the offset delta if the frame is part of a stack map frame
779
// table (for JDK 6.0) instead of a stack map (for Java Micro Edition).
780
if (expectedStackMapFrameOffset >= 0)
782
offsetDelta -= expectedStackMapFrameOffset;
784
expectedStackMapFrameOffset = stackMapFrameOffset + 1;
787
stackMapFrame.u2offsetDelta = offsetDelta;
791
public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
793
// Remap the stack map frame offset.
794
visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
796
// Remap the verification type offset.
797
sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
801
public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
803
// Remap the stack map frame offset.
804
visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
806
// Remap the verification type offsets.
807
moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
811
public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
813
// Remap the stack map frame offset.
814
visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
816
// Remap the verification type offsets.
817
fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
818
fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
822
// Implementations for VerificationTypeVisitor.
824
public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
827
public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType)
829
// Remap the offset of the 'new' instruction.
830
uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset);
834
// Implementations for LineNumberInfoVisitor.
836
public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo)
838
// Remap the code offset.
839
lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC);
843
// Implementations for LocalVariableInfoVisitor.
845
public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
847
// Remap the code offset and length.
848
// TODO: The local variable frame might not be strictly preserved.
849
localVariableInfo.u2length = remapBranchOffset(localVariableInfo.u2startPC,
850
localVariableInfo.u2length);
851
localVariableInfo.u2startPC = remapInstructionOffset(localVariableInfo.u2startPC);
855
// Implementations for LocalVariableTypeInfoVisitor.
857
public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
859
// Remap the code offset and length.
860
// TODO: The local variable frame might not be strictly preserved.
861
localVariableTypeInfo.u2length = remapBranchOffset(localVariableTypeInfo.u2startPC,
862
localVariableTypeInfo.u2length);
863
localVariableTypeInfo.u2startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC);
867
// Small utility methods.
870
* Adjusts the given jump offsets for the instruction at the given offset.
872
private void remapJumpOffsets(int offset, int[] jumpOffsets)
874
for (int index = 0; index < jumpOffsets.length; index++)
876
jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]);
882
* Computes the new branch offset for the instruction at the given offset
883
* with the given branch offset.
885
private int remapBranchOffset(int offset, int branchOffset)
887
return remapInstructionOffset(offset + branchOffset) - newOffset;
892
* Computes the new instruction offset for the instruction at the given offset.
894
private int remapInstructionOffset(int offset)
899
throw new IllegalArgumentException("Invalid instruction offset ["+offset+"] in code with length ["+codeLength+"]");
902
return instructionOffsetMap[offset];
907
* Returns the given list of exceptions, without the ones that have empty
910
private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos,
911
int exceptionInfoCount)
913
// Overwrite all empty exceptions.
915
for (int index = 0; index < exceptionInfoCount; index++)
917
ExceptionInfo exceptionInfo = exceptionInfos[index];
918
if (exceptionInfo.u2startPC < exceptionInfo.u2endPC)
920
exceptionInfos[newIndex++] = exceptionInfo;
929
* Returns the given list of line numbers, without the ones that have empty
930
* code blocks or that exceed the code size.
932
private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos,
933
int lineNumberInfoCount,
936
// Overwrite all empty line number entries.
938
for (int index = 0; index < lineNumberInfoCount; index++)
940
LineNumberInfo lineNumberInfo = lineNumberInfos[index];
941
int startPC = lineNumberInfo.u2startPC;
942
if (startPC < codeLength &&
943
(index == 0 || startPC > lineNumberInfos[index-1].u2startPC))
945
lineNumberInfos[newIndex++] = lineNumberInfo;
954
* Returns the given list of local variables, without the ones that have empty
955
* code blocks or that exceed the actual number of local variables.
957
private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
958
int localVariableInfoCount,
961
// Overwrite all empty local variable entries.
963
for (int index = 0; index < localVariableInfoCount; index++)
965
LocalVariableInfo localVariableInfo = localVariableInfos[index];
966
if (localVariableInfo.u2length > 0 &&
967
localVariableInfo.u2index < maxLocals)
969
localVariableInfos[newIndex++] = localVariableInfo;
978
* Returns the given list of local variable types, without the ones that
979
* have empty code blocks or that exceed the actual number of local variables.
981
private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
982
int localVariableTypeInfoCount,
985
// Overwrite all empty local variable type entries.
987
for (int index = 0; index < localVariableTypeInfoCount; index++)
989
LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
990
if (localVariableTypeInfo.u2length > 0 &&
991
localVariableTypeInfo.u2index < maxLocals)
993
localVariableTypeInfos[newIndex++] = localVariableTypeInfo;