summaryrefslogtreecommitdiff
path: root/src/proguard/classfile/editor/CodeAttributeComposer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/classfile/editor/CodeAttributeComposer.java')
-rw-r--r--src/proguard/classfile/editor/CodeAttributeComposer.java953
1 files changed, 0 insertions, 953 deletions
diff --git a/src/proguard/classfile/editor/CodeAttributeComposer.java b/src/proguard/classfile/editor/CodeAttributeComposer.java
deleted file mode 100644
index eaa8015..0000000
--- a/src/proguard/classfile/editor/CodeAttributeComposer.java
+++ /dev/null
@@ -1,953 +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.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 instructions and exceptions, and then
- * copies them into code attributes that it visits.
- *
- * @author Eric Lafortune
- */
-public class CodeAttributeComposer
-extends SimplifiedVisitor
-implements AttributeVisitor,
- InstructionVisitor,
- ExceptionInfoVisitor,
- StackMapFrameVisitor,
- VerificationTypeVisitor,
- LineNumberInfoVisitor,
- LocalVariableInfoVisitor,
- LocalVariableTypeInfoVisitor
-{
- //*
- private static final boolean DEBUG = false;
- /*/
- public static boolean DEBUG = false;
- //*/
-
-
- private static final int MAXIMUM_LEVELS = 32;
- private static final int INVALID = -1;
-
-
- private final boolean allowExternalBranchTargets;
- private final boolean allowExternalExceptionHandlers;
- private final boolean shrinkInstructions;
-
- 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 final int[] codeFragmentOffsets = new int[MAXIMUM_LEVELS];
- private final int[] codeFragmentLengths = new int[MAXIMUM_LEVELS];
- private final int[][] instructionOffsetMap = new int[MAXIMUM_LEVELS][ClassConstants.TYPICAL_CODE_LENGTH + 1];
-
- private ExceptionInfo[] exceptionTable = new ExceptionInfo[ClassConstants.TYPICAL_EXCEPTION_TABLE_LENGTH];
-
- private int expectedStackMapFrameOffset;
-
- private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater();
- private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater();
- private final InstructionWriter instructionWriter = new InstructionWriter();
-
-
- /**
- * Creates a new CodeAttributeComposer that doesn't allow external branch
- * targets or exception handlers and that automatically shrinks
- * instructions.
- */
- public CodeAttributeComposer()
- {
- this(false, false, true);
- }
-
-
- /**
- * Creates a new CodeAttributeComposer.
- * @param allowExternalBranchTargets specifies whether branch targets
- * can lie outside the code fragment
- * of the branch instructions.
- * @param allowExternalExceptionHandlers specifies whether exception
- * handlers can lie outside the code
- * fragment in which exceptions are
- * defined.
- * @param shrinkInstructions specifies whether instructions
- * should automatically be shrunk
- * before being written.
- */
- public CodeAttributeComposer(boolean allowExternalBranchTargets,
- boolean allowExternalExceptionHandlers,
- boolean shrinkInstructions)
- {
- this.allowExternalBranchTargets = allowExternalBranchTargets;
- this.allowExternalExceptionHandlers = allowExternalExceptionHandlers;
- this.shrinkInstructions = shrinkInstructions;
- }
-
-
- /**
- * Starts a new code definition.
- */
- public void reset()
- {
- maximumCodeLength = 0;
- codeLength = 0;
- exceptionTableLength = 0;
- level = -1;
-
- // Make sure the instruction writer has at least the same buffer size
- // as the local arrays.
- instructionWriter.reset(code.length);
- }
-
-
- /**
- * 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 (more
- * precisely, the maximum old instruction
- * offset or label that is specified, plus
- * one).
- */
- 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.
- // It's only a rough initial estimate for the code length, not even
- // necessarily a length expressed in bytes.
- maximumCodeLength += maximumCodeFragmentLength;
-
- ensureCodeLength(maximumCodeLength);
-
- // Try to reuse the previous array for this code fragment.
- if (instructionOffsetMap[level].length <= maximumCodeFragmentLength)
- {
- instructionOffsetMap[level] = new int[maximumCodeFragmentLength + 1];
- }
-
- // Initialize the offset map.
- for (int index = 0; index <= maximumCodeFragmentLength; index++)
- {
- instructionOffsetMap[level][index] = INVALID;
- }
-
- // Remember the location of the code fragment.
- codeFragmentOffsets[level] = codeLength;
- codeFragmentLengths[level] = maximumCodeFragmentLength;
- }
-
-
- /**
- * Appends the given instruction with the given old offset.
- * Branch instructions must fit, for instance by enabling automatic
- * shrinking of instructions.
- * @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 (shrinkInstructions)
- {
- instruction = instruction.shrink();
- }
-
- if (DEBUG)
- {
- println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset));
- }
-
- // Make sure the code and offset arrays are large enough.
- int newCodeLength = codeLength + instruction.length(codeLength);
-
- ensureCodeLength(newCodeLength);
-
- // Remember the old offset of the appended instruction.
- oldInstructionOffsets[codeLength] = oldInstructionOffset;
-
- // Fill out the new offset of the appended instruction.
- instructionOffsetMap[level][oldInstructionOffset] = codeLength;
-
- // Write the instruction. The instruction writer may widen it later on,
- // if necessary.
- instruction.accept(null,
- null,
- new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
- codeLength,
- instructionWriter);
- //instruction.write(code, codeLength);
-
- // Continue appending at the next instruction offset.
- codeLength = newCodeLength;
- }
-
-
- /**
- * 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)
- {
- println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)");
- }
-
- // Make sure the code and offset arrays are large enough.
- ensureCodeLength(codeLength + 1);
-
- // Remember the old offset of the following instruction.
- oldInstructionOffsets[codeLength] = oldInstructionOffset;
-
- // Fill out the new offset of the following instruction.
- instructionOffsetMap[level][oldInstructionOffset] = codeLength;
- }
-
-
- /**
- * Appends the given instruction without defined offsets.
- * @param instructions the instructions to be appended.
- */
- public void appendInstructions(Instruction[] instructions)
- {
- for (int index = 0; index < instructions.length; index++)
- {
- appendInstruction(instructions[index]);
- }
- }
-
-
- /**
- * Appends the given instruction without a defined offset.
- * Branch instructions should have a label, to allow computing the
- * new relative offset.
- * Branch instructions must fit, for instance by enabling automatic
- * shrinking of instructions.
- * @param instruction the instruction to be appended.
- */
- public void appendInstruction(Instruction instruction)
- {
- if (shrinkInstructions)
- {
- instruction = instruction.shrink();
- }
-
- if (DEBUG)
- {
- println("["+codeLength+"] <- ", instruction.toString());
- }
-
- // Make sure the code array is large enough.
- int newCodeLength = codeLength + instruction.length(codeLength);
-
- ensureCodeLength(newCodeLength);
-
- // Write the instruction. The instruction writer may widen it later on,
- // if necessary.
- instruction.accept(null,
- null,
- new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
- codeLength,
- instructionWriter);
- //instruction.write(code, codeLength);
-
- // Continue appending at the next instruction offset.
- codeLength = newCodeLength;
- }
-
-
- /**
- * Appends the given exception to the exception table.
- * @param exceptionInfo the exception to be appended.
- */
- public void appendException(ExceptionInfo exceptionInfo)
- {
- if (DEBUG)
- {
- print(" ", "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)
- {
- println(" ", " (not added because of empty instruction range)");
- }
-
- return;
- }
-
- // Add the exception.
- exceptionTable =
- (ExceptionInfo[])ArrayUtil.add(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. The instruction writer may still
- // widen it later on, if necessary.
- instruction.accept(null,
- null,
- new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null),
- instructionOffset,
- instructionWriter);
- //instruction.write(code, codeLength);
- }
-
- // 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];
-
- // Try to remap the exception handlers that couldn't be remapped before.
- if (allowExternalExceptionHandlers)
- {
- for (int index = 0; index < exceptionTableLength; index++)
- {
- ExceptionInfo exceptionInfo = exceptionTable[index];
-
- // Unmapped exception handlers are still negated.
- int handlerPC = -exceptionInfo.u2handlerPC;
- if (handlerPC > 0)
- {
- if (remappableExceptionHandler(handlerPC))
- {
- exceptionInfo.u2handlerPC = newInstructionOffset(handlerPC);
- }
- else if (level == 0)
- {
- throw new IllegalStateException("Couldn't remap exception handler offset ["+handlerPC+"]");
- }
- }
- }
- }
-
- 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 (done before).
- //codeAttribute.exceptionsAccept(clazz, method, this);
-
- // Remove exceptions with empty code blocks (done before).
- //codeAttribute.u2exceptionTableLength =
- // removeEmptyExceptions(codeAttribute.exceptionTable,
- // codeAttribute.u2exceptionTableLength);
-
- // Make sure instructions are widened if necessary.
- instructionWriter.visitCodeAttribute(clazz, method, codeAttribute);
-
- 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.
- localVariableTableAttribute.u2localVariableTableLength =
- removeEmptyLocalVariables(localVariableTableAttribute.localVariableTable,
- localVariableTableAttribute.u2localVariableTableLength,
- codeAttribute.u2maxLocals);
- }
-
-
- public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
- {
- // Remap all local variable table entries.
- localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
-
- // Remove local variables with empty code blocks.
- localVariableTypeTableAttribute.u2localVariableTypeTableLength =
- removeEmptyLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
- localVariableTypeTableAttribute.u2localVariableTypeTableLength,
- codeAttribute.u2maxLocals);
- }
-
-
- // Implementations for InstructionVisitor.
-
- public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
-
-
- public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
- {
- try
- {
- // Adjust the branch offset.
- branchInstruction.branchOffset =
- newBranchOffset(offset, branchInstruction.branchOffset);
-
- // Don't remap this instruction again.
- oldInstructionOffsets[offset] = -1;
- }
- catch (IllegalArgumentException e)
- {
- if (level == 0 || !allowExternalBranchTargets)
- {
- throw e;
- }
- }
- }
-
-
- public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
- {
- try
- {
- // TODO: We're assuming we can adjust no offsets or all offsets at once.
- // Adjust the default jump offset.
- switchInstruction.defaultOffset =
- newBranchOffset(offset, switchInstruction.defaultOffset);
-
- // Adjust the jump offsets.
- updateJumpOffsets(offset,
- switchInstruction.jumpOffsets);
-
- // Don't remap this instruction again.
- oldInstructionOffsets[offset] = -1;
- }
- catch (IllegalArgumentException e)
- {
- if (level == 0 || !allowExternalBranchTargets)
- {
- throw e;
- }
- }
- }
-
-
- // Implementations for ExceptionInfoVisitor.
-
- public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
- {
- // Remap 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);
-
- // See if we can remap the handler right away. Unmapped exception
- // handlers are negated, in order to mark them as external.
- int handlerPC = exceptionInfo.u2handlerPC;
- exceptionInfo.u2handlerPC =
- !allowExternalExceptionHandlers ||
- remappableExceptionHandler(handlerPC) ?
- newInstructionOffset(handlerPC) :
- -handlerPC;
- }
-
-
- // Implementations for StackMapFrameVisitor.
-
- public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame)
- {
- // Remap 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)
- {
- // Remap the stack map frame offset.
- visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame);
-
- // Remap the verification type offset.
- sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
- }
-
-
- public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
- {
- // Remap the stack map frame offset.
- visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame);
-
- // Remap the verification type offsets.
- moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
- }
-
-
- public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
- {
- // Remap the stack map frame offset.
- visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame);
-
- // Remap 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)
- {
- // Remap 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)
- {
- // Remap the code offset.
- lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC);
- }
-
-
- // Implementations for LocalVariableInfoVisitor.
-
- public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
- {
- // Remap the code offset and length.
- // TODO: The local variable frame might not be strictly preserved.
- int startPC = newInstructionOffset(localVariableInfo.u2startPC);
- int endPC = newInstructionOffset(localVariableInfo.u2startPC +
- localVariableInfo.u2length);
-
- localVariableInfo.u2startPC = startPC;
- localVariableInfo.u2length = endPC - startPC;
- }
-
- // Implementations for LocalVariableTypeInfoVisitor.
-
- public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
- {
- // Remap the code offset and length.
- // TODO: The local variable frame might not be strictly preserved.
- int startPC = newInstructionOffset(localVariableTypeInfo.u2startPC);
- int endPC = newInstructionOffset(localVariableTypeInfo.u2startPC +
- localVariableTypeInfo.u2length);
-
- localVariableTypeInfo.u2startPC = startPC;
- localVariableTypeInfo.u2length = endPC - startPC;
- }
-
-
- // Small utility methods.
-
- /**
- * Make sure the code arrays have at least the given size.
- */
- private void ensureCodeLength(int newCodeLength)
- {
- if (code.length < newCodeLength)
- {
- // Add 20% to avoid extending the arrays too often.
- newCodeLength = newCodeLength * 6 / 5;
-
- code = ArrayUtil.extendArray(code, newCodeLength);
- oldInstructionOffsets = ArrayUtil.extendArray(oldInstructionOffsets, newCodeLength);
-
- instructionWriter.extend(newCodeLength);
- }
- }
-
-
- /**
- * Adjusts the given jump offsets for the instruction at the given offset.
- */
- private void updateJumpOffsets(int offset, int[] jumpOffsets)
- {
- for (int index = 0; index < jumpOffsets.length; index++)
- {
- jumpOffsets[index] = newBranchOffset(offset, jumpOffsets[index]);
- }
- }
-
-
- /**
- * Computes the new branch offset for the instruction at the given new offset
- * with the given old branch offset.
- */
- private int newBranchOffset(int newInstructionOffset, int oldBranchOffset)
- {
- if (newInstructionOffset < 0 ||
- newInstructionOffset > codeLength)
- {
- throw new IllegalArgumentException("Invalid instruction offset ["+newInstructionOffset +"] in code with length ["+codeLength+"]");
- }
-
- int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset];
-
- // For ordinary branch instructions, we can compute the offset
- // relative to the instruction itself.
- return newInstructionOffset(oldInstructionOffset + oldBranchOffset) -
- newInstructionOffset;
- }
-
-
- /**
- * Computes the new instruction offset for the instruction at the given old
- * offset.
- */
- private int newInstructionOffset(int oldInstructionOffset)
- {
- if (oldInstructionOffset < 0 ||
- oldInstructionOffset > codeFragmentLengths[level])
- {
- throw new IllegalArgumentException("Instruction offset ["+oldInstructionOffset +"] out of range in code fragment with length ["+codeFragmentLengths[level]+"] at level "+level);
- }
-
- int newInstructionOffset = instructionOffsetMap[level][oldInstructionOffset];
- if (newInstructionOffset == INVALID)
- {
- throw new IllegalArgumentException("Invalid instruction offset ["+oldInstructionOffset +"] in code fragment at level "+level);
- }
-
- return newInstructionOffset;
- }
-
-
- /**
- * Returns whether the given old exception handler can be remapped in the
- * current code fragment.
- */
- private boolean remappableExceptionHandler(int oldInstructionOffset)
- {
- // Can we index in the array?
- if (oldInstructionOffset > codeFragmentLengths[level])
- {
- return false;
- }
-
- // Do we have a valid new instruction offset, but not yet right after
- // the code? That offset is only labeled for mapping try blocks, not
- // for mapping handlers.
- int newInstructionOffset =
- instructionOffsetMap[level][oldInstructionOffset];
-
- return newInstructionOffset > INVALID &&
- newInstructionOffset < codeLength;
- }
-
-
- /**
- * 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;
- }
- }
-
- // Clear the unused array entries.
- Arrays.fill(exceptionInfos, newIndex, exceptionInfoCount, null);
-
- 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;
- }
- }
-
- // Clear the unused array entries.
- Arrays.fill(lineNumberInfos, newIndex, lineNumberInfoCount, null);
-
- return newIndex;
- }
-
-
- /**
- * Returns the given list of local variables, without the ones that have empty
- * code blocks or that exceed the actual number of local variables.
- */
- private int removeEmptyLocalVariables(LocalVariableInfo[] localVariableInfos,
- int localVariableInfoCount,
- int maxLocals)
- {
- // Overwrite all empty local variable entries.
- int newIndex = 0;
- for (int index = 0; index < localVariableInfoCount; index++)
- {
- LocalVariableInfo localVariableInfo = localVariableInfos[index];
- if (localVariableInfo.u2length > 0 &&
- localVariableInfo.u2index < maxLocals)
- {
- localVariableInfos[newIndex++] = localVariableInfo;
- }
- }
-
- // Clear the unused array entries.
- Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, null);
-
- return newIndex;
- }
-
-
- /**
- * Returns the given list of local variable types, without the ones that
- * have empty code blocks or that exceed the actual number of local variables.
- */
- private int removeEmptyLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
- int localVariableTypeInfoCount,
- int maxLocals)
- {
- // Overwrite all empty local variable type entries.
- int newIndex = 0;
- for (int index = 0; index < localVariableTypeInfoCount; index++)
- {
- LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
- if (localVariableTypeInfo.u2length > 0 &&
- localVariableTypeInfo.u2index < maxLocals)
- {
- localVariableTypeInfos[newIndex++] = localVariableTypeInfo;
- }
- }
-
- // Clear the unused array entries.
- Arrays.fill(localVariableTypeInfos, newIndex, localVariableTypeInfoCount, null);
-
- return newIndex;
- }
-
-
- private void println(String string1, String string2)
- {
- print(string1, string2);
-
- System.out.println();
- }
-
- private void print(String string1, String string2)
- {
- System.out.print(string1);
-
- for (int index = 0; index < level; index++)
- {
- System.out.print(" ");
- }
-
- System.out.print(string2);
- }
-
-
- public static void main(String[] args)
- {
- CodeAttributeComposer composer = new CodeAttributeComposer();
-
- composer.beginCodeFragment(4);
- composer.appendInstruction(0, new SimpleInstruction(InstructionConstants.OP_ICONST_0));
- composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ISTORE, 0));
- composer.appendInstruction(2, new BranchInstruction(InstructionConstants.OP_GOTO, 1));
-
- composer.beginCodeFragment(4);
- composer.appendInstruction(0, new VariableInstruction(InstructionConstants.OP_IINC, 0, 1));
- composer.appendInstruction(1, new VariableInstruction(InstructionConstants.OP_ILOAD, 0));
- composer.appendInstruction(2, new SimpleInstruction(InstructionConstants.OP_ICONST_5));
- composer.appendInstruction(3, new BranchInstruction(InstructionConstants.OP_IFICMPLT, -3));
- composer.endCodeFragment();
-
- composer.appendInstruction(3, new SimpleInstruction(InstructionConstants.OP_RETURN));
- composer.endCodeFragment();
- }
-}