diff options
Diffstat (limited to 'src/proguard/classfile/editor/CodeAttributeEditor.java')
-rw-r--r-- | src/proguard/classfile/editor/CodeAttributeEditor.java | 1267 |
1 files changed, 0 insertions, 1267 deletions
diff --git a/src/proguard/classfile/editor/CodeAttributeEditor.java b/src/proguard/classfile/editor/CodeAttributeEditor.java deleted file mode 100644 index 41b7471..0000000 --- a/src/proguard/classfile/editor/CodeAttributeEditor.java +++ /dev/null @@ -1,1267 +0,0 @@ -/* - * ProGuard -- shrinking, optimization, obfuscation, and preverification - * of Java bytecode. - * - * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -package proguard.classfile.editor; - -import proguard.classfile.*; -import proguard.classfile.attribute.*; -import proguard.classfile.attribute.annotation.*; -import proguard.classfile.attribute.annotation.target.*; -import proguard.classfile.attribute.annotation.target.visitor.*; -import proguard.classfile.attribute.annotation.visitor.TypeAnnotationVisitor; -import proguard.classfile.attribute.preverification.*; -import proguard.classfile.attribute.preverification.visitor.*; -import proguard.classfile.attribute.visitor.*; -import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.SimplifiedVisitor; -import proguard.util.ArrayUtil; - -import java.util.Arrays; - -/** - * This AttributeVisitor accumulates specified changes to code, and then applies - * these accumulated changes to the code attributes that it visits. - * - * @author Eric Lafortune - */ -public class CodeAttributeEditor -extends SimplifiedVisitor -implements AttributeVisitor, - InstructionVisitor, - ExceptionInfoVisitor, - StackMapFrameVisitor, - VerificationTypeVisitor, - LineNumberInfoVisitor, - LocalVariableInfoVisitor, - LocalVariableTypeInfoVisitor, - TypeAnnotationVisitor, - TargetInfoVisitor, - LocalVariableTargetElementVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - public static boolean DEBUG = false; - //*/ - - - private final boolean updateFrameSizes; - private final boolean shrinkInstructions; - - private int codeLength; - private boolean modified; - private boolean simple; - - /*private*/public Instruction[] preInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; - /*private*/public Instruction[] replacements = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; - /*private*/public Instruction[] postInsertions = new Instruction[ClassConstants.TYPICAL_CODE_LENGTH]; - /*private*/public boolean[] deleted = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; - - private int[] newInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; - private int newOffset; - private boolean lengthIncreased; - - private int expectedStackMapFrameOffset; - - private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); - private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); - private final InstructionWriter instructionWriter = new InstructionWriter(); - - - /** - * Creates a new CodeAttributeEditor that automatically updates frame - * sizes and shrinks instructions. - */ - public CodeAttributeEditor() - { - this(true, true); - } - - - /** - * Creates a new CodeAttributeEditor. - * @param updateFrameSizes specifies whether frame sizes of edited code - * should be updated. - * @param shrinkInstructions specifies whether added instructions should - * automatically be shrunk before being written. - */ - public CodeAttributeEditor(boolean updateFrameSizes, - boolean shrinkInstructions) - { - this.updateFrameSizes = updateFrameSizes; - this.shrinkInstructions = shrinkInstructions; - } - - - /** - * Resets the accumulated code changes. - * @param codeLength the length of the code that will be edited next. - */ - public void reset(int codeLength) - { - // Try to reuse the previous arrays. - if (preInsertions.length < codeLength) - { - preInsertions = new Instruction[codeLength]; - replacements = new Instruction[codeLength]; - postInsertions = new Instruction[codeLength]; - deleted = new boolean[codeLength]; - } - else - { - Arrays.fill(preInsertions, 0, codeLength, null); - Arrays.fill(replacements, 0, codeLength, null); - Arrays.fill(postInsertions, 0, codeLength, null); - Arrays.fill(deleted, 0, codeLength, false); - } - - this.codeLength = codeLength; - - modified = false; - simple = true; - } - - - /** - * Extends the size of the accumulated code changes. - * @param codeLength the length of the code that will be edited next. - */ - public void extend(int codeLength) - { - // Try to reuse the previous arrays. - if (preInsertions.length < codeLength) - { - preInsertions = (Instruction[])ArrayUtil.extendArray(preInsertions, codeLength); - replacements = (Instruction[])ArrayUtil.extendArray(replacements, codeLength); - postInsertions = (Instruction[])ArrayUtil.extendArray(postInsertions, codeLength); - deleted = ArrayUtil.extendArray(deleted, codeLength); - } - else - { - Arrays.fill(preInsertions, this.codeLength, codeLength, null); - Arrays.fill(replacements, this.codeLength, codeLength, null); - Arrays.fill(postInsertions, this.codeLength, codeLength, null); - Arrays.fill(deleted, this.codeLength, codeLength, false); - } - - this.codeLength = codeLength; - } - - - /** - * Remembers to place the given instruction right before the instruction - * at the given offset. - * @param instructionOffset the offset of the instruction. - * @param instruction the new instruction. - */ - public void insertBeforeInstruction(int instructionOffset, Instruction instruction) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - preInsertions[instructionOffset] = shrinkInstructions ? - instruction.shrink() : - instruction; - - modified = true; - simple = false; - } - - - /** - * Remembers to place the given instructions right before the instruction - * at the given offset. - * @param instructionOffset the offset of the instruction. - * @param instructions the new instructions. - */ - public void insertBeforeInstruction(int instructionOffset, Instruction[] instructions) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - CompositeInstruction instruction = - new CompositeInstruction(instructions); - - preInsertions[instructionOffset] = shrinkInstructions ? - instruction.shrink() : - instruction; - - modified = true; - simple = false; - } - - - /** - * Remembers to replace the instruction at the given offset by the given - * instruction. - * @param instructionOffset the offset of the instruction to be replaced. - * @param instruction the new instruction. - */ - public void replaceInstruction(int instructionOffset, Instruction instruction) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - replacements[instructionOffset] = shrinkInstructions ? - instruction.shrink() : - instruction; - - modified = true; - } - - - /** - * Remembers to replace the instruction at the given offset by the given - * instructions. - * @param instructionOffset the offset of the instruction to be replaced. - * @param instructions the new instructions. - */ - public void replaceInstruction(int instructionOffset, Instruction[] instructions) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - CompositeInstruction instruction = - new CompositeInstruction(instructions); - - replacements[instructionOffset] = shrinkInstructions ? - instruction.shrink() : - instruction; - - modified = true; - } - - - /** - * Remembers to place the given instruction right after the instruction - * at the given offset. - * @param instructionOffset the offset of the instruction. - * @param instruction the new instruction. - */ - public void insertAfterInstruction(int instructionOffset, Instruction instruction) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - postInsertions[instructionOffset] = shrinkInstructions ? - instruction.shrink() : - instruction; - - modified = true; - simple = false; - } - - - /** - * Remembers to place the given instructions right after the instruction - * at the given offset. - * @param instructionOffset the offset of the instruction. - * @param instructions the new instructions. - */ - public void insertAfterInstruction(int instructionOffset, Instruction[] instructions) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - CompositeInstruction instruction = - new CompositeInstruction(instructions); - - postInsertions[instructionOffset] = shrinkInstructions ? - instruction.shrink() : - instruction; - - modified = true; - simple = false; - } - - - /** - * Remembers to delete the instruction at the given offset. - * @param instructionOffset the offset of the instruction to be deleted. - */ - public void deleteInstruction(int instructionOffset) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - deleted[instructionOffset] = true; - - modified = true; - simple = false; - } - - - /** - * Remembers not to delete the instruction at the given offset. - * @param instructionOffset the offset of the instruction not to be deleted. - */ - public void undeleteInstruction(int instructionOffset) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - deleted[instructionOffset] = false; - } - - - /** - * Clears all modifications of the instruction at the given offset. - * @param instructionOffset the offset of the instruction to be deleted. - */ - public void clearModifications(int instructionOffset) - { - if (instructionOffset < 0 || - instructionOffset >= codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+instructionOffset+"] in code with length ["+codeLength+"]"); - } - - preInsertions[instructionOffset] = null; - replacements[instructionOffset] = null; - postInsertions[instructionOffset] = null; - deleted[instructionOffset] = false; - } - - - /** - * Returns whether the code has been modified in any way. - */ - public boolean isModified() - { - return modified; - } - - - /** - * Returns whether the instruction at the given offset has been modified - * in any way. - */ - public boolean isModified(int instructionOffset) - { - return preInsertions[instructionOffset] != null || - replacements[instructionOffset] != null || - postInsertions[instructionOffset] != null || - deleted[instructionOffset]; - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { -// DEBUG = -// clazz.getName().equals("abc/Def") && -// method.getName(clazz).equals("abc"); - - // TODO: Remove this when the code has stabilized. - // Catch any unexpected exceptions from the actual visiting method. - try - { - // Process the code. - visitCodeAttribute0(clazz, method, codeAttribute); - } - catch (RuntimeException ex) - { - System.err.println("Unexpected error while editing code:"); - System.err.println(" Class = ["+clazz.getName()+"]"); - System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); - - throw ex; - } - } - - - public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - // Do we have to update the code? - if (modified) - { - if (DEBUG) - { - System.out.println("CodeAttributeEditor: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); - } - - // Can we perform a faster simple replacement of instructions? - if (canPerformSimpleReplacements(codeAttribute)) - { - if (DEBUG) - { - System.out.println(" Simple editing"); - } - - // Simply overwrite the instructions. - performSimpleReplacements(codeAttribute); - } - else - { - if (DEBUG) - { - System.out.println(" Full editing"); - } - - // Move and remap the instructions. - codeAttribute.u4codeLength = - updateInstructions(clazz, method, codeAttribute); - - // Update the exception table. - codeAttribute.exceptionsAccept(clazz, method, this); - - // Remove exceptions with empty code blocks. - codeAttribute.u2exceptionTableLength = - removeEmptyExceptions(codeAttribute.exceptionTable, - codeAttribute.u2exceptionTableLength); - - // Update the line number table and the local variable tables. - codeAttribute.attributesAccept(clazz, method, this); - } - - // Make sure instructions are widened if necessary. - instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); - } - - // Update the maximum stack size and local variable frame size. - if (updateFrameSizes) - { - stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); - variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); - } - } - - - public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) - { - // Update all stack map entries. - expectedStackMapFrameOffset = -1; - stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); - } - - - public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) - { - // Update all stack map table entries. - expectedStackMapFrameOffset = 0; - stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); - } - - - public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) - { - // Update all line number table entries. - lineNumberTableAttribute.lineNumbersAccept(clazz, method, codeAttribute, this); - - // Remove line numbers with empty code blocks. - lineNumberTableAttribute.u2lineNumberTableLength = - removeEmptyLineNumbers(lineNumberTableAttribute.lineNumberTable, - lineNumberTableAttribute.u2lineNumberTableLength, - codeAttribute.u4codeLength); - } - - - public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) - { - // Update all local variable table entries. - localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); - } - - - public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) - { - // Update all local variable table entries. - localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); - } - - - public void visitAnyTypeAnnotationsAttribute(Clazz clazz, TypeAnnotationsAttribute typeAnnotationsAttribute) - { - typeAnnotationsAttribute.typeAnnotationsAccept(clazz, this); - } - - - /** - * Checks if it is possible to modifies the given code without having to - * update any offsets. - * @param codeAttribute the code to be changed. - * @return the new code length. - */ - private boolean canPerformSimpleReplacements(CodeAttribute codeAttribute) - { - if (!simple) - { - return false; - } - - byte[] code = codeAttribute.code; - int codeLength = codeAttribute.u4codeLength; - - // Go over all replacement instructions. - for (int offset = 0; offset < codeLength; offset++) - { - // Check if the replacement instruction, if any, has a different - // length than the original instruction. - Instruction replacementInstruction = replacements[offset]; - if (replacementInstruction != null && - replacementInstruction.length(offset) != - InstructionFactory.create(code, offset).length(offset)) - { - return false; - } - } - - return true; - } - - - /** - * Modifies the given code without updating any offsets. - * @param codeAttribute the code to be changed. - */ - private void performSimpleReplacements(CodeAttribute codeAttribute) - { - int codeLength = codeAttribute.u4codeLength; - - // Go over all replacement instructions. - for (int offset = 0; offset < codeLength; offset++) - { - // Overwrite the original instruction with the replacement - // instruction if any. - Instruction replacementInstruction = replacements[offset]; - if (replacementInstruction != null) - { - replacementInstruction.write(codeAttribute, offset); - - if (DEBUG) - { - System.out.println(" Replaced "+replacementInstruction.toString(offset)); - } - } - } - } - - - /** - * Modifies the given code based on the previously specified changes. - * @param clazz the class file of the code to be changed. - * @param method the method of the code to be changed. - * @param codeAttribute the code to be changed. - * @return the new code length. - */ - private int updateInstructions(Clazz clazz, - Method method, - CodeAttribute codeAttribute) - { - byte[] oldCode = codeAttribute.code; - int oldLength = codeAttribute.u4codeLength; - - // Make sure there is a sufficiently large instruction offset map. - if (newInstructionOffsets.length < oldLength + 1) - { - newInstructionOffsets = new int[oldLength + 1]; - } - - // Fill out the instruction offset map. - int newLength = mapInstructions(oldCode, - oldLength); - - // Create a new code array if necessary. - if (lengthIncreased) - { - codeAttribute.code = new byte[newLength]; - } - - // Prepare for possible widening of instructions. - instructionWriter.reset(newLength); - - // Move the instructions into the new code array. - moveInstructions(clazz, - method, - codeAttribute, - oldCode, - oldLength); - - // We can return the new length. - return newLength; - } - - - /** - * Fills out the instruction offset map for the given code block. - * @param oldCode the instructions to be moved. - * @param oldLength the code length. - * @return the new code length. - */ - private int mapInstructions(byte[] oldCode, int oldLength) - { - // Start mapping instructions at the beginning. - newOffset = 0; - lengthIncreased = false; - - int oldOffset = 0; - do - { - // Get the next instruction. - Instruction instruction = InstructionFactory.create(oldCode, oldOffset); - - // Compute the mapping of the instruction. - mapInstruction(oldOffset, instruction); - - oldOffset += instruction.length(oldOffset); - - if (newOffset > oldOffset) - { - lengthIncreased = true; - } - } - while (oldOffset < oldLength); - - // Also add an entry for the first offset after the code. - newInstructionOffsets[oldOffset] = newOffset; - - return newOffset; - } - - - /** - * Fills out the instruction offset map for the given instruction. - * @param oldOffset the instruction's old offset. - * @param instruction the instruction to be moved. - */ - private void mapInstruction(int oldOffset, - Instruction instruction) - { - newInstructionOffsets[oldOffset] = newOffset; - - // Account for the pre-inserted instruction, if any. - Instruction preInstruction = preInsertions[oldOffset]; - if (preInstruction != null) - { - newOffset += preInstruction.length(newOffset); - } - - // Account for the replacement instruction, or for the current - // instruction, if it shouldn't be deleted. - Instruction replacementInstruction = replacements[oldOffset]; - if (replacementInstruction != null) - { - newOffset += replacementInstruction.length(newOffset); - } - else if (!deleted[oldOffset]) - { - // Note that the instruction's length may change at its new offset, - // e.g. if it is a switch instruction. - newOffset += instruction.length(newOffset); - } - - // Account for the post-inserted instruction, if any. - Instruction postInstruction = postInsertions[oldOffset]; - if (postInstruction != null) - { - newOffset += postInstruction.length(newOffset); - } - } - - - /** - * Moves the given code block to the new offsets. - * @param clazz the class file of the code to be changed. - * @param method the method of the code to be changed. - * @param codeAttribute the code to be changed. - * @param oldCode the original code to be moved. - * @param oldLength the original code length. - */ - private void moveInstructions(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - byte[] oldCode, - int oldLength) - { - // Start writing instructions at the beginning. - newOffset = 0; - - int oldOffset = 0; - do - { - // Get the next instruction. - Instruction instruction = InstructionFactory.create(oldCode, oldOffset); - - // Move the instruction to its new offset. - moveInstruction(clazz, - method, - codeAttribute, - oldOffset, - instruction); - - oldOffset += instruction.length(oldOffset); - } - while (oldOffset < oldLength); - } - - - /** - * Moves the given instruction to its new offset. - * @param clazz the class file of the code to be changed. - * @param method the method of the code to be changed. - * @param codeAttribute the code to be changed. - * @param oldOffset the original instruction offset. - * @param instruction the original instruction. - */ - private void moveInstruction(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - int oldOffset, - Instruction instruction) - { - // Update and insert the pre-inserted instruction, if any. - Instruction preInstruction = preInsertions[oldOffset]; - if (preInstruction != null) - { - if (DEBUG) - { - System.out.println(" Pre-inserted ["+oldOffset+"] -> "+preInstruction.toString(newOffset)); - } - - // Update the instruction. - preInstruction.accept(clazz, method, codeAttribute, oldOffset, this); - } - - // Update and insert the replacement instruction, or the current - // instruction, if it shouldn't be deleted. - Instruction replacementInstruction = replacements[oldOffset]; - if (replacementInstruction != null) - { - if (DEBUG) - { - System.out.println(" Replaced ["+oldOffset+"] -> "+replacementInstruction.toString(newOffset)); - } - - // Update the instruction. - replacementInstruction.accept(clazz, method, codeAttribute, oldOffset, this); - } - else if (!deleted[oldOffset]) - { - if (DEBUG) - { - System.out.println(" Copied ["+oldOffset+"] -> "+instruction.toString(newOffset)); - } - - // Update the instruction. - instruction.accept(clazz, method, codeAttribute, oldOffset, this); - } - - // Update and insert the post-inserted instruction, if any. - Instruction postInstruction = postInsertions[oldOffset]; - if (postInstruction != null) - { - if (DEBUG) - { - System.out.println(" Post-inserted ["+oldOffset+"] -> "+postInstruction.toString(newOffset)); - } - - // Update the instruction. - postInstruction.accept(clazz, method, codeAttribute, oldOffset, this); - } - } - - - // Implementations for InstructionVisitor. - - public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) - { - // Write out the instruction. - instructionWriter.visitSimpleInstruction(clazz, - method, - codeAttribute, - newOffset, - simpleInstruction); - - newOffset += simpleInstruction.length(newOffset); - } - - - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) - { - // Write out the instruction. - instructionWriter.visitConstantInstruction(clazz, - method, - codeAttribute, - newOffset, - constantInstruction); - - newOffset += constantInstruction.length(newOffset); - } - - - public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) - { - // Write out the instruction. - instructionWriter.visitVariableInstruction(clazz, - method, - codeAttribute, - newOffset, - variableInstruction); - - newOffset += variableInstruction.length(newOffset); - } - - - public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) - { - // Update the branch offset, relative to the precise new offset. - branchInstruction.branchOffset = - newBranchOffset(offset, branchInstruction.branchOffset, newOffset); - - // Write out the instruction. - instructionWriter.visitBranchInstruction(clazz, - method, - codeAttribute, - newOffset, - branchInstruction); - - newOffset += branchInstruction.length(newOffset); - } - - - public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) - { - // Update the default jump offset, relative to the precise new offset. - tableSwitchInstruction.defaultOffset = - newBranchOffset(offset, tableSwitchInstruction.defaultOffset, newOffset); - - // Update the jump offsets, relative to the precise new offset. - newJumpOffsets(offset, - tableSwitchInstruction.jumpOffsets, - newOffset); - - // Write out the instruction. - instructionWriter.visitTableSwitchInstruction(clazz, - method, - codeAttribute, - newOffset, - tableSwitchInstruction); - - newOffset += tableSwitchInstruction.length(newOffset); - } - - - public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) - { - // Update the default jump offset, relative to the precise new offset. - lookUpSwitchInstruction.defaultOffset = - newBranchOffset(offset, lookUpSwitchInstruction.defaultOffset, newOffset); - - // Update the jump offsets, relative to the precise new offset. - newJumpOffsets(offset, - lookUpSwitchInstruction.jumpOffsets, - newOffset); - - // Write out the instruction. - instructionWriter.visitLookUpSwitchInstruction(clazz, - method, - codeAttribute, - newOffset, - lookUpSwitchInstruction); - - newOffset += lookUpSwitchInstruction.length(newOffset); - } - - - // Implementations for ExceptionInfoVisitor. - - public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) - { - // Update the code offsets. Note that the instruction offset map also - // has an entry for the first offset after the code, for u2endPC. - exceptionInfo.u2startPC = newInstructionOffset(exceptionInfo.u2startPC); - exceptionInfo.u2endPC = newInstructionOffset(exceptionInfo.u2endPC); - exceptionInfo.u2handlerPC = newInstructionOffset(exceptionInfo.u2handlerPC); - } - - - // Implementations for StackMapFrameVisitor. - - public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) - { - // Update the stack map frame offset. - int stackMapFrameOffset = newInstructionOffset(offset); - - int offsetDelta = stackMapFrameOffset; - - // Compute the offset delta if the frame is part of a stack map frame - // table (for JDK 6.0) instead of a stack map (for Java Micro Edition). - if (expectedStackMapFrameOffset >= 0) - { - offsetDelta -= expectedStackMapFrameOffset; - - expectedStackMapFrameOffset = stackMapFrameOffset + 1; - } - - stackMapFrame.u2offsetDelta = offsetDelta; - } - - - public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) - { - // Update the stack map frame offset. - visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); - - // Update the verification type offset. - sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this); - } - - - public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) - { - // Update the stack map frame offset. - visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); - - // Update the verification type offsets. - moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this); - } - - - public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) - { - // Update the stack map frame offset. - visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); - - // Update the verification type offsets. - fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this); - fullFrame.stackAccept(clazz, method, codeAttribute, offset, this); - } - - - // Implementations for VerificationTypeVisitor. - - public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {} - - - public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) - { - // Update the offset of the 'new' instruction. - uninitializedType.u2newInstructionOffset = newInstructionOffset(uninitializedType.u2newInstructionOffset); - } - - - // Implementations for LineNumberInfoVisitor. - - public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) - { - // Update the code offset. - lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC); - } - - - // Implementations for LocalVariableInfoVisitor. - - public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) - { - // Update the code offset and length. - // Be careful to update the length first. - localVariableInfo.u2length = newBranchOffset(localVariableInfo.u2startPC, localVariableInfo.u2length); - localVariableInfo.u2startPC = newInstructionOffset(localVariableInfo.u2startPC); - } - - - // Implementations for LocalVariableTypeInfoVisitor. - - public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) - { - // Update the code offset and length. - // Be careful to update the length first. - localVariableTypeInfo.u2length = newBranchOffset(localVariableTypeInfo.u2startPC, localVariableTypeInfo.u2length); - localVariableTypeInfo.u2startPC = newInstructionOffset(localVariableTypeInfo.u2startPC); - } - - - // Implementations for TypeAnnotationVisitor. - - public void visitTypeAnnotation(Clazz clazz, TypeAnnotation typeAnnotation) - { - // Update all local variable targets. - typeAnnotation.targetInfoAccept(clazz, this); - } - - - // Implementations for TargetInfoVisitor. - - public void visitAnyTargetInfo(Clazz clazz, TypeAnnotation typeAnnotation, TargetInfo targetInfo) {} - - - public void visitLocalVariableTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo) - { - // Update the offsets of the variables. - localVariableTargetInfo.targetElementsAccept(clazz, method, codeAttribute, typeAnnotation, this); - } - - - public void visitOffsetTargetInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, OffsetTargetInfo offsetTargetInfo) - { - // Update the offset. - offsetTargetInfo.u2offset = newInstructionOffset(offsetTargetInfo.u2offset); - } - - - // Implementations for LocalVariableTargetElementVisitor. - - public void visitLocalVariableTargetElement(Clazz clazz, Method method, CodeAttribute codeAttribute, TypeAnnotation typeAnnotation, LocalVariableTargetInfo localVariableTargetInfo, LocalVariableTargetElement localVariableTargetElement) - { - // Update the variable start offset and length. - // Be careful to update the length first. - localVariableTargetElement.u2length = newBranchOffset(localVariableTargetElement.u2startPC, localVariableTargetElement.u2length); - localVariableTargetElement.u2startPC = newInstructionOffset(localVariableTargetElement.u2startPC); - } - - - // Small utility methods. - - /** - * Updates the given jump offsets for the instruction at the given offset, - * relative to the given new offset. - */ - private void newJumpOffsets(int oldInstructionOffset, - int[] oldJumpOffsets, - int newInstructionOffset) - { - for (int index = 0; index < oldJumpOffsets.length; index++) - { - oldJumpOffsets[index] = newBranchOffset(oldInstructionOffset, - oldJumpOffsets[index], - newInstructionOffset); - } - } - - - /** - * Computes the new branch offset for the instruction at the given offset - * with the given branch offset, relative to the new instruction (block) - * offset. - */ - private int newBranchOffset(int oldInstructionOffset, - int oldBranchOffset) - { - return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - - newInstructionOffset(oldInstructionOffset); - } - - - /** - * Computes the new branch offset for the instruction at the given offset - * with the given branch offset, relative to the given new offset. - */ - private int newBranchOffset(int oldInstructionOffset, - int oldBranchOffset, - int newInstructionOffset) - { - return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - - newInstructionOffset; - } - - - /** - * Computes the new instruction offset for the instruction at the given - * offset. - */ - private int newInstructionOffset(int oldInstructionOffset) - { - if (oldInstructionOffset < 0 || - oldInstructionOffset > codeLength) - { - throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset+"] in code with length ["+codeLength+"]"); - } - - return newInstructionOffsets[oldInstructionOffset]; - } - - - /** - * Returns the given list of exceptions, without the ones that have empty - * code blocks. - */ - private int removeEmptyExceptions(ExceptionInfo[] exceptionInfos, - int exceptionInfoCount) - { - // Overwrite all empty exceptions. - int newIndex = 0; - for (int index = 0; index < exceptionInfoCount; index++) - { - ExceptionInfo exceptionInfo = exceptionInfos[index]; - if (exceptionInfo.u2startPC < exceptionInfo.u2endPC) - { - exceptionInfos[newIndex++] = exceptionInfo; - } - } - - return newIndex; - } - - - /** - * Returns the given list of line numbers, without the ones that have empty - * code blocks or that exceed the code size. - */ - private int removeEmptyLineNumbers(LineNumberInfo[] lineNumberInfos, - int lineNumberInfoCount, - int codeLength) - { - // Overwrite all empty line number entries. - int newIndex = 0; - for (int index = 0; index < lineNumberInfoCount; index++) - { - LineNumberInfo lineNumberInfo = lineNumberInfos[index]; - int startPC = lineNumberInfo.u2startPC; - if (startPC < codeLength && - (index == 0 || startPC > lineNumberInfos[index-1].u2startPC)) - { - lineNumberInfos[newIndex++] = lineNumberInfo; - } - } - - return newIndex; - } - - - /** - * This instruction is a composite of other instructions, for local use - * inside the editor class only. - */ - private class CompositeInstruction - extends Instruction - { - private Instruction[] instructions; - - - private CompositeInstruction(Instruction[] instructions) - { - this.instructions = instructions; - } - - - // Implementations for Instruction. - - public Instruction shrink() - { - for (int index = 0; index < instructions.length; index++) - { - instructions[index] = instructions[index].shrink(); - } - - return this; - } - - - public void write(byte[] code, int offset) - { - for (int index = 0; index < instructions.length; index++) - { - Instruction instruction = instructions[index]; - - instruction.write(code, offset); - - offset += instruction.length(offset); - } - } - - - protected void readInfo(byte[] code, int offset) - { - throw new UnsupportedOperationException("Can't read composite instruction"); - } - - - protected void writeInfo(byte[] code, int offset) - { - throw new UnsupportedOperationException("Can't write composite instruction"); - } - - - public int length(int offset) - { - int newOffset = offset; - - for (int index = 0; index < instructions.length; index++) - { - newOffset += instructions[index].length(newOffset); - } - - return newOffset - offset; - } - - - public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor) - { - if (instructionVisitor != CodeAttributeEditor.this) - { - throw new UnsupportedOperationException("Unexpected visitor ["+instructionVisitor+"]"); - } - - for (int index = 0; index < instructions.length; index++) - { - Instruction instruction = instructions[index]; - - instruction.accept(clazz, method, codeAttribute, offset, CodeAttributeEditor.this); - - offset += instruction.length(offset); - } - } - - - // Implementations for Object. - - public String toString() - { - StringBuffer stringBuffer = new StringBuffer(); - - for (int index = 0; index < instructions.length; index++) - { - stringBuffer.append(instructions[index].toString()).append("; "); - } - - return stringBuffer.toString(); - } - } -} |