📄 instructionlist.java
字号:
public boolean contains(Instruction i) {
return findInstruction1(i) != null;
}
public void setPositions() {
setPositions(false);
}
/**
* Give all instructions their position number (offset in byte stream), i.e.,
* make the list ready to be dumped.
*
* @param check Perform sanity checks, e.g. if all targeted instructions really belong
* to this list
*/
public void setPositions(boolean check) {
int max_additional_bytes = 0, additional_bytes = 0;
int index = 0, count = 0;
int[] pos = new int[length];
/* Pass 0: Sanity checks
*/
if(check) {
for(InstructionHandle ih=start; ih != null; ih = ih.next) {
Instruction i = ih.instruction;
if(i instanceof BranchInstruction) { // target instruction within list?
Instruction inst = ((BranchInstruction)i).getTarget().instruction;
if(!contains(inst))
throw new ClassGenException("Branch target of " +
Constants.OPCODE_NAMES[i.opcode] + ":" +
inst + " not in instruction list");
if(i instanceof Select) {
InstructionHandle[] targets = ((Select)i).getTargets();
for(int j=0; j < targets.length; j++) {
inst = targets[j].instruction;
if(!contains(inst))
throw new ClassGenException("Branch target of " +
Constants.OPCODE_NAMES[i.opcode] + ":" +
inst + " not in instruction list");
}
}
if(!(ih instanceof BranchHandle))
throw new ClassGenException("Branch instruction " +
Constants.OPCODE_NAMES[i.opcode] + ":" +
inst + " not contained in BranchHandle.");
}
}
}
/* Pass 1: Set position numbers and sum up the maximum number of bytes an
* instruction may be shifted.
*/
for(InstructionHandle ih=start; ih != null; ih = ih.next) {
Instruction i = ih.instruction;
ih.setPosition(index);
pos[count++] = index;
/* Get an estimate about how many additional bytes may be added, because
* BranchInstructions may have variable length depending on the target
* offset (short vs. int) or alignment issues (TABLESWITCH and
* LOOKUPSWITCH).
*/
switch(i.getOpcode()) {
case Constants.JSR: case Constants.GOTO:
max_additional_bytes += 2;
break;
case Constants.TABLESWITCH: case Constants.LOOKUPSWITCH:
max_additional_bytes += 3;
break;
}
index += i.getLength();
}
/* Pass 2: Expand the variable-length (Branch)Instructions depending on
* the target offset (short or int) and ensure that branch targets are
* within this list.
*/
for(InstructionHandle ih=start; ih != null; ih = ih.next)
additional_bytes += ih.updatePosition(additional_bytes, max_additional_bytes);
/* Pass 3: Update position numbers (which may have changed due to the
* preceding expansions), like pass 1.
*/
index=count=0;
for(InstructionHandle ih=start; ih != null; ih = ih.next) {
Instruction i = ih.instruction;
ih.setPosition(index);
pos[count++] = index;
index += i.getLength();
}
byte_positions = new int[count]; // Trim to proper size
System.arraycopy(pos, 0, byte_positions, 0, count);
}
/**
* When everything is finished, use this method to convert the instruction
* list into an array of bytes.
*
* @return the byte code ready to be dumped
*/
public byte[] getByteCode() {
// Update position indices of instructions
setPositions();
ByteArrayOutputStream b = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(b);
try {
for(InstructionHandle ih=start; ih != null; ih = ih.next) {
Instruction i = ih.instruction;
i.dump(out); // Traverse list
}
} catch(IOException e) {
System.err.println(e);
return null;
}
return b.toByteArray();
}
/**
* @return an array of instructions without target information for branch instructions.
*/
public Instruction[] getInstructions() {
ByteSequence bytes = new ByteSequence(getByteCode());
ArrayList<Instruction> instructions = new ArrayList<Instruction>();
try {
while(bytes.available() > 0) {
instructions.add(Instruction.readInstruction(bytes));
}
} catch(IOException e) { throw new ClassGenException(e.toString()); }
Instruction[] result = new Instruction[instructions.size()];
instructions.toArray(result);
return result;
}
public String toString() {
return toString(true);
}
/**
* @param verbose toggle output format
* @return String containing all instructions in this list.
*/
public String toString(boolean verbose) {
StringBuffer buf = new StringBuffer();
for(InstructionHandle ih=start; ih != null; ih = ih.next) {
buf.append(ih.toString(verbose) + "\n");
}
return buf.toString();
}
/**
* @return Enumeration that lists all instructions (handles)
*/
public Iterator iterator() {
return new Iterator() {
private InstructionHandle ih = start;
public Object next() {
InstructionHandle i = ih;
ih = ih.next;
return i;
}
public void remove() {
throw new UnsupportedOperationException();
}
public boolean hasNext() { return ih != null; }
};
}
/**
* @return array containing all instructions (handles)
*/
public InstructionHandle[] getInstructionHandles() {
InstructionHandle[] ihs = new InstructionHandle[length];
InstructionHandle ih = start;
for(int i=0; i < length; i++) {
ihs[i] = ih;
ih = ih.next;
}
return ihs;
}
/**
* Get positions (offsets) of all instructions in the list. This relies on that
* the list has been freshly created from an byte code array, or that setPositions()
* has been called. Otherwise this may be inaccurate.
*
* @return array containing all instruction's offset in byte code
*/
public int[] getInstructionPositions() { return byte_positions; }
/**
* @return complete, i.e., deep copy of this list
*/
public InstructionList copy() {
HashMap<InstructionHandle, InstructionHandle> map = new HashMap<InstructionHandle, InstructionHandle>();
InstructionList il = new InstructionList();
/* Pass 1: Make copies of all instructions, append them to the new list
* and associate old instruction references with the new ones, i.e.,
* a 1:1 mapping.
*/
for(InstructionHandle ih=start; ih != null; ih = ih.next) {
Instruction i = ih.instruction;
Instruction c = i.copy(); // Use clone for shallow copy
if(c instanceof BranchInstruction)
map.put(ih, il.append((BranchInstruction)c));
else
map.put(ih, il.append(c));
}
/* Pass 2: Update branch targets.
*/
InstructionHandle ih=start;
InstructionHandle ch=il.start;
while(ih != null) {
Instruction i = ih.instruction;
Instruction c = ch.instruction;
if(i instanceof BranchInstruction) {
BranchInstruction bi = (BranchInstruction)i;
BranchInstruction bc = (BranchInstruction)c;
InstructionHandle itarget = bi.getTarget(); // old target
// New target is in hash map
bc.setTarget((InstructionHandle)map.get(itarget));
if(bi instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
InstructionHandle[] itargets = ((Select)bi).getTargets();
InstructionHandle[] ctargets = ((Select)bc).getTargets();
for(int j=0; j < itargets.length; j++) { // Update all targets
ctargets[j] = (InstructionHandle)map.get(itargets[j]);
}
}
}
ih = ih.next;
ch = ch.next;
}
return il;
}
/** Replace all references to the old constant pool with references to the new
* constant pool
*/
public void replaceConstantPool(ConstantPoolGen old_cp, ConstantPoolGen new_cp) {
for(InstructionHandle ih=start; ih != null; ih = ih.next) {
Instruction i = ih.instruction;
if(i instanceof CPInstruction) {
CPInstruction ci = (CPInstruction)i;
Constant c = old_cp.getConstant(ci.getIndex());
ci.setIndex(new_cp.addConstant(c, old_cp));
}
}
}
private void clear() {
start = end = null;
length = 0;
}
/**
* Delete contents of list. Provides besser memory utilization,
* because the system then may reuse the instruction handles. This
* method is typically called right after
* <href="MethodGen.html#getMethod()">MethodGen.getMethod()</a>.
*/
public void dispose() {
// Traverse in reverse order, because ih.next is overwritten
for(InstructionHandle ih=end; ih != null; ih = ih.prev)
/* Causes BranchInstructions to release target and targeters, because it
* calls dispose() on the contained instruction.
*/
ih.dispose();
clear();
}
/**
* @return start of list
*/
public InstructionHandle getStart() { return start; }
/**
* @return end of list
*/
public InstructionHandle getEnd() { return end; }
/**
* @return length of list (Number of instructions, not bytes)
*/
public int getLength() { return length; }
/**
* @return length of list (Number of instructions, not bytes)
*/
public int size() { return length; }
/**
* Redirect all references from old_target to new_target, i.e., update targets
* of branch instructions.
*
* @param old_target the old target instruction handle
* @param new_target the new target instruction handle
*/
public void redirectBranches(InstructionHandle old_target,
InstructionHandle new_target) {
for(InstructionHandle ih = start; ih != null; ih = ih.next) {
Instruction i = ih.getInstruction();
if(i instanceof BranchInstruction) {
BranchInstruction b = (BranchInstruction)i;
InstructionHandle target = b.getTarget();
if(target == old_target)
b.setTarget(new_target);
if(b instanceof Select) { // Either LOOKUPSWITCH or TABLESWITCH
InstructionHandle[] targets = ((Select)b).getTargets();
for(int j=0; j < targets.length; j++) // Update targets
if(targets[j] == old_target)
((Select)b).setTarget(j, new_target);
}
}
}
}
/**
* Redirect all references of local variables from old_target to new_target.
*
* @param lg array of local variables
* @param old_target the old target instruction handle
* @param new_target the new target instruction handle
* @see MethodGen
*/
public void redirectLocalVariables(LocalVariableGen[] lg,
InstructionHandle old_target,
InstructionHandle new_target) {
for(int i=0; i < lg.length; i++) {
InstructionHandle start = lg[i].getStart();
InstructionHandle end = lg[i].getEnd();
if(start == old_target)
lg[i].setStart(new_target);
if(end == old_target)
lg[i].setEnd(new_target);
}
}
/**
* Redirect all references of exception handlers from old_target to new_target.
*
* @param exceptions array of exception handlers
* @param old_target the old target instruction handle
* @param new_target the new target instruction handle
* @see MethodGen
*/
public void redirectExceptionHandlers(CodeExceptionGen[] exceptions,
InstructionHandle old_target,
InstructionHandle new_target) {
for(int i=0; i < exceptions.length; i++) {
if(exceptions[i].getStartPC() == old_target)
exceptions[i].setStartPC(new_target);
if(exceptions[i].getEndPC() == old_target)
exceptions[i].setEndPC(new_target);
if(exceptions[i].getHandlerPC() == old_target)
exceptions[i].setHandlerPC(new_target);
}
}
private ArrayList<InstructionListObserver> observers;
/** Add observer for this object.
*/
public void addObserver(InstructionListObserver o) {
if(observers == null)
observers = new ArrayList<InstructionListObserver>();
observers.add(o);
}
/** Remove observer for this object.
*/
public void removeObserver(InstructionListObserver o) {
if(observers != null)
observers.remove(o);
}
/** Call notify() method on all observers. This method is not called
* automatically whenever the state has changed, but has to be
* called by the user after he has finished editing the object.
*/
public void update() {
if(observers != null)
for(Iterator e = observers.iterator(); e.hasNext(); )
((InstructionListObserver)e.next()).notify(this);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -