📄 codeattributecomposer.java
字号:
/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2007 Eric Lafortune (eric@graphics.cornell.edu) * * This library 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 library 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 Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; 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.preverification.visitor.*;import proguard.classfile.attribute.preverification.*;import proguard.classfile.attribute.visitor.*;import proguard.classfile.instruction.*;import proguard.classfile.instruction.visitor.InstructionVisitor;import proguard.classfile.util.SimplifiedVisitor;/** * This AttributeVisitor accumulates instructions and exceptions, and then * copies them into code attributes that it visits. * * @author Eric Lafortune */public class CodeAttributeComposerextends SimplifiedVisitorimplements AttributeVisitor, InstructionVisitor, ExceptionInfoVisitor, StackMapFrameVisitor, VerificationTypeVisitor, LineNumberInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor{ //* private static final boolean DEBUG = false; /*/ public static boolean DEBUG = true; //*/ private static final int MAXIMUM_LEVELS = 32; private int maximumCodeLength; private int codeLength; private int exceptionTableLength; private int level = -1; private byte[] code = new byte[ClassConstants.TYPICAL_CODE_LENGTH]; private int[] oldInstructionOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; private int[] codeFragmentOffsets = new int[MAXIMUM_LEVELS]; private int[] codeFragmentLengths = new int[MAXIMUM_LEVELS]; private int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH]; private ExceptionInfo[] exceptionTable = new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH]; private int expectedStackMapFrameOffset; private StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); private VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); /** * Starts a new code definition. */ public void reset() { maximumCodeLength = 0; codeLength = 0; exceptionTableLength = 0; level = -1; } /** * Starts a new code fragment. Branch instructions that are added are * assumed to be relative within such code fragments. * @param maximumCodeFragmentLength the maximum length of the code that will * be added as part of this fragment. */ public void beginCodeFragment(int maximumCodeFragmentLength) { level++; if (level >= MAXIMUM_LEVELS) { throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]"); } // Make sure there is sufficient space for adding the code fragment. maximumCodeLength += maximumCodeFragmentLength; if (code.length < maximumCodeLength) { byte[] newCode = new byte[maximumCodeLength]; System.arraycopy(code, 0, newCode, 0, codeLength); code = newCode; int[] newOldInstructionOffsets = new int[maximumCodeLength]; System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength); oldInstructionOffsets = newOldInstructionOffsets; } // Try to reuse the previous array for this code fragment. if (instructionOffsetMap[level].length <= maximumCodeFragmentLength) { instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1]; } // Remember the location of the code fragment. codeFragmentOffsets[level] = codeLength; codeFragmentLengths[level] = maximumCodeFragmentLength; } /** * Appends the given instruction with the given old offset. * @param oldInstructionOffset the old offset of the instruction, to which * branches and other references in the current * code fragment are pointing. * @param instruction the instruction to be appended. */ public void appendInstruction(int oldInstructionOffset, Instruction instruction) { if (DEBUG) { System.out.println("["+codeLength+"] <- "+" ".substring(0, 2*level)+instruction.toString(oldInstructionOffset)); } // Remember the old offset of the appended instruction. oldInstructionOffsets[codeLength] = oldInstructionOffset; // Write the instruction. instruction.write(code, codeLength); // Fill out the new offset of the appended instruction. instructionOffsetMap[level][oldInstructionOffset] = codeLength; // Continue appending at the next instruction offset. codeLength += instruction.length(codeLength); } /** * Appends the given label with the given old offset. * @param oldInstructionOffset the old offset of the label, to which * branches and other references in the current * code fragment are pointing. */ public void appendLabel(int oldInstructionOffset) { if (DEBUG) { System.out.println("["+codeLength+"] <- "+" ".substring(0, 2*level)+"["+oldInstructionOffset+"] (label)"); } // Fill out the new offset of the appended instruction. instructionOffsetMap[level][oldInstructionOffset] = codeLength; } /** * Appends the given exception to the exception table. * @param exceptionInfo the exception to be appended. */ public void appendException(ExceptionInfo exceptionInfo) { if (DEBUG) { System.out.print(" "+" ".substring(0, 2*level)+"Exception ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]"); } // Remap the exception right away. visitExceptionInfo(null, null, null, exceptionInfo); if (DEBUG) { System.out.println(" -> ["+exceptionInfo.u2startPC+" -> "+exceptionInfo.u2endPC+": "+exceptionInfo.u2handlerPC+"]"); } // Don't add the exception if its instruction range is empty. if (exceptionInfo.u2startPC == exceptionInfo.u2endPC) { if (DEBUG) { System.out.println(" "+" ".substring(0, 2*level)+" (not added because of empty instruction range)"); } return; } // Make sure there is sufficient space in the exception table. if (exceptionTable.length <= exceptionTableLength) { ExceptionInfo[] newExceptionTable = new ExceptionInfo[exceptionTableLength+1]; System.arraycopy(exceptionTable, 0, newExceptionTable, 0, exceptionTableLength); exceptionTable = newExceptionTable; } // Add the exception. exceptionTable[exceptionTableLength++] = exceptionInfo; } /** * Wraps up the current code fragment, continuing with the previous one on * the stack. */ public void endCodeFragment() { if (level < 0) { throw new IllegalArgumentException("Code fragment not begun ["+level+"]"); } // Remap the instructions of the code fragment. int instructionOffset = codeFragmentOffsets[level]; while (instructionOffset < codeLength) { // Get the next instruction. Instruction instruction = InstructionFactory.create(code, instructionOffset); // Does this instruction still have to be remapped? if (oldInstructionOffsets[instructionOffset] >= 0) { // Adapt the instruction for its new offset. instruction.accept(null, null, null, instructionOffset, this); // Write the instruction back. instruction.write(code, instructionOffset); // Don't remap this instruction again. oldInstructionOffsets[instructionOffset] = -1; } // Continue remapping at the next instruction offset. instructionOffset += instruction.length(instructionOffset); } // Correct the estimated maximum code length, now that we know the // actual length of this code fragment. maximumCodeLength += codeLength - codeFragmentOffsets[level] - codeFragmentLengths[level]; level--; } // Implementations for AttributeVisitor. public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { if (DEBUG) { System.out.println("CodeAttributeComposer: putting results in ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); } if (level != -1) { throw new IllegalArgumentException("Code fragment not ended ["+level+"]"); } level++; // Make sure the code attribute has sufficient space for the composed // code. if (codeAttribute.u4codeLength < codeLength) { codeAttribute.code = new byte[codeLength]; } // Copy the composed code over into the code attribute. codeAttribute.u4codeLength = codeLength; System.arraycopy(code, 0, codeAttribute.code, 0, codeLength); // Remove exceptions with empty code blocks (done before). //exceptionTableLength = // removeEmptyExceptions(exceptionTable, exceptionTableLength); // Make sure the exception table has sufficient space for the composed // exceptions. if (codeAttribute.exceptionTable.length < exceptionTableLength) { codeAttribute.exceptionTable = new ExceptionInfo[exceptionTableLength]; } // Copy the exception table. codeAttribute.u2exceptionTableLength = exceptionTableLength; System.arraycopy(exceptionTable, 0, codeAttribute.exceptionTable, 0, exceptionTableLength); // Update the maximum stack size and local variable frame size. stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); variableSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); // Remap the line number table and the local variable table. codeAttribute.attributesAccept(clazz, method, this); // Remap the exception table. //codeAttribute.exceptionsAccept(clazz, method, this); // Remove exceptions with empty code blocks (done before). //codeAttribute.u2exceptionTableLength = // removeEmptyExceptions(codeAttribute.exceptionTable, // codeAttribute.u2exceptionTableLength); level--; } public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) { // Remap all stack map entries. expectedStackMapFrameOffset = -1; stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); } public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) { // Remap all stack map table entries. expectedStackMapFrameOffset = 0; stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this); } public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) { // Remap 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) { // Remap all local variable table entries. localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); // Remove local variables with empty code blocks.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -