📄 codeinsertion.java
字号:
/*
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.
*/
package org.gjt.jclasslib.bytecode;
import org.gjt.jclasslib.structures.InvalidByteCodeException;
import org.gjt.jclasslib.structures.attributes.*;
import java.util.*;
/**
Contains all information necessary to insert code into a
method. Allows for pre and post insertions. The core method
to perform code insertions is the static <tt>apply</tt> method.
@author <a href="mailto:jclasslib@ej-technologies.com">Ingo Kegel</a>
@version $Revision: 1.2 $ $Date: 2006/09/04 15:43:18 $
*/
public class CodeInsertion {
/**
Merge two code insertions into one.
@param position the position of the resulting <tt>CodeInsertion</tt>
@param shiftTarget should offsets of branch instructions pointing to
the position of the resulting code insertion
be shifted or point to the beginning of the
inserted code.
@param inner the inner <tt>CodeInsertion</tt>
@param outer the outer <tt>CodeInsertion</tt>
@return the resulting <tt>CodeInsertion</tt>
*/
public static CodeInsertion merge(int position,
boolean shiftTarget,
CodeInsertion inner,
CodeInsertion outer)
{
if (outer == null) {
return inner;
}
if (inner == null) {
return outer;
}
AbstractInstruction[] preInstructions = mergeInstructions(outer.preInstructions, inner.preInstructions);
AbstractInstruction[] postInstructions = mergeInstructions(inner.postInstructions, outer.postInstructions);
CodeInsertion codeInsertion =
new CodeInsertion(position,
preInstructions,
postInstructions,
shiftTarget);
return codeInsertion;
}
/**
Merge two arrays of instructions into one.
@param firstInstructions the head array of type <tt>AbstractInstruction</tt>
@param lastInstructions the tail array of type <tt>AbstractInstruction</tt>
@return the merged array of type <tt>AbstractInstruction[]</tt>
*/
public static AbstractInstruction[] mergeInstructions(AbstractInstruction[] firstInstructions,
AbstractInstruction[] lastInstructions)
{
if (firstInstructions== null) {
return lastInstructions;
}
if (lastInstructions == null) {
return firstInstructions;
}
AbstractInstruction[] mergedInstructions = new AbstractInstruction[firstInstructions.length + lastInstructions.length];
System.arraycopy(firstInstructions, 0, mergedInstructions, 0, firstInstructions.length);
System.arraycopy(lastInstructions, 0, mergedInstructions, firstInstructions.length, lastInstructions.length);
return mergedInstructions;
}
/**
Apply a list of <tt>CodeInsertion</tt>s to a list of instructions
such as the one supplied by a <tt>ByteCodeReader</tt>. Offsets
of branch instructions will be adapted to point to the original
instructions. Exception and line number tables in the associated
<tt>CodeAttribute</tt>will also be updated.
@param instructions the list of instructions which is to be treated
with the <tt>CodeInsertion</tt>s. This list
and the resulting instructions will not be usable
after the method is finished. If you need to
reuse the original instructions, you have to pass
a deep-cloned list into this method.
@param codeInsertions the list of codeInsertions which is to be applied to
the list of instructions.
@param codeAttribute the <tt>CodeAttribute</tt> pertaining to the supplied
list of instructions.
@return the resulting list of instructions
@throws InvalidByteCodeException
*/
public static List apply(List instructions,
List codeInsertions,
CodeAttribute codeAttribute)
throws InvalidByteCodeException
{
int instructionCount = instructions.size();
int[] transformedIndices = new int[instructionCount];
for (int i = 0; i < instructionCount; i++) {
transformedIndices[i] = i;
}
List newInstructions = insertCode(instructions,
codeInsertions,
transformedIndices);
int[] oldOffsets = new int[instructionCount];
for (int i = 0; i < instructionCount; i++) {
oldOffsets[i] = ((AbstractInstruction)instructions.get(i)).getOffset();
}
int[] newOffsets = new int[newInstructions.size()];
calculateOffsets(newInstructions, newOffsets);
adjustOffsets(instructions,
newInstructions,
oldOffsets,
newOffsets,
transformedIndices);
if (codeAttribute != null) {
adjustExceptionTable(oldOffsets,
newOffsets,
transformedIndices,
codeAttribute);
adjustLineNumberTable(oldOffsets,
newOffsets,
transformedIndices,
codeAttribute);
}
applyOffsets(newInstructions, newOffsets);
return newInstructions;
}
private static List insertCode(List instructions,
List codeInsertions,
int[] transformedIndices)
{
int instructionCount = instructions.size();
int insertionCount = codeInsertions.size();
int newSize = calculateNewSize(instructions, codeInsertions);
List<Object> newInstructions = new ArrayList<Object>(newSize);
int currentInsertionIndex = 0;
CodeInsertion currentInsertion = (CodeInsertion)codeInsertions.get(0);
for (int i = 0; i < instructionCount; i++) {
if (currentInsertion.getPosition() < i && currentInsertionIndex < insertionCount - 1) {
++currentInsertionIndex;
currentInsertion = (CodeInsertion)codeInsertions.get(currentInsertionIndex);
}
int addedBefore = 0;
int addedAfter = 0;
if (currentInsertion.getPosition() == i) {
addedBefore = addInstructions(newInstructions, currentInsertion.getPreInstructions());
}
newInstructions.add(instructions.get(i));
if (currentInsertion.getPosition() == i) {
addedAfter = addInstructions(newInstructions, currentInsertion.getPostInstructions());
}
if (addedBefore > 0 || addedAfter > 0) {
shiftIndices(i, addedBefore, addedAfter, transformedIndices, currentInsertion.isShiftTarget());
}
}
return newInstructions;
}
private static int calculateNewSize(List instructions, List codeInsertions) {
int insertionCount = codeInsertions.size();
int newSize = instructions.size();
for (int i = 0; i < insertionCount; i++) {
CodeInsertion insertion = (CodeInsertion)codeInsertions.get(i);
AbstractInstruction[] preInstructions = insertion.getPreInstructions();
if (preInstructions != null) {
newSize += preInstructions.length;
}
AbstractInstruction[] postInstructions = insertion.getPostInstructions();
if (postInstructions != null) {
newSize += postInstructions.length;
}
}
return newSize;
}
private static void shiftIndices(int currentIndex,
int addedBefore,
int addedAfter,
int[] transformedIndices,
boolean shiftTarget)
{
if (!shiftTarget) {
transformedIndices[currentIndex] += addedBefore;
}
for (int i = currentIndex + 1; i < transformedIndices.length; i++) {
transformedIndices[i] += addedBefore + addedAfter;
}
}
private static int addInstructions(List<Object> newInstructions,
AbstractInstruction[] insertedInstructions)
{
if (insertedInstructions != null) {
for (int i = 0; i < insertedInstructions.length; i++) {
newInstructions.add(insertedInstructions[i]);
}
return insertedInstructions.length;
} else {
return 0;
}
}
private static void calculateOffsets(List instructions,
int[] offsets)
{
int instructionCount = instructions.size();
int currentOffset = 0;
for (int i = 0; i < instructionCount; i++) {
offsets[i] = currentOffset;
AbstractInstruction instr = (AbstractInstruction)instructions.get(i);
int currentSize;
if (instr instanceof PaddedInstruction) {
currentSize = ((PaddedInstruction)instr).getPaddedSize(currentOffset);
} else {
currentSize = instr.getSize();
}
currentOffset += currentSize;
}
}
private static void applyOffsets(List instructions, int[] offsets) {
int instructionCount = instructions.size();
for (int i = 0; i < instructionCount; i++) {
AbstractInstruction instr = (AbstractInstruction)instructions.get(i);
instr.setOffset(offsets[i]);
}
}
private static void adjustOffsets(List instructions,
List newInstructions,
int[] oldOffsets,
int[] newOffsets,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -