diff options
Diffstat (limited to 'src/proguard/classfile/editor/CodeAttributeComposer.java')
-rw-r--r-- | src/proguard/classfile/editor/CodeAttributeComposer.java | 269 |
1 files changed, 171 insertions, 98 deletions
diff --git a/src/proguard/classfile/editor/CodeAttributeComposer.java b/src/proguard/classfile/editor/CodeAttributeComposer.java index e783203..c59b712 100644 --- a/src/proguard/classfile/editor/CodeAttributeComposer.java +++ b/src/proguard/classfile/editor/CodeAttributeComposer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -28,6 +28,9 @@ 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 @@ -49,7 +52,7 @@ implements AttributeVisitor, //* private static final boolean DEBUG = false; /*/ - public static boolean DEBUG = true; + public static boolean DEBUG = false; //*/ @@ -57,7 +60,8 @@ implements AttributeVisitor, private static final int INVALID = -1; - private boolean allowExternalExceptionHandlers; + private final boolean allowExternalExceptionHandlers; + private final boolean shrinkInstructions; private int maximumCodeLength; private int codeLength; @@ -77,26 +81,34 @@ implements AttributeVisitor, private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); private final VariableSizeUpdater variableSizeUpdater = new VariableSizeUpdater(); -// private final InstructionWriter instructionWriter = new InstructionWriter(); + private final InstructionWriter instructionWriter = new InstructionWriter(); /** * Creates a new CodeAttributeComposer that doesn't allow external exception - * handlers. + * handlers and that automatically shrinks instructions. */ public CodeAttributeComposer() { - this(false); + this(false, true); } /** - * Creates a new CodeAttributeComposer that optionally allows external - * exception handlers. + * Creates a new CodeAttributeComposer. + * @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 allowExternalExceptionHandlers) + public CodeAttributeComposer(boolean allowExternalExceptionHandlers, + boolean shrinkInstructions) { this.allowExternalExceptionHandlers = allowExternalExceptionHandlers; + this.shrinkInstructions = shrinkInstructions; } @@ -109,6 +121,8 @@ implements AttributeVisitor, codeLength = 0; exceptionTableLength = 0; level = -1; + + instructionWriter.reset(ClassConstants.TYPICAL_CODE_LENGTH); } @@ -116,7 +130,10 @@ implements AttributeVisitor, * 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. + * 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) { @@ -127,14 +144,9 @@ implements AttributeVisitor, throw new IllegalArgumentException("Maximum number of code fragment levels exceeded ["+level+"]"); } -// // TODO: Figure out some length. -// if (level == 0) -// { -// // Prepare for possible widening of instructions. -// instructionWriter.reset(2 * maximumCodeFragmentLength); -// } - // 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); @@ -159,6 +171,8 @@ implements AttributeVisitor, /** * 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. @@ -167,12 +181,17 @@ implements AttributeVisitor, public void appendInstruction(int oldInstructionOffset, Instruction instruction) { + if (shrinkInstructions) + { + instruction = instruction.shrink(); + } + if (DEBUG) { println("["+codeLength+"] <- ", instruction.toString(oldInstructionOffset)); } - // Make sure the code array is large enough. + // Make sure the code and offset arrays are large enough. int newCodeLength = codeLength + instruction.length(codeLength); ensureCodeLength(newCodeLength); @@ -180,17 +199,18 @@ implements AttributeVisitor, // Remember the old offset of the appended instruction. oldInstructionOffsets[codeLength] = oldInstructionOffset; - // Write the instruction. -// instruction.accept(null, -// null, -// new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), -// codeLength, -// instructionWriter); - instruction.write(code, codeLength); - // 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; } @@ -209,12 +229,70 @@ implements AttributeVisitor, println("["+codeLength+"] <- ", "[" + oldInstructionOffset + "] (label)"); } - // Fill out the new offset of the appended instruction. + // 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. */ @@ -244,16 +322,11 @@ implements AttributeVisitor, 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; + exceptionTable = + (ExceptionInfo[])ArrayUtil.add(exceptionTable, + exceptionTableLength++, + exceptionInfo); } @@ -281,13 +354,14 @@ implements AttributeVisitor, // Adapt the instruction for its new offset. instruction.accept(null, null, null, instructionOffset, this); - // Write the instruction back. -// instruction.accept(null, -// null, -// new CodeAttribute(0, 0, 0, 0, code, 0, null, 0, null), -// instructionOffset, -// instructionWriter); - instruction.write(code, instructionOffset); + // 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); // Don't remap this instruction again. oldInstructionOffsets[instructionOffset] = -1; @@ -313,9 +387,9 @@ implements AttributeVisitor, int handlerPC = -exceptionInfo.u2handlerPC; if (handlerPC > 0) { - if (remappableInstructionOffset(handlerPC)) + if (remappableExceptionHandler(handlerPC)) { - exceptionInfo.u2handlerPC = remapInstructionOffset(handlerPC); + exceptionInfo.u2handlerPC = newInstructionOffset(handlerPC); } else if (level == 0) { @@ -381,7 +455,7 @@ implements AttributeVisitor, // Remap the line number table and the local variable table. codeAttribute.attributesAccept(clazz, method, this); - // Remap the exception table. + // Remap the exception table (done before). //codeAttribute.exceptionsAccept(clazz, method, this); // Remove exceptions with empty code blocks (done before). @@ -389,8 +463,8 @@ implements AttributeVisitor, // removeEmptyExceptions(codeAttribute.exceptionTable, // codeAttribute.u2exceptionTableLength); -// // Make sure instructions are widened if necessary. -// instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); + // Make sure instructions are widened if necessary. + instructionWriter.visitCodeAttribute(clazz, method, codeAttribute); level--; } @@ -459,20 +533,20 @@ implements AttributeVisitor, public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) { // Adjust the branch offset. - branchInstruction.branchOffset = remapBranchOffset(offset, - branchInstruction.branchOffset); + branchInstruction.branchOffset = newBranchOffset(offset, + branchInstruction.branchOffset); } public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) { // Adjust the default jump offset. - switchInstruction.defaultOffset = remapBranchOffset(offset, - switchInstruction.defaultOffset); + switchInstruction.defaultOffset = newBranchOffset(offset, + switchInstruction.defaultOffset); // Adjust the jump offsets. - remapJumpOffsets(offset, - switchInstruction.jumpOffsets); + updateJumpOffsets(offset, + switchInstruction.jumpOffsets); } @@ -482,16 +556,16 @@ implements AttributeVisitor, { // 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 = remapInstructionOffset(exceptionInfo.u2startPC); - exceptionInfo.u2endPC = remapInstructionOffset(exceptionInfo.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 || - remappableInstructionOffset(handlerPC) ? - remapInstructionOffset(handlerPC) : + remappableExceptionHandler(handlerPC) ? + newInstructionOffset(handlerPC) : -handlerPC; } @@ -501,7 +575,7 @@ implements AttributeVisitor, public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) { // Remap the stack map frame offset. - int stackMapFrameOffset = remapInstructionOffset(offset); + int stackMapFrameOffset = newInstructionOffset(offset); int offsetDelta = stackMapFrameOffset; @@ -557,7 +631,7 @@ implements AttributeVisitor, public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) { // Remap the offset of the 'new' instruction. - uninitializedType.u2newInstructionOffset = remapInstructionOffset(uninitializedType.u2newInstructionOffset); + uninitializedType.u2newInstructionOffset = newInstructionOffset(uninitializedType.u2newInstructionOffset); } @@ -566,7 +640,7 @@ implements AttributeVisitor, public void visitLineNumberInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberInfo lineNumberInfo) { // Remap the code offset. - lineNumberInfo.u2startPC = remapInstructionOffset(lineNumberInfo.u2startPC); + lineNumberInfo.u2startPC = newInstructionOffset(lineNumberInfo.u2startPC); } @@ -576,8 +650,9 @@ implements AttributeVisitor, { // Remap the code offset and length. // TODO: The local variable frame might not be strictly preserved. - int startPC = remapInstructionOffset(localVariableInfo.u2startPC); - int endPC = remapInstructionOffset(localVariableInfo.u2startPC + localVariableInfo.u2length); + int startPC = newInstructionOffset(localVariableInfo.u2startPC); + int endPC = newInstructionOffset(localVariableInfo.u2startPC + + localVariableInfo.u2length); localVariableInfo.u2startPC = startPC; localVariableInfo.u2length = endPC - startPC; @@ -589,8 +664,9 @@ implements AttributeVisitor, { // Remap the code offset and length. // TODO: The local variable frame might not be strictly preserved. - int startPC = remapInstructionOffset(localVariableTypeInfo.u2startPC); - int endPC = remapInstructionOffset(localVariableTypeInfo.u2startPC + localVariableTypeInfo.u2length); + int startPC = newInstructionOffset(localVariableTypeInfo.u2startPC); + int endPC = newInstructionOffset(localVariableTypeInfo.u2startPC + + localVariableTypeInfo.u2length); localVariableTypeInfo.u2startPC = startPC; localVariableTypeInfo.u2length = endPC - startPC; @@ -609,13 +685,10 @@ implements AttributeVisitor, // Add 20% to avoid extending the arrays too often. newCodeLength = newCodeLength * 6 / 5; - byte[] newCode = new byte[newCodeLength]; - System.arraycopy(code, 0, newCode, 0, codeLength); - code = newCode; + code = ArrayUtil.extendArray(code, newCodeLength); + oldInstructionOffsets = ArrayUtil.extendArray(oldInstructionOffsets, newCodeLength); - int[] newOldInstructionOffsets = new int[newCodeLength]; - System.arraycopy(oldInstructionOffsets, 0, newOldInstructionOffsets, 0, codeLength); - oldInstructionOffsets = newOldInstructionOffsets; + instructionWriter.extend(newCodeLength); } } @@ -623,11 +696,11 @@ implements AttributeVisitor, /** * Adjusts the given jump offsets for the instruction at the given offset. */ - private void remapJumpOffsets(int offset, int[] jumpOffsets) + private void updateJumpOffsets(int offset, int[] jumpOffsets) { for (int index = 0; index < jumpOffsets.length; index++) { - jumpOffsets[index] = remapBranchOffset(offset, jumpOffsets[index]); + jumpOffsets[index] = newBranchOffset(offset, jumpOffsets[index]); } } @@ -636,7 +709,7 @@ implements AttributeVisitor, * Computes the new branch offset for the instruction at the given new offset * with the given old branch offset. */ - private int remapBranchOffset(int newInstructionOffset, int branchOffset) + private int newBranchOffset(int newInstructionOffset, int oldBranchOffset) { if (newInstructionOffset < 0 || newInstructionOffset > codeLength) @@ -646,8 +719,8 @@ implements AttributeVisitor, int oldInstructionOffset = oldInstructionOffsets[newInstructionOffset]; - return remapInstructionOffset(oldInstructionOffset + branchOffset) - - remapInstructionOffset(oldInstructionOffset); + return newInstructionOffset(oldInstructionOffset + oldBranchOffset) - + newInstructionOffset(oldInstructionOffset); } @@ -655,7 +728,7 @@ implements AttributeVisitor, * Computes the new instruction offset for the instruction at the given old * offset. */ - private int remapInstructionOffset(int oldInstructionOffset) + private int newInstructionOffset(int oldInstructionOffset) { if (oldInstructionOffset < 0 || oldInstructionOffset > codeFragmentLengths[level]) @@ -674,13 +747,25 @@ implements AttributeVisitor, /** - * Returns whether the given old instruction offset can be remapped at the + * Returns whether the given old exception handler can be remapped in the + * current code fragment. */ - private boolean remappableInstructionOffset(int oldInstructionOffset) + private boolean remappableExceptionHandler(int oldInstructionOffset) { - return - oldInstructionOffset <= codeFragmentLengths[level] && - instructionOffsetMap[level][oldInstructionOffset] > INVALID; + // 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; } @@ -703,10 +788,7 @@ implements AttributeVisitor, } // Clear the unused array entries. - for (int index = newIndex; index < exceptionInfoCount; index++) - { - exceptionInfos[index] = null; - } + Arrays.fill(exceptionInfos, newIndex, exceptionInfoCount, null); return newIndex; } @@ -734,10 +816,7 @@ implements AttributeVisitor, } // Clear the unused array entries. - for (int index = newIndex; index < lineNumberInfoCount; index++) - { - lineNumberInfos[index] = null; - } + Arrays.fill(lineNumberInfos, newIndex, lineNumberInfoCount, null); return newIndex; } @@ -764,10 +843,7 @@ implements AttributeVisitor, } // Clear the unused array entries. - for (int index = newIndex; index < localVariableInfoCount; index++) - { - localVariableInfos[index] = null; - } + Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, null); return newIndex; } @@ -794,10 +870,7 @@ implements AttributeVisitor, } // Clear the unused array entries. - for (int index = newIndex; index < localVariableTypeInfoCount; index++) - { - localVariableTypeInfos[index] = null; - } + Arrays.fill(localVariableTypeInfos, newIndex, localVariableTypeInfoCount, null); return newIndex; } |