Mirror of the BLOAT repository
https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2408 lines
55 KiB
2408 lines
55 KiB
/**
|
|
* All files in the distribution of BLOAT (Bytecode Level Optimization and
|
|
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
|
|
* Research Foundation of Purdue University. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package EDU.purdue.cs.bloat.editor;
|
|
|
|
import java.util.*;
|
|
|
|
import EDU.purdue.cs.bloat.reflect.*;
|
|
import EDU.purdue.cs.bloat.util.*;
|
|
|
|
/**
|
|
* CodeArray converts an array of Instructions into an array of bytes suitable
|
|
* for saving to a <tt>MethodInfo</tt> with <tt>setCode</tt>.
|
|
*
|
|
* <p>
|
|
*
|
|
* The byte array is returned by calling the <tt>array</tt> method.
|
|
*
|
|
* <p>
|
|
*
|
|
* This code assumes no branch will be longer than 65536 bytes.
|
|
*
|
|
* @see Instruction
|
|
* @see MethodInfo
|
|
* @see MethodInfo#setCode
|
|
*
|
|
* @author Nate Nystrom (<a
|
|
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
|
|
*/
|
|
public class CodeArray implements InstructionVisitor, Opcode {
|
|
public static boolean DEBUG = Boolean.getBoolean("CodeArray.DEBUG");
|
|
|
|
private ByteCell codeTail; // Linked list of ByteCells representing code
|
|
|
|
private int codeLength; // Number of bytes in method
|
|
|
|
private Map branches;
|
|
|
|
private Map longBranches;
|
|
|
|
private Map branchInsts;
|
|
|
|
private Map labels; // Labels mapped to their offsets
|
|
|
|
private int lastInst; // Offset of last (most recent) instrucion
|
|
|
|
private int maxStack; // Max stack height
|
|
|
|
private int stackHeight; // Current stack height
|
|
|
|
private int maxLocals; // Max number of local variables
|
|
|
|
private ConstantPool constants;
|
|
|
|
private MethodEditor method;
|
|
|
|
private boolean longBranch; // Do we use long (wide) jumps?
|
|
|
|
private List insts;
|
|
|
|
/**
|
|
* Create the byte array for a method.
|
|
*
|
|
* @param method
|
|
* The method being edited.
|
|
* @param constants
|
|
* The constant pool of the class.
|
|
* @param insts
|
|
* A List of Instructions and Labels to convert to a byte array.
|
|
* @see MethodEditor
|
|
* @see ConstantPool
|
|
* @see Instruction
|
|
* @see Label
|
|
*/
|
|
public CodeArray(final MethodEditor method, final ConstantPool constants,
|
|
final List insts) {
|
|
this.constants = constants;
|
|
this.method = method;
|
|
this.insts = insts;
|
|
this.maxStack = 0;
|
|
this.maxLocals = 0;
|
|
|
|
longBranch = false;
|
|
buildCode();
|
|
}
|
|
|
|
/**
|
|
* Examine the method's Labels and Instructions. Keep track of such things
|
|
* as the height of the stack at each instruction and to where subroutines
|
|
* return. The ultimate goal is to compute the max stack height of this
|
|
* method. This computation is complicated by subroutines that may be
|
|
* invoked at a variety of stack heights.
|
|
*/
|
|
private void buildCode() {
|
|
codeTail = null;
|
|
codeLength = 0;
|
|
branches = new HashMap();
|
|
longBranches = new HashMap();
|
|
branchInsts = new HashMap();
|
|
labels = new HashMap();
|
|
|
|
// We need at least enought locals to store the parameters
|
|
maxLocals = method.type().stackHeight();
|
|
|
|
if (!method.isStatic()) {
|
|
// One more for the this pointer
|
|
maxLocals++;
|
|
}
|
|
|
|
stackHeight = 0;
|
|
|
|
final Map labelPos = new HashMap(); // Maps Labels to their code offsets
|
|
final int[] heights = new int[insts.size()]; // Stack height at each
|
|
// inst
|
|
|
|
// Maps Labels that begin jsrs to their return targets. Maps ret
|
|
// instructions to the subroutine from which they return.
|
|
final Map retTargets = new HashMap();
|
|
final Map retInsts = new HashMap();
|
|
|
|
// Print the code we're dealing with
|
|
if (CodeArray.DEBUG) {
|
|
System.out.println("Building code for "
|
|
+ method.declaringClass().name() + "." + method.name());
|
|
final Iterator iter = insts.iterator();
|
|
while (iter.hasNext()) {
|
|
final Object o = iter.next();
|
|
System.out.println(" " + o);
|
|
}
|
|
}
|
|
|
|
// Build the bytecode array, assuming each basic block begins with
|
|
// stack height 0. We'll fix up the heights later.
|
|
final Iterator iter = insts.iterator();
|
|
int i = 0; // Which instruction are we at?
|
|
while (iter.hasNext()) {
|
|
final Object ce = iter.next();
|
|
|
|
if (ce instanceof Label) {
|
|
final Label label = (Label) ce;
|
|
|
|
// A Label starts a new basic block. Reset the stack height.
|
|
|
|
stackHeight = 0;
|
|
labelPos.put(label, new Integer(i));
|
|
|
|
addLabel(label);
|
|
heights[i++] = stackHeight;
|
|
|
|
// If this label starts a subroutine (i.e. is the target of
|
|
// jsr instruction), then make not of it.
|
|
if (retTargets.containsKey(label)) {
|
|
}
|
|
|
|
} else if (ce instanceof Instruction) {
|
|
final Instruction inst = (Instruction) ce;
|
|
|
|
// Visit this instruction to compute the current stack height
|
|
inst.visit(this);
|
|
|
|
if (inst.isJsr()) {
|
|
// Make sure that the jsr is not the last instruction in the
|
|
// method. If it was, where would we return to? Make note
|
|
// of the return target (the Label following the jsr).
|
|
|
|
heights[i++] = stackHeight;
|
|
|
|
Assert.isTrue(iter.hasNext(), inst
|
|
+ " found at end of method");
|
|
|
|
final Object x = iter.next();
|
|
|
|
Assert.isTrue(x instanceof Label, inst
|
|
+ " not followed by label");
|
|
|
|
final Label sub = (Label) inst.operand();
|
|
final Label target = (Label) x;
|
|
|
|
// Maintain a mapping between a subroutine (the Label that
|
|
// begins it) and all return targets.
|
|
Set targets = (Set) retTargets.get(sub);
|
|
if (targets == null) {
|
|
targets = new HashSet();
|
|
retTargets.put(sub, targets);
|
|
}
|
|
targets.add(target);
|
|
|
|
stackHeight = 0;
|
|
labelPos.put(target, new Integer(i));
|
|
|
|
addLabel(target);
|
|
heights[i++] = stackHeight;
|
|
|
|
} else {
|
|
heights[i++] = stackHeight;
|
|
}
|
|
|
|
} else {
|
|
// Something bad in instruction list
|
|
throw new IllegalArgumentException();
|
|
}
|
|
}
|
|
|
|
// Sorry, but we have to make another forward pass over some of
|
|
// the code to determine the subroutine from which a given ret
|
|
// instruction returns.
|
|
final Iterator subLabels = retTargets.keySet().iterator();
|
|
while (subLabels.hasNext()) {
|
|
final Label subLabel = (Label) subLabels.next();
|
|
final int pos = insts.indexOf(subLabel);
|
|
Assert.isTrue(pos != -1, "Label " + subLabel + " not found");
|
|
boolean foundRet = false;
|
|
final ListIterator liter = insts.listIterator(pos);
|
|
while (liter.hasNext()) {
|
|
final Object o = liter.next();
|
|
if (o instanceof Instruction) {
|
|
final Instruction inst = (Instruction) o;
|
|
if (inst.isRet()) {
|
|
retInsts.put(inst, subLabel);
|
|
foundRet = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Assert.isTrue(foundRet, "No ret for subroutine " + subLabel);
|
|
}
|
|
|
|
if (CodeArray.DEBUG) {
|
|
// Print subroutine to return target mapping
|
|
System.out.println("Subroutines and return targets:");
|
|
final Iterator subs = retTargets.keySet().iterator();
|
|
while (subs.hasNext()) {
|
|
final Label sub = (Label) subs.next();
|
|
System.out.print(" " + sub + ": ");
|
|
final Set s = (Set) retTargets.get(sub);
|
|
Assert.isTrue(s != null, "No return targets for " + sub);
|
|
final Iterator rets = s.iterator();
|
|
while (rets.hasNext()) {
|
|
final Label ret = (Label) rets.next();
|
|
System.out.print(ret.toString());
|
|
if (rets.hasNext()) {
|
|
System.out.print(", ");
|
|
}
|
|
}
|
|
System.out.println("");
|
|
}
|
|
}
|
|
|
|
// Fix up the stack heights by propagating the heights at each catch
|
|
// and each branch to their targets. Visit the blocks
|
|
// depth-first. Remember that the classfile requires the maximum
|
|
// stack height. I would assume that is why we do all of this
|
|
// stack height calculation stuff.
|
|
|
|
final Set visited = new HashSet(); // Labels that we've seen
|
|
final Stack stack = new Stack(); // Stack of HeightRecords
|
|
Label label;
|
|
|
|
// Start with the first Label
|
|
if (insts.size() > 0) {
|
|
Assert.isTrue((insts.get(0) instanceof Label),
|
|
"A method must begin with a Label, not " + insts.get(0));
|
|
label = (Label) insts.get(0);
|
|
visited.add(label);
|
|
stack.push(new HeightRecord(label, 0));
|
|
}
|
|
|
|
// Also examine each exception handler. Recall that the exception
|
|
// object is implicitly pushed on the stack. So, the HeightRecord
|
|
// initially has height 1.
|
|
final Iterator e = method.tryCatches().iterator();
|
|
while (e.hasNext()) {
|
|
final TryCatch tc = (TryCatch) e.next();
|
|
visited.add(tc.handler());
|
|
stack.push(new HeightRecord(tc.handler(), 1));
|
|
}
|
|
|
|
// Examine the HeightRecords on the stack. Make sure that the
|
|
// stack height has not exceeded 256. If the height at a given
|
|
// label has changed since we last visited it, then propagate this
|
|
// change to labels following the block begun by the label in
|
|
// question.
|
|
while (!stack.isEmpty()) {
|
|
final HeightRecord h = (HeightRecord) stack.pop();
|
|
|
|
Assert.isTrue(h.height < 256, "Stack height of " + h.height
|
|
+ " reached. " + h.label + " (" + labelPos.get(h.label)
|
|
+ ")");
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(h.label + " has height " + h.height);
|
|
}
|
|
|
|
Integer labelIndex = (Integer) labelPos.get(h.label);
|
|
Assert.isTrue(labelIndex != null, "Index of " + h.label
|
|
+ " not found");
|
|
|
|
final int start = labelIndex.intValue();
|
|
int diff = h.height - heights[start];
|
|
|
|
// Propagate the change in height to the next branch.
|
|
// Then push the branch targets.
|
|
if (ClassEditor.DEBUG) {
|
|
/*
|
|
* System.out.println(" " + h.label + ": change " +
|
|
* heights[start] + " to " + h.height);
|
|
*/
|
|
}
|
|
|
|
heights[start] = h.height;
|
|
|
|
final ListIterator blockIter = insts.listIterator(start + 1);
|
|
i = start;
|
|
|
|
// Examine the instructions following the label
|
|
while (blockIter.hasNext()) {
|
|
final Object ce = blockIter.next();
|
|
|
|
i++;
|
|
|
|
if (ce instanceof Instruction) {
|
|
final Instruction inst = (Instruction) ce;
|
|
|
|
if (inst.isReturn() || inst.isThrow()) {
|
|
// The method terminates. The stack is popped empty.
|
|
heights[i] = 0;
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + heights[i] + ") " + inst);
|
|
}
|
|
|
|
// Consider next HeightRecord on stack.
|
|
break;
|
|
|
|
} else if (inst.isConditionalJump()) {
|
|
// If the stack height at this Label has changed since
|
|
// we
|
|
// last saw it or if we have not processed the target of
|
|
// the jump, add a new HeightRecord for the target
|
|
// Label.
|
|
|
|
heights[i] += diff;
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + heights[i] + ") " + inst);
|
|
}
|
|
|
|
label = (Label) inst.operand();
|
|
|
|
if ((diff > 0) || !visited.contains(label)) {
|
|
visited.add(label);
|
|
stack.push(new HeightRecord(label, heights[i]));
|
|
}
|
|
|
|
// Fall through. That is, process the instruction after
|
|
// the conditional jump. Remember that the code is in
|
|
// trace order so the false block (which is the next
|
|
// block
|
|
// in a depth first traversal) follows. The height of
|
|
// the
|
|
// stack won't change when we fall through.
|
|
|
|
} else if (inst.isGoto() || inst.isJsr()) {
|
|
// Once again, if we have already visited the target
|
|
// block, add a HeightRecord to the stack.
|
|
|
|
heights[i] += diff;
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + heights[i] + ") " + inst);
|
|
}
|
|
|
|
label = (Label) inst.operand();
|
|
|
|
if ((diff > 0) || !visited.contains(label)) {
|
|
visited.add(label);
|
|
stack.push(new HeightRecord(label, heights[i]));
|
|
}
|
|
|
|
// Deal with the next HeightRecord on the stack.
|
|
break;
|
|
|
|
} else if (inst.isRet()) {
|
|
// Process any unvisited return targets (of the current
|
|
// jsr) or those whose current height is less than the
|
|
// height at this return instruction.
|
|
|
|
heights[i] += diff;
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + heights[i] + ") " + inst);
|
|
}
|
|
|
|
final Label subLabel = (Label) retInsts.get(inst);
|
|
Assert.isTrue(subLabel != null,
|
|
"Not inside a subroutine at " + inst);
|
|
|
|
final Set targets = (Set) retTargets.get(subLabel);
|
|
Assert.isTrue(targets != null, "Subroutine " + subLabel
|
|
+ " has no return targets");
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" Returning from: "
|
|
+ subLabel);
|
|
}
|
|
|
|
final Iterator retIter = targets.iterator();
|
|
|
|
while (retIter.hasNext()) {
|
|
label = (Label) retIter.next();
|
|
|
|
labelIndex = (Integer) labelPos.get(label);
|
|
Assert.isTrue(labelIndex != null, "Index of "
|
|
+ label + " not found");
|
|
|
|
final int idx = labelIndex.intValue();
|
|
|
|
if ((heights[idx] < heights[i])
|
|
|| !visited.contains(label)) {
|
|
visited.add(label);
|
|
stack.push(new HeightRecord(label, heights[i]));
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
} else if (inst.isSwitch()) {
|
|
// Visit each unvisited switch target if it increases
|
|
// the
|
|
// stack height
|
|
|
|
// If the height at this Label has changed since it was
|
|
// last visited, process each target Label. Otherwise,
|
|
// only process unvisited Labels.
|
|
|
|
heights[i] += diff;
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + heights[i] + ") " + inst);
|
|
}
|
|
|
|
// A switch.
|
|
final Switch sw = (Switch) inst.operand();
|
|
|
|
label = sw.defaultTarget();
|
|
|
|
if ((diff > 0) || !visited.contains(label)) {
|
|
visited.add(label);
|
|
stack.push(new HeightRecord(label, heights[i]));
|
|
}
|
|
|
|
final Label[] targets = sw.targets();
|
|
|
|
for (int j = 0; j < targets.length; j++) {
|
|
label = targets[j];
|
|
if ((diff > 0) || !visited.contains(label)) {
|
|
visited.add(label);
|
|
stack.push(new HeightRecord(label, heights[i]));
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
} else {
|
|
// No other blocks to visit. Just adjust the height.
|
|
|
|
heights[i] += diff;
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + heights[i] + ") " + inst);
|
|
}
|
|
}
|
|
|
|
} else if (ce instanceof Label) {
|
|
// We've hit the next block. Update the stack height.
|
|
// Process this next block if has not been visited or its
|
|
// current height is different from the previous
|
|
// instruction.
|
|
|
|
label = (Label) ce;
|
|
|
|
diff = heights[i - 1] - heights[i];
|
|
|
|
if ((diff > 0) || !visited.contains(label)) {
|
|
visited.add(label);
|
|
heights[i] = heights[i - 1];
|
|
}
|
|
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + heights[i] + ") " + label);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find the maximum stack height.
|
|
maxStack = 0;
|
|
|
|
for (i = 0; i < heights.length; i++) {
|
|
final int h = heights[i];
|
|
|
|
if (h > maxStack) {
|
|
maxStack = h;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum number of local variables used by this method.
|
|
*/
|
|
public int maxLocals() {
|
|
return maxLocals;
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum height of the stack at any point in this method.
|
|
*/
|
|
public int maxStack() {
|
|
return maxStack;
|
|
}
|
|
|
|
/**
|
|
* Returns the index in the byte array of the given label.
|
|
*/
|
|
public int labelIndex(final Label label) {
|
|
final Integer i = (Integer) labels.get(label);
|
|
|
|
if (i != null) {
|
|
return i.intValue();
|
|
}
|
|
|
|
throw new IllegalArgumentException("Label " + label + " not found");
|
|
}
|
|
|
|
/**
|
|
* Returns the byte array after resolving branches.
|
|
*/
|
|
public byte[] array() {
|
|
if (branches.size() > 0) {
|
|
if (!longBranch && (codeLength >= 0x10000)) {
|
|
longBranch = true;
|
|
buildCode();
|
|
}
|
|
}
|
|
|
|
final byte[] c = new byte[codeLength];
|
|
int i = codeLength;
|
|
|
|
for (ByteCell p = codeTail; p != null; p = p.prev) {
|
|
c[--i] = p.value;
|
|
}
|
|
|
|
Iterator e;
|
|
|
|
e = branches.keySet().iterator();
|
|
|
|
while (e.hasNext()) {
|
|
final Integer branch = (Integer) e.next();
|
|
final int branchIndex = branch.intValue();
|
|
|
|
final Integer inst = (Integer) branchInsts.get(branch);
|
|
final int instIndex = inst.intValue();
|
|
|
|
final Label label = (Label) branches.get(branch);
|
|
final Integer target = (Integer) labels.get(label);
|
|
|
|
Assert.isTrue(target != null, "Index of " + label + " not found");
|
|
|
|
int diff = target.intValue() - instIndex;
|
|
|
|
Assert.isTrue((-diff < 0x10000) && (diff < 0x10000),
|
|
"Branch offset too large: " + diff);
|
|
|
|
c[branchIndex] = (byte) ((diff >>> 8) & 0xff);
|
|
c[branchIndex + 1] = (byte) (diff & 0xff);
|
|
}
|
|
|
|
e = longBranches.keySet().iterator();
|
|
|
|
while (e.hasNext()) {
|
|
final Integer branch = (Integer) e.next();
|
|
final int branchIndex = branch.intValue();
|
|
|
|
final Integer inst = (Integer) branchInsts.get(branch);
|
|
final int instIndex = inst.intValue();
|
|
|
|
final Label label = (Label) longBranches.get(branch);
|
|
final Integer target = (Integer) labels.get(label);
|
|
|
|
final int diff = target.intValue() - instIndex;
|
|
|
|
c[branchIndex] = (byte) ((diff >>> 24) & 0xff);
|
|
c[branchIndex + 1] = (byte) ((diff >>> 16) & 0xff);
|
|
c[branchIndex + 2] = (byte) ((diff >>> 8) & 0xff);
|
|
c[branchIndex + 3] = (byte) (diff & 0xff);
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* Makes note of a label.
|
|
*/
|
|
public void addLabel(final Label label) {
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + codeLength + ": " + "label " + label);
|
|
}
|
|
|
|
labels.put(label, new Integer(codeLength));
|
|
}
|
|
|
|
/**
|
|
* Adds a 4-byte branch to a given label. The branch is from the index of
|
|
* the last opcode added.
|
|
*/
|
|
public void addLongBranch(final Label label) {
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + codeLength + ": " + "long branch to "
|
|
+ label);
|
|
}
|
|
|
|
branchInsts.put(new Integer(codeLength), new Integer(lastInst));
|
|
longBranches.put(new Integer(codeLength), label);
|
|
addByte(0);
|
|
addByte(0);
|
|
addByte(0);
|
|
addByte(0);
|
|
}
|
|
|
|
/**
|
|
* Adds a 2-byte branch to a given label. The branch is from the index of
|
|
* the last opcode added.
|
|
*/
|
|
public void addBranch(final Label label) {
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + codeLength + ": " + "branch to "
|
|
+ label);
|
|
}
|
|
|
|
branchInsts.put(new Integer(codeLength), new Integer(lastInst));
|
|
branches.put(new Integer(codeLength), label);
|
|
addByte(0);
|
|
addByte(0);
|
|
}
|
|
|
|
/**
|
|
* Add an opcode to the byte array, adjusting for 4-byte alignment for
|
|
* switch instructions and saving the index for calculating branches.
|
|
*
|
|
* @param opcode
|
|
* The opcode.
|
|
* @see Opcode
|
|
*/
|
|
public void addOpcode(final int opcode) {
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" " + codeLength + ": " + "opcode "
|
|
+ Opcode.opcNames[opcode]);
|
|
}
|
|
|
|
lastInst = codeLength;
|
|
|
|
addByte(opcode);
|
|
|
|
if ((opcode == Opcode.opc_tableswitch)
|
|
|| (opcode == Opcode.opc_lookupswitch)) {
|
|
// Switch instructions are followed by padding so that table
|
|
// starts on a 4-byte boundary.
|
|
while (codeLength % 4 != 0) {
|
|
addByte(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a single byte to the array.
|
|
*/
|
|
public void addByte(final int i) {
|
|
if (ClassEditor.DEBUG) {
|
|
System.out.println(" " + codeLength + ": " + "byte " + i);
|
|
}
|
|
|
|
// The bytecode array is represented as a linked list of
|
|
// ByteCells. This method creates a new ByteCell and appends it
|
|
// to the linked list.
|
|
|
|
final ByteCell p = new ByteCell();
|
|
p.value = (byte) (i & 0xff);
|
|
p.prev = codeTail;
|
|
codeTail = p;
|
|
codeLength++;
|
|
}
|
|
|
|
/**
|
|
* Adds a 2-byte short to the array, high byte first.
|
|
*/
|
|
public void addShort(final int i) {
|
|
if (ClassEditor.DEBUG) {
|
|
System.out.println(" " + codeLength + ": " + "short " + i);
|
|
}
|
|
|
|
addByte(i >>> 8);
|
|
addByte(i);
|
|
}
|
|
|
|
/**
|
|
* Adds a 4-byte int to the array, high byte first.
|
|
*/
|
|
public void addInt(final int i) {
|
|
if (ClassEditor.DEBUG) {
|
|
System.out.println(" " + codeLength + ": " + "int " + i);
|
|
}
|
|
|
|
addByte(i >>> 24);
|
|
addByte(i >>> 16);
|
|
addByte(i >>> 8);
|
|
addByte(i);
|
|
}
|
|
|
|
public void visit_nop(final Instruction inst) {
|
|
// If it must have been put there for a reason.
|
|
addOpcode(Opcode.opc_nop);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
/*
|
|
* Does pretty much what you'd expect. Examines the instruction's operand to
|
|
* determine if one of the special constant opcodes (e.g. iconst_1) can be
|
|
* used. Adds the most appropriate instruction.
|
|
*/
|
|
public void visit_ldc(final Instruction inst) {
|
|
final Object operand = inst.operand();
|
|
|
|
if (operand == null) {
|
|
addOpcode(Opcode.opc_aconst_null);
|
|
stackHeight++;
|
|
|
|
} else if (operand instanceof Integer) {
|
|
final int v = ((Integer) operand).intValue();
|
|
|
|
switch (v) {
|
|
case -1:
|
|
addOpcode(Opcode.opc_iconst_m1);
|
|
break;
|
|
case 0:
|
|
addOpcode(Opcode.opc_iconst_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_iconst_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_iconst_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_iconst_3);
|
|
break;
|
|
case 4:
|
|
addOpcode(Opcode.opc_iconst_4);
|
|
break;
|
|
case 5:
|
|
addOpcode(Opcode.opc_iconst_5);
|
|
break;
|
|
default: {
|
|
if ((byte) v == v) {
|
|
addOpcode(Opcode.opc_bipush);
|
|
addByte(v);
|
|
} else if ((short) v == v) {
|
|
addOpcode(Opcode.opc_sipush);
|
|
addShort(v);
|
|
} else {
|
|
final int index = constants.addConstant(Constant.INTEGER,
|
|
operand);
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_ldc);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_ldc_w);
|
|
addShort(index);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
stackHeight++;
|
|
|
|
} else if (operand instanceof Float) {
|
|
final float v = ((Float) operand).floatValue();
|
|
|
|
if (v == 0.0F) {
|
|
addOpcode(Opcode.opc_fconst_0);
|
|
} else if (v == 1.0F) {
|
|
addOpcode(Opcode.opc_fconst_1);
|
|
} else if (v == 2.0F) {
|
|
addOpcode(Opcode.opc_fconst_2);
|
|
} else {
|
|
final int index = constants
|
|
.addConstant(Constant.FLOAT, operand);
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_ldc);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_ldc_w);
|
|
addShort(index);
|
|
}
|
|
}
|
|
|
|
stackHeight++;
|
|
|
|
} else if (operand instanceof Long) {
|
|
final long v = ((Long) operand).longValue();
|
|
|
|
if (v == 0) {
|
|
addOpcode(Opcode.opc_lconst_0);
|
|
} else if (v == 1) {
|
|
addOpcode(Opcode.opc_lconst_1);
|
|
} else {
|
|
final int index = constants.addConstant(Constant.LONG, operand);
|
|
addOpcode(Opcode.opc_ldc2_w);
|
|
addShort(index);
|
|
}
|
|
|
|
stackHeight += 2;
|
|
|
|
} else if (operand instanceof Double) {
|
|
final double v = ((Double) operand).doubleValue();
|
|
|
|
if (v == 0.0) {
|
|
addOpcode(Opcode.opc_dconst_0);
|
|
} else if (v == 1.0) {
|
|
addOpcode(Opcode.opc_dconst_1);
|
|
} else {
|
|
final int index = constants.addConstant(Constant.DOUBLE,
|
|
operand);
|
|
addOpcode(Opcode.opc_ldc2_w);
|
|
addShort(index);
|
|
}
|
|
|
|
stackHeight += 2;
|
|
|
|
} else if (operand instanceof String) {
|
|
final int index = constants.addConstant(Constant.STRING, operand);
|
|
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_ldc);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_ldc_w);
|
|
addShort(index);
|
|
}
|
|
|
|
stackHeight++;
|
|
} else {
|
|
throw new RuntimeException();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Tries to use the shorter iload_x instructions.
|
|
*/
|
|
public void visit_iload(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_iload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_iload);
|
|
addShort(index);
|
|
}
|
|
stackHeight++;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_iload_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_iload_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_iload_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_iload_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_iload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_iload);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_lload(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 2 > maxLocals) {
|
|
maxLocals = index + 2;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_lload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_lload);
|
|
addShort(index);
|
|
}
|
|
stackHeight++;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_lload_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_lload_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_lload_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_lload_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_lload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_lload);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight += 2;
|
|
}
|
|
|
|
public void visit_fload(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_fload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_fload);
|
|
addShort(index);
|
|
}
|
|
|
|
stackHeight++;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_fload_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_fload_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_fload_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_fload_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_fload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_fload);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_dload(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 2 > maxLocals) {
|
|
maxLocals = index + 2;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_dload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_dload);
|
|
addShort(index);
|
|
}
|
|
stackHeight += 2;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_dload_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_dload_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_dload_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_dload_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_dload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_dload);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight += 2;
|
|
}
|
|
|
|
public void visit_aload(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_aload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_aload);
|
|
addShort(index);
|
|
}
|
|
stackHeight++;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_aload_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_aload_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_aload_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_aload_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_aload);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_aload);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight++;
|
|
}
|
|
|
|
/**
|
|
* Pops an item off the stack.
|
|
*/
|
|
public void visit_iaload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_iaload);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_laload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_laload);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_faload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_faload);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_daload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_daload);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_aaload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_aaload);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_baload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_baload);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_caload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_caload);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_saload(final Instruction inst) {
|
|
addOpcode(Opcode.opc_saload);
|
|
stackHeight--;
|
|
}
|
|
|
|
/*
|
|
* Try to take advantage of smaller opcodes (e.g. istore_1).
|
|
*/
|
|
public void visit_istore(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_istore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_istore);
|
|
addShort(index);
|
|
}
|
|
stackHeight--;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_istore_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_istore_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_istore_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_istore_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_istore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_istore);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lstore(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 2 > maxLocals) {
|
|
maxLocals = index + 2;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_lstore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_lstore);
|
|
addShort(index);
|
|
}
|
|
stackHeight -= 2;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_lstore_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_lstore_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_lstore_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_lstore_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_lstore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_lstore);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_fstore(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_fstore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_fstore);
|
|
addShort(index);
|
|
}
|
|
stackHeight--;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_fstore_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_fstore_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_fstore_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_fstore_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_fstore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_fstore);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_dstore(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 2 > maxLocals) {
|
|
maxLocals = index + 2;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_dstore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_dstore);
|
|
addShort(index);
|
|
}
|
|
stackHeight -= 2;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_dstore_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_dstore_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_dstore_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_dstore_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_dstore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_dstore);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_astore(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
if (inst.useSlow()) {
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_astore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_astore);
|
|
addShort(index);
|
|
}
|
|
stackHeight--;
|
|
return;
|
|
}
|
|
|
|
switch (index) {
|
|
case 0:
|
|
addOpcode(Opcode.opc_astore_0);
|
|
break;
|
|
case 1:
|
|
addOpcode(Opcode.opc_astore_1);
|
|
break;
|
|
case 2:
|
|
addOpcode(Opcode.opc_astore_2);
|
|
break;
|
|
case 3:
|
|
addOpcode(Opcode.opc_astore_3);
|
|
break;
|
|
default:
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_astore);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_astore);
|
|
addShort(index);
|
|
}
|
|
break;
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
/*
|
|
* Store into an array. Pop 3+ items off the stack.
|
|
*/
|
|
public void visit_iastore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_iastore);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_lastore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lastore);
|
|
stackHeight -= 4;
|
|
}
|
|
|
|
public void visit_fastore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fastore);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_dastore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dastore);
|
|
stackHeight -= 4;
|
|
}
|
|
|
|
public void visit_aastore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_aastore);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_bastore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_bastore);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_castore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_castore);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_sastore(final Instruction inst) {
|
|
addOpcode(Opcode.opc_sastore);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_pop(final Instruction inst) {
|
|
addOpcode(Opcode.opc_pop);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_pop2(final Instruction inst) {
|
|
addOpcode(Opcode.opc_pop2);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_dup(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dup);
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_dup_x1(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dup_x1);
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_dup_x2(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dup_x2);
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_dup2(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dup2);
|
|
stackHeight += 2;
|
|
}
|
|
|
|
public void visit_dup2_x1(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dup2_x1);
|
|
stackHeight += 2;
|
|
}
|
|
|
|
public void visit_dup2_x2(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dup2_x2);
|
|
stackHeight += 2;
|
|
}
|
|
|
|
public void visit_swap(final Instruction inst) {
|
|
addOpcode(Opcode.opc_swap);
|
|
}
|
|
|
|
public void visit_iadd(final Instruction inst) {
|
|
addOpcode(Opcode.opc_iadd);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ladd(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ladd);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_fadd(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fadd);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_dadd(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dadd);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_isub(final Instruction inst) {
|
|
addOpcode(Opcode.opc_isub);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lsub(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lsub);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_fsub(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fsub);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_dsub(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dsub);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_imul(final Instruction inst) {
|
|
addOpcode(Opcode.opc_imul);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lmul(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lmul);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_fmul(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fmul);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_dmul(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dmul);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_idiv(final Instruction inst) {
|
|
addOpcode(Opcode.opc_idiv);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ldiv(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ldiv);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_fdiv(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fdiv);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ddiv(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ddiv);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_irem(final Instruction inst) {
|
|
addOpcode(Opcode.opc_irem);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lrem(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lrem);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_frem(final Instruction inst) {
|
|
addOpcode(Opcode.opc_frem);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_drem(final Instruction inst) {
|
|
addOpcode(Opcode.opc_drem);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_ineg(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ineg);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_lneg(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lneg);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_fneg(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fneg);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_dneg(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dneg);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_ishl(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ishl);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lshl(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lshl);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ishr(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ishr);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lshr(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lshr);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_iushr(final Instruction inst) {
|
|
addOpcode(Opcode.opc_iushr);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lushr(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lushr);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_iand(final Instruction inst) {
|
|
addOpcode(Opcode.opc_iand);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_land(final Instruction inst) {
|
|
addOpcode(Opcode.opc_land);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_ior(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ior);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lor(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lor);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_ixor(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ixor);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_lxor(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lxor);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_iinc(final Instruction inst) {
|
|
final IncOperand operand = (IncOperand) inst.operand();
|
|
|
|
final int index = operand.var().index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
final int incr = operand.incr();
|
|
|
|
if ((index < 256) && ((byte) incr == incr)) {
|
|
addOpcode(Opcode.opc_iinc);
|
|
addByte(index);
|
|
addByte(incr);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_iinc);
|
|
addShort(index);
|
|
addShort(incr);
|
|
}
|
|
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_i2l(final Instruction inst) {
|
|
addOpcode(Opcode.opc_i2l);
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_i2f(final Instruction inst) {
|
|
addOpcode(Opcode.opc_i2f);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_i2d(final Instruction inst) {
|
|
addOpcode(Opcode.opc_i2d);
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_l2i(final Instruction inst) {
|
|
addOpcode(Opcode.opc_l2i);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_l2f(final Instruction inst) {
|
|
addOpcode(Opcode.opc_l2f);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_l2d(final Instruction inst) {
|
|
addOpcode(Opcode.opc_l2d);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_f2i(final Instruction inst) {
|
|
addOpcode(Opcode.opc_f2i);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_f2l(final Instruction inst) {
|
|
addOpcode(Opcode.opc_f2l);
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_f2d(final Instruction inst) {
|
|
addOpcode(Opcode.opc_f2d);
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_d2i(final Instruction inst) {
|
|
addOpcode(Opcode.opc_d2i);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_d2l(final Instruction inst) {
|
|
addOpcode(Opcode.opc_d2l);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_d2f(final Instruction inst) {
|
|
addOpcode(Opcode.opc_d2f);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_i2b(final Instruction inst) {
|
|
addOpcode(Opcode.opc_i2b);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_i2c(final Instruction inst) {
|
|
addOpcode(Opcode.opc_i2c);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_i2s(final Instruction inst) {
|
|
addOpcode(Opcode.opc_i2s);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_lcmp(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lcmp);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_fcmpl(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fcmpl);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_fcmpg(final Instruction inst) {
|
|
addOpcode(Opcode.opc_fcmpg);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_dcmpl(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dcmpl);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_dcmpg(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dcmpg);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
/*
|
|
* Handle long branches.
|
|
*/
|
|
public void visit_ifeq(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_ifne);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_ifeq);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ifne(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_ifeq);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_ifne);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_iflt(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_ifge);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_iflt);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ifge(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_iflt);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_ifge);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ifgt(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_ifle);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_ifgt);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ifle(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_ifgt);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_ifle);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_if_icmpeq(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_icmpne);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_icmpeq);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_if_icmpne(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_icmpeq);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_icmpne);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_if_icmplt(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_icmpge);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_icmplt);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_if_icmpge(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_icmplt);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_icmpge);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_if_icmpgt(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_icmple);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_icmpgt);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_if_icmple(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_icmpgt);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_icmple);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_if_acmpeq(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_acmpne);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_acmpeq);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_if_acmpne(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_if_acmpeq);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_if_acmpne);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_goto(final Instruction inst) {
|
|
if (longBranch) {
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
} else {
|
|
addOpcode(Opcode.opc_goto);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_jsr(final Instruction inst) {
|
|
if (longBranch) {
|
|
addOpcode(Opcode.opc_jsr_w);
|
|
addLongBranch((Label) inst.operand());
|
|
} else {
|
|
addOpcode(Opcode.opc_jsr);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_ret(final Instruction inst) {
|
|
final int index = ((LocalVariable) inst.operand()).index();
|
|
|
|
if (index + 1 > maxLocals) {
|
|
maxLocals = index + 1;
|
|
}
|
|
|
|
if (index < 256) {
|
|
addOpcode(Opcode.opc_ret);
|
|
addByte(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_wide);
|
|
addByte(Opcode.opc_ret);
|
|
addShort(index);
|
|
}
|
|
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_switch(final Instruction inst) {
|
|
final Switch sw = (Switch) inst.operand();
|
|
|
|
final int[] values = sw.values();
|
|
final Label[] targets = sw.targets();
|
|
|
|
if (values.length == 0) {
|
|
if (longBranch) {
|
|
addOpcode(Opcode.opc_pop); // Pop switch "index" off stack
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch(sw.defaultTarget());
|
|
} else {
|
|
addOpcode(Opcode.opc_pop); // Pop switch "index" off stack
|
|
addOpcode(Opcode.opc_goto);
|
|
addBranch(sw.defaultTarget());
|
|
}
|
|
} else if (sw.hasContiguousValues()) {
|
|
addOpcode(Opcode.opc_tableswitch);
|
|
addLongBranch(sw.defaultTarget());
|
|
|
|
addInt(values[0]);
|
|
addInt(values[values.length - 1]);
|
|
|
|
for (int i = 0; i < targets.length; i++) {
|
|
addLongBranch(targets[i]);
|
|
}
|
|
} else {
|
|
addOpcode(Opcode.opc_lookupswitch);
|
|
addLongBranch(sw.defaultTarget());
|
|
|
|
addInt(values.length);
|
|
|
|
for (int i = 0; i < targets.length; i++) {
|
|
addInt(values[i]);
|
|
addLongBranch(targets[i]);
|
|
}
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ireturn(final Instruction inst) {
|
|
addOpcode(Opcode.opc_ireturn);
|
|
stackHeight = 0;
|
|
}
|
|
|
|
public void visit_lreturn(final Instruction inst) {
|
|
addOpcode(Opcode.opc_lreturn);
|
|
stackHeight = 0;
|
|
}
|
|
|
|
public void visit_freturn(final Instruction inst) {
|
|
addOpcode(Opcode.opc_freturn);
|
|
stackHeight = 0;
|
|
}
|
|
|
|
public void visit_dreturn(final Instruction inst) {
|
|
addOpcode(Opcode.opc_dreturn);
|
|
stackHeight = 0;
|
|
}
|
|
|
|
public void visit_areturn(final Instruction inst) {
|
|
addOpcode(Opcode.opc_areturn);
|
|
stackHeight = 0;
|
|
}
|
|
|
|
public void visit_return(final Instruction inst) {
|
|
addOpcode(Opcode.opc_return);
|
|
stackHeight = 0;
|
|
}
|
|
|
|
public void visit_getstatic(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.FIELD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_getstatic);
|
|
addShort(index);
|
|
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
stackHeight += type.stackHeight();
|
|
}
|
|
|
|
public void visit_putstatic(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.FIELD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_putstatic);
|
|
addShort(index);
|
|
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
stackHeight -= type.stackHeight();
|
|
}
|
|
|
|
public void visit_putstatic_nowb(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.FIELD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_putstatic_nowb);
|
|
addShort(index);
|
|
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
stackHeight -= type.stackHeight();
|
|
}
|
|
|
|
public void visit_getfield(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.FIELD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_getfield);
|
|
addShort(index);
|
|
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
stackHeight += type.stackHeight() - 1;
|
|
}
|
|
|
|
public void visit_putfield(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.FIELD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_putfield);
|
|
addShort(index);
|
|
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
stackHeight -= type.stackHeight() + 1;
|
|
}
|
|
|
|
public void visit_putfield_nowb(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.FIELD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_putfield_nowb);
|
|
addShort(index);
|
|
|
|
final Type type = ((MemberRef) inst.operand()).nameAndType().type();
|
|
stackHeight -= type.stackHeight() + 1;
|
|
}
|
|
|
|
public void visit_invokevirtual(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.METHOD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_invokevirtual);
|
|
addShort(index);
|
|
|
|
final MemberRef method = (MemberRef) inst.operand();
|
|
final Type type = method.nameAndType().type();
|
|
|
|
stackHeight += type.returnType().stackHeight() - type.stackHeight() - 1;
|
|
}
|
|
|
|
public void visit_invokespecial(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.METHOD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_invokespecial);
|
|
addShort(index);
|
|
|
|
final MemberRef method = (MemberRef) inst.operand();
|
|
final Type type = method.nameAndType().type();
|
|
|
|
stackHeight += type.returnType().stackHeight() - type.stackHeight() - 1;
|
|
}
|
|
|
|
public void visit_invokestatic(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.METHOD_REF, inst
|
|
.operand());
|
|
addOpcode(Opcode.opc_invokestatic);
|
|
addShort(index);
|
|
|
|
final MemberRef method = (MemberRef) inst.operand();
|
|
final Type type = method.nameAndType().type();
|
|
|
|
Assert.isTrue(type.isMethod(),
|
|
"Trying to invoke a type that is not a method: " + method);
|
|
|
|
stackHeight += type.returnType().stackHeight() - type.stackHeight();
|
|
}
|
|
|
|
public void visit_invokeinterface(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.INTERFACE_METHOD_REF,
|
|
inst.operand());
|
|
final MemberRef method = (MemberRef) constants.constantAt(index);
|
|
final Type type = method.nameAndType().type();
|
|
|
|
addOpcode(Opcode.opc_invokeinterface);
|
|
addShort(index);
|
|
addByte(type.stackHeight() + 1);
|
|
addByte(0);
|
|
|
|
stackHeight += type.returnType().stackHeight() - type.stackHeight() - 1;
|
|
}
|
|
|
|
public void visit_new(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.CLASS, inst.operand());
|
|
addOpcode(Opcode.opc_new);
|
|
addShort(index);
|
|
|
|
stackHeight++;
|
|
}
|
|
|
|
public void visit_newarray(final Instruction inst) {
|
|
final Type type = (Type) inst.operand();
|
|
|
|
if (type.isReference()) {
|
|
final int index = constants.addConstant(Constant.CLASS, type);
|
|
addOpcode(Opcode.opc_anewarray);
|
|
addShort(index);
|
|
} else {
|
|
addOpcode(Opcode.opc_newarray);
|
|
addByte(type.typeCode());
|
|
}
|
|
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_arraylength(final Instruction inst) {
|
|
addOpcode(Opcode.opc_arraylength);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_athrow(final Instruction inst) {
|
|
addOpcode(Opcode.opc_athrow);
|
|
stackHeight = 0;
|
|
}
|
|
|
|
public void visit_checkcast(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.CLASS, inst.operand());
|
|
addOpcode(Opcode.opc_checkcast);
|
|
addShort(index);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_instanceof(final Instruction inst) {
|
|
final int index = constants.addConstant(Constant.CLASS, inst.operand());
|
|
addOpcode(Opcode.opc_instanceof);
|
|
addShort(index);
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_monitorenter(final Instruction inst) {
|
|
addOpcode(Opcode.opc_monitorenter);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_monitorexit(final Instruction inst) {
|
|
addOpcode(Opcode.opc_monitorexit);
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_multianewarray(final Instruction inst) {
|
|
final MultiArrayOperand operand = (MultiArrayOperand) inst.operand();
|
|
final Type type = operand.type();
|
|
final int dim = operand.dimensions();
|
|
final int index = constants.addConstant(Constant.CLASS, type);
|
|
addOpcode(Opcode.opc_multianewarray);
|
|
addShort(index);
|
|
addByte(dim);
|
|
|
|
stackHeight += 1 - dim;
|
|
}
|
|
|
|
public void visit_ifnull(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_ifnonnull);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_ifnull);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_ifnonnull(final Instruction inst) {
|
|
if (longBranch) {
|
|
final Label tmp = method.newLabel();
|
|
addOpcode(Opcode.opc_ifnull);
|
|
addBranch(tmp);
|
|
addOpcode(Opcode.opc_goto_w);
|
|
addLongBranch((Label) inst.operand());
|
|
addLabel(tmp);
|
|
} else {
|
|
addOpcode(Opcode.opc_ifnonnull);
|
|
addBranch((Label) inst.operand());
|
|
}
|
|
|
|
stackHeight--;
|
|
}
|
|
|
|
public void visit_rc(final Instruction inst) {
|
|
final Integer operand = (Integer) inst.operand();
|
|
addOpcode(Opcode.opc_rc);
|
|
addByte(operand.intValue());
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_aswizzle(final Instruction inst) {
|
|
addOpcode(Opcode.opc_aswizzle);
|
|
stackHeight -= 2;
|
|
}
|
|
|
|
public void visit_aswrange(final Instruction inst) {
|
|
addOpcode(Opcode.opc_aswrange);
|
|
stackHeight -= 3;
|
|
}
|
|
|
|
public void visit_aupdate(final Instruction inst) {
|
|
final Integer operand = (Integer) inst.operand();
|
|
addOpcode(Opcode.opc_aupdate);
|
|
addByte(operand.intValue());
|
|
stackHeight += 0;
|
|
}
|
|
|
|
public void visit_supdate(final Instruction inst) {
|
|
final Integer operand = (Integer) inst.operand();
|
|
addOpcode(Opcode.opc_supdate);
|
|
addByte(operand.intValue());
|
|
stackHeight += 0;
|
|
}
|
|
|
|
/**
|
|
* Represents the height of the stack at given Label.
|
|
*/
|
|
class HeightRecord {
|
|
Label label;
|
|
|
|
int height;
|
|
|
|
public HeightRecord(final Label label, final int height) {
|
|
if (ClassEditor.DEBUG || CodeArray.DEBUG) {
|
|
System.out.println(" push " + label + " at " + height);
|
|
}
|
|
|
|
this.label = label;
|
|
this.height = height;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used to represent the byte array.
|
|
*/
|
|
class ByteCell {
|
|
byte value;
|
|
|
|
ByteCell prev;
|
|
}
|
|
}
|
|
|