📄 jsrinlineradapter.java
字号:
* @param instant TODO.
* @param workList TODO.
* @param newInstructions TODO.
* @param newTryCatchBlocks TODO.
*/
private void emitSubroutine(
final Instantiation instant,
final List worklist,
final InsnList newInstructions,
final List newTryCatchBlocks,
final List newLocalVariables)
{
LabelNode duplbl = null;
if (LOGGING) {
log("--------------------------------------------------------");
log("Emitting instantiation of subroutine " + instant.subroutine);
}
// Emit the relevant instructions for this instantiation, translating
// labels and jump targets as we go:
for (int i = 0, c = instructions.size(); i < c; i++) {
AbstractInsnNode insn = instructions.get(i);
Instantiation owner = instant.findOwner(i);
// Always remap labels:
if (insn.getType() == AbstractInsnNode.LABEL) {
// Translate labels into their renamed equivalents.
// Avoid adding the same label more than once. Note
// that because we own this instruction the gotoTable
// and the rangeTable will always agree.
LabelNode ilbl = (LabelNode) insn;
LabelNode remap = instant.rangeLabel(ilbl);
if (LOGGING) {
log("Translating lbl #" + i + ":" + ilbl + " to " + remap);
}
if (remap != duplbl) {
newInstructions.add(remap);
duplbl = remap;
}
continue;
}
// We don't want to emit instructions that were already
// emitted by a subroutine higher on the stack. Note that
// it is still possible for a given instruction to be
// emitted twice because it may belong to two subroutines
// that do not invoke each other.
if (owner != instant) {
continue;
}
if (LOGGING) {
log("Emitting inst #" + i);
}
if (insn.getOpcode() == RET) {
// Translate RET instruction(s) to a jump to the return label
// for the appropriate instantiation. The problem is that the
// subroutine may "fall through" to the ret of a parent
// subroutine; therefore, to find the appropriate ret label we
// find the lowest subroutine on the stack that claims to own
// this instruction. See the class javadoc comment for an
// explanation on why this technique is safe (note: it is only
// safe if the input is verifiable).
LabelNode retlabel = null;
for (Instantiation p = instant; p != null; p = p.previous) {
if (p.subroutine.ownsInstruction(i)) {
retlabel = p.returnLabel;
}
}
if (retlabel == null) {
// This is only possible if the mainSubroutine owns a RET
// instruction, which should never happen for verifiable
// code.
throw new RuntimeException("Instruction #" + i
+ " is a RET not owned by any subroutine");
}
newInstructions.add(new JumpInsnNode(GOTO, retlabel));
} else if (insn.getOpcode() == JSR) {
LabelNode lbl = ((JumpInsnNode) insn).label;
Subroutine sub = (Subroutine) subroutineHeads.get(lbl);
Instantiation newinst = new Instantiation(instant, sub);
LabelNode startlbl = newinst.gotoLabel(lbl);
if (LOGGING) {
log(" Creating instantiation of subr " + sub);
}
// Rather than JSRing, we will jump to the inline version and
// push NULL for what was once the return value. This hack
// allows us to avoid doing any sort of data flow analysis to
// figure out which instructions manipulate the old return value
// pointer which is now known to be unneeded.
newInstructions.add(new InsnNode(ACONST_NULL));
newInstructions.add(new JumpInsnNode(GOTO, startlbl));
newInstructions.add(newinst.returnLabel);
// Insert this new instantiation into the queue to be emitted
// later.
worklist.add(newinst);
} else {
newInstructions.add(insn.clone(instant));
}
}
// Emit try/catch blocks that are relevant to this method.
for (Iterator it = tryCatchBlocks.iterator(); it.hasNext();) {
TryCatchBlockNode trycatch = (TryCatchBlockNode) it.next();
if (LOGGING) {
log("try catch block original labels=" + trycatch.start + "-"
+ trycatch.end + "->" + trycatch.handler);
}
final LabelNode start = instant.rangeLabel(trycatch.start);
final LabelNode end = instant.rangeLabel(trycatch.end);
// Ignore empty try/catch regions
if (start == end) {
if (LOGGING) {
log(" try catch block empty in this subroutine");
}
continue;
}
final LabelNode handler = instant.gotoLabel(trycatch.handler);
if (LOGGING) {
log(" try catch block new labels=" + start + "-" + end + "->"
+ handler);
}
if (start == null || end == null || handler == null) {
throw new RuntimeException("Internal error!");
}
newTryCatchBlocks.add(new TryCatchBlockNode(start,
end,
handler,
trycatch.type));
}
for (Iterator it = localVariables.iterator(); it.hasNext();) {
LocalVariableNode lvnode = (LocalVariableNode) it.next();
if (LOGGING) {
log("local var " + lvnode.name);
}
final LabelNode start = instant.rangeLabel(lvnode.start);
final LabelNode end = instant.rangeLabel(lvnode.end);
if (start == end) {
if (LOGGING) {
log(" local variable empty in this sub");
}
continue;
}
newLocalVariables.add(new LocalVariableNode(lvnode.name,
lvnode.desc,
lvnode.signature,
start,
end,
lvnode.index));
}
}
private void log(final String str) {
System.err.println(str);
}
protected static class Subroutine {
public final BitSet instructions = new BitSet();
public void addInstruction(final int idx) {
instructions.set(idx);
}
public boolean ownsInstruction(final int idx) {
return instructions.get(idx);
}
public String toString() {
return "Subroutine: " + instructions;
}
}
/**
* A class that represents an instantiation of a subroutine. Each
* instantiation has an associate "stack" --- which is a listing of those
* instantiations that were active when this particular instance of this
* subroutine was invoked. Each instantiation also has a map from the
* original labels of the program to the labels appropriate for this
* instantiation, and finally a label to return to.
*/
private class Instantiation extends AbstractMap {
/**
* Previous instantiations; the stack must be statically predictable to
* be inlinable.
*/
final Instantiation previous;
/**
* The subroutine this is an instantiation of.
*/
public final Subroutine subroutine;
/**
* This table maps Labels from the original source to Labels pointing at
* code specific to this instantiation, for use in remapping try/catch
* blocks,as well as gotos.
*
* Note that in the presence of dual citizens instructions, that is,
* instructions which belong to more than one subroutine due to the
* merging of control flow without a RET instruction, we will map the
* target label of a GOTO to the label used by the instantiation lowest
* on the stack. This avoids code duplication during inlining in most
* cases.
*
* @see #findOwner(int)
*/
public final Map rangeTable = new HashMap();
/**
* All returns for this instantiation will be mapped to this label
*/
public final LabelNode returnLabel;
public Instantiation(final Instantiation prev, final Subroutine sub) {
previous = prev;
subroutine = sub;
for (Instantiation p = prev; p != null; p = p.previous) {
if (p.subroutine == sub) {
throw new RuntimeException("Recursive invocation of " + sub);
}
}
// Determine the label to return to when this subroutine terminates
// via RET: note that the main subroutine never terminates via RET.
if (prev != null) {
returnLabel = new LabelNode();
} else {
returnLabel = null;
}
// Each instantiation will remap the labels from the code above to
// refer to its particular copy of its own instructions. Note that
// we collapse labels which point at the same instruction into one:
// this is fairly common as we are often ignoring large chunks of
// instructions, so what were previously distinct labels become
// duplicates.
LabelNode duplbl = null;
for (int i = 0, c = instructions.size(); i < c; i++) {
AbstractInsnNode insn = instructions.get(i);
if (insn.getType() == AbstractInsnNode.LABEL) {
LabelNode ilbl = (LabelNode) insn;
if (duplbl == null) {
// if we already have a label pointing at this spot,
// don't recreate it.
duplbl = new LabelNode();
}
// Add an entry in the rangeTable for every label
// in the original code which points at the next
// instruction of our own to be emitted.
rangeTable.put(ilbl, duplbl);
} else if (findOwner(i) == this) {
// We will emit this instruction, so clear the 'duplbl' flag
// since the next Label will refer to a distinct
// instruction.
duplbl = null;
}
}
}
/**
* Returns the "owner" of a particular instruction relative to this
* instantiation: the owner referes to the Instantiation which will emit
* the version of this instruction that we will execute.
*
* Typically, the return value is either <code>this</code> or
* <code>null</code>. <code>this</code> indicates that this
* instantiation will generate the version of this instruction that we
* will execute, and <code>null</code> indicates that this
* instantiation never executes the given instruction.
*
* Sometimes, however, an instruction can belong to multiple
* subroutines; this is called a "dual citizen" instruction (though it
* may belong to more than 2 subroutines), and occurs when multiple
* subroutines branch to common points of control. In this case, the
* owner is the subroutine that appears lowest on the stack, and which
* also owns the instruction in question.
*
* @param i the index of the instruction in the original code
* @return the "owner" of a particular instruction relative to this
* instantiation.
*/
public Instantiation findOwner(final int i) {
if (!subroutine.ownsInstruction(i)) {
return null;
}
if (!dualCitizens.get(i)) {
return this;
}
Instantiation own = this;
for (Instantiation p = previous; p != null; p = p.previous) {
if (p.subroutine.ownsInstruction(i)) {
own = p;
}
}
return own;
}
/**
* Looks up the label <code>l</code> in the <code>gotoTable</code>,
* thus translating it from a Label in the original code, to a Label in
* the inlined code that is appropriate for use by an instruction that
* branched to the original label.
*
* @param l The label we will be translating
* @return a label for use by a branch instruction in the inlined code
* @see #gotoTable
*/
public LabelNode gotoLabel(final LabelNode l) {
// owner should never be null, because owner is only null
// if an instruction cannot be reached from this subroutine
Instantiation owner = findOwner(instructions.indexOf(l));
return (LabelNode) owner.rangeTable.get(l);
}
/**
* Looks up the label <code>l</code> in the <code>rangeTable</code>,
* thus translating it from a Label in the original code, to a Label in
* the inlined code that is appropriate for use by an try/catch or
* variable use annotation.
*
* @param l The label we will be translating
* @return a label for use by a try/catch or variable annotation in the
* original code
* @see #rangeTable
*/
public LabelNode rangeLabel(final LabelNode l) {
return (LabelNode) rangeTable.get(l);
}
// AbstractMap implementation
public Set entrySet() {
return null;
}
public Object get(final Object o) {
return gotoLabel((LabelNode) o);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -