| /*** |
| * ASM: a very small and fast Java bytecode manipulation framework |
| * Copyright (c) 2000-2007 INRIA, France Telecom |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package com.google.gwt.dev.asm.commons; |
| |
| import java.util.AbstractMap; |
| import java.util.ArrayList; |
| import java.util.BitSet; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import com.google.gwt.dev.asm.Label; |
| import com.google.gwt.dev.asm.MethodVisitor; |
| import com.google.gwt.dev.asm.Opcodes; |
| import com.google.gwt.dev.asm.Type; |
| import com.google.gwt.dev.asm.tree.AbstractInsnNode; |
| import com.google.gwt.dev.asm.tree.InsnList; |
| import com.google.gwt.dev.asm.tree.InsnNode; |
| import com.google.gwt.dev.asm.tree.JumpInsnNode; |
| import com.google.gwt.dev.asm.tree.LabelNode; |
| import com.google.gwt.dev.asm.tree.LookupSwitchInsnNode; |
| import com.google.gwt.dev.asm.tree.MethodNode; |
| import com.google.gwt.dev.asm.tree.TableSwitchInsnNode; |
| import com.google.gwt.dev.asm.tree.TryCatchBlockNode; |
| import com.google.gwt.dev.asm.tree.LocalVariableNode; |
| |
| /** |
| * A {@link org.objectweb.asm.MethodAdapter} that removes JSR instructions and |
| * inlines the referenced subroutines. |
| * |
| * <b>Explanation of how it works</b> TODO |
| * |
| * @author Niko Matsakis |
| */ |
| public class JSRInlinerAdapter extends MethodNode implements Opcodes { |
| |
| private static final boolean LOGGING = false; |
| |
| /** |
| * The visitor to which we will emit a translation of this method without |
| * internal subroutines. |
| */ |
| private final MethodVisitor mv; |
| |
| /** |
| * For each label that is jumped to by a JSR, we create a Subroutine |
| * instance. Map<LabelNode,Subroutine> is the generic type. |
| */ |
| private final Map subroutineHeads = new HashMap(); |
| |
| /** |
| * This subroutine instance denotes the line of execution that is not |
| * contained within any subroutine; i.e., the "subroutine" that is executing |
| * when a method first begins. |
| */ |
| private final Subroutine mainSubroutine = new Subroutine(); |
| |
| /** |
| * This BitSet contains the index of every instruction that belongs to more |
| * than one subroutine. This should not happen often. |
| */ |
| private final BitSet dualCitizens = new BitSet(); |
| |
| /** |
| * Creates a new JSRInliner. |
| * |
| * @param mv the <code>MethodVisitor</code> to send the resulting inlined |
| * method code to (use <code>null</code> for none). |
| * @param access the method's access flags (see {@link Opcodes}). This |
| * parameter also indicates if the method is synthetic and/or |
| * deprecated. |
| * @param name the method's name. |
| * @param desc the method's descriptor (see {@link Type}). |
| * @param signature the method's signature. May be <tt>null</tt>. |
| * @param exceptions the internal names of the method's exception classes |
| * (see {@link Type#getInternalName() getInternalName}). May be |
| * <tt>null</tt>. |
| */ |
| public JSRInlinerAdapter( |
| final MethodVisitor mv, |
| final int access, |
| final String name, |
| final String desc, |
| final String signature, |
| final String[] exceptions) |
| { |
| super(access, name, desc, signature, exceptions); |
| this.mv = mv; |
| } |
| |
| /** |
| * Detects a JSR instruction and sets a flag to indicate we will need to do |
| * inlining. |
| */ |
| public void visitJumpInsn(final int opcode, final Label lbl) { |
| super.visitJumpInsn(opcode, lbl); |
| LabelNode ln = ((JumpInsnNode) instructions.getLast()).label; |
| if (opcode == JSR && !subroutineHeads.containsKey(ln)) { |
| subroutineHeads.put(ln, new Subroutine()); |
| } |
| } |
| |
| /** |
| * If any JSRs were seen, triggers the inlining process. Otherwise, forwards |
| * the byte codes untouched. |
| */ |
| public void visitEnd() { |
| if (!subroutineHeads.isEmpty()) { |
| markSubroutines(); |
| if (LOGGING) { |
| log(mainSubroutine.toString()); |
| Iterator it = subroutineHeads.values().iterator(); |
| while (it.hasNext()) { |
| Subroutine sub = (Subroutine) it.next(); |
| log(sub.toString()); |
| } |
| } |
| emitCode(); |
| } |
| |
| // Forward the translate opcodes on if appropriate: |
| if (mv != null) { |
| accept(mv); |
| } |
| } |
| |
| /** |
| * Walks the method and determines which internal subroutine(s), if any, |
| * each instruction is a method of. |
| */ |
| private void markSubroutines() { |
| BitSet anyvisited = new BitSet(); |
| |
| // First walk the main subroutine and find all those instructions which |
| // can be reached without invoking any JSR at all |
| markSubroutineWalk(mainSubroutine, 0, anyvisited); |
| |
| // Go through the head of each subroutine and find any nodes reachable |
| // to that subroutine without following any JSR links. |
| for (Iterator it = subroutineHeads.entrySet().iterator(); it.hasNext();) |
| { |
| Map.Entry entry = (Map.Entry) it.next(); |
| LabelNode lab = (LabelNode) entry.getKey(); |
| Subroutine sub = (Subroutine) entry.getValue(); |
| int index = instructions.indexOf(lab); |
| markSubroutineWalk(sub, index, anyvisited); |
| } |
| } |
| |
| /** |
| * Performs a depth first search walking the normal byte code path starting |
| * at <code>index</code>, and adding each instruction encountered into |
| * the subroutine <code>sub</code>. After this walk is complete, iterates |
| * over the exception handlers to ensure that we also include those byte |
| * codes which are reachable through an exception that may be thrown during |
| * the execution of the subroutine. Invoked from |
| * <code>markSubroutines()</code>. |
| * |
| * @param sub the subroutine whose instructions must be computed. |
| * @param index an instruction of this subroutine. |
| * @param anyvisited indexes of the already visited instructions, i.e. |
| * marked as part of this subroutine or any previously computed |
| * subroutine. |
| */ |
| private void markSubroutineWalk( |
| final Subroutine sub, |
| final int index, |
| final BitSet anyvisited) |
| { |
| if (LOGGING) { |
| log("markSubroutineWalk: sub=" + sub + " index=" + index); |
| } |
| |
| // First find those instructions reachable via normal execution |
| markSubroutineWalkDFS(sub, index, anyvisited); |
| |
| // Now, make sure we also include any applicable exception handlers |
| boolean loop = true; |
| while (loop) { |
| loop = false; |
| for (Iterator it = tryCatchBlocks.iterator(); it.hasNext();) { |
| TryCatchBlockNode trycatch = (TryCatchBlockNode) it.next(); |
| |
| if (LOGGING) { |
| // TODO use of default toString(). |
| log("Scanning try/catch " + trycatch); |
| } |
| |
| // If the handler has already been processed, skip it. |
| int handlerindex = instructions.indexOf(trycatch.handler); |
| if (sub.instructions.get(handlerindex)) { |
| continue; |
| } |
| |
| int startindex = instructions.indexOf(trycatch.start); |
| int endindex = instructions.indexOf(trycatch.end); |
| int nextbit = sub.instructions.nextSetBit(startindex); |
| if (nextbit != -1 && nextbit < endindex) { |
| if (LOGGING) { |
| log("Adding exception handler: " + startindex + '-' |
| + endindex + " due to " + nextbit + " handler " |
| + handlerindex); |
| } |
| markSubroutineWalkDFS(sub, handlerindex, anyvisited); |
| loop = true; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Performs a simple DFS of the instructions, assigning each to the |
| * subroutine <code>sub</code>. Starts from <code>index</code>. |
| * Invoked only by <code>markSubroutineWalk()</code>. |
| * |
| * @param sub the subroutine whose instructions must be computed. |
| * @param index an instruction of this subroutine. |
| * @param anyvisited indexes of the already visited instructions, i.e. |
| * marked as part of this subroutine or any previously computed |
| * subroutine. |
| */ |
| private void markSubroutineWalkDFS( |
| final Subroutine sub, |
| int index, |
| final BitSet anyvisited) |
| { |
| while (true) { |
| AbstractInsnNode node = instructions.get(index); |
| |
| // don't visit a node twice |
| if (sub.instructions.get(index)) { |
| return; |
| } |
| sub.instructions.set(index); |
| |
| // check for those nodes already visited by another subroutine |
| if (anyvisited.get(index)) { |
| dualCitizens.set(index); |
| if (LOGGING) { |
| log("Instruction #" + index + " is dual citizen."); |
| } |
| } |
| anyvisited.set(index); |
| |
| if (node.getType() == AbstractInsnNode.JUMP_INSN |
| && node.getOpcode() != JSR) |
| { |
| // we do not follow recursively called subroutines here; but any |
| // other sort of branch we do follow |
| JumpInsnNode jnode = (JumpInsnNode) node; |
| int destidx = instructions.indexOf(jnode.label); |
| markSubroutineWalkDFS(sub, destidx, anyvisited); |
| } |
| if (node.getType() == AbstractInsnNode.TABLESWITCH_INSN) { |
| TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node; |
| int destidx = instructions.indexOf(tsnode.dflt); |
| markSubroutineWalkDFS(sub, destidx, anyvisited); |
| for (int i = tsnode.labels.size() - 1; i >= 0; --i) { |
| LabelNode l = (LabelNode) tsnode.labels.get(i); |
| destidx = instructions.indexOf(l); |
| markSubroutineWalkDFS(sub, destidx, anyvisited); |
| } |
| } |
| if (node.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) { |
| LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node; |
| int destidx = instructions.indexOf(lsnode.dflt); |
| markSubroutineWalkDFS(sub, destidx, anyvisited); |
| for (int i = lsnode.labels.size() - 1; i >= 0; --i) { |
| LabelNode l = (LabelNode) lsnode.labels.get(i); |
| destidx = instructions.indexOf(l); |
| markSubroutineWalkDFS(sub, destidx, anyvisited); |
| } |
| } |
| |
| // check to see if this opcode falls through to the next instruction |
| // or not; if not, return. |
| switch (instructions.get(index).getOpcode()) { |
| case GOTO: |
| case RET: |
| case TABLESWITCH: |
| case LOOKUPSWITCH: |
| case IRETURN: |
| case LRETURN: |
| case FRETURN: |
| case DRETURN: |
| case ARETURN: |
| case RETURN: |
| case ATHROW: |
| /* |
| * note: this either returns from this subroutine, or a |
| * parent subroutine which invoked it |
| */ |
| return; |
| } |
| |
| // Use tail recursion here in the form of an outer while loop to |
| // avoid our stack growing needlessly: |
| index++; |
| } |
| } |
| |
| /** |
| * Creates the new instructions, inlining each instantiation of each |
| * subroutine until the code is fully elaborated. |
| */ |
| private void emitCode() { |
| LinkedList worklist = new LinkedList(); |
| // Create an instantiation of the "root" subroutine, which is just the |
| // main routine |
| worklist.add(new Instantiation(null, mainSubroutine)); |
| |
| // Emit instantiations of each subroutine we encounter, including the |
| // main subroutine |
| InsnList newInstructions = new InsnList(); |
| List newTryCatchBlocks = new ArrayList(); |
| List newLocalVariables = new ArrayList(); |
| while (!worklist.isEmpty()) { |
| Instantiation inst = (Instantiation) worklist.removeFirst(); |
| emitSubroutine(inst, |
| worklist, |
| newInstructions, |
| newTryCatchBlocks, |
| newLocalVariables); |
| } |
| instructions = newInstructions; |
| tryCatchBlocks = newTryCatchBlocks; |
| localVariables = newLocalVariables; |
| } |
| |
| /** |
| * Emits one instantiation of one subroutine, specified by |
| * <code>instant</code>. May add new instantiations that are invoked by |
| * this one to the <code>worklist</code> parameter, and new try/catch |
| * blocks to <code>newTryCatchBlocks</code>. |
| * |
| * @param instant the instantiation that must be performed. |
| * @param worklist list of the instantiations that remain to be done. |
| * @param newInstructions the instruction list to which the instantiated |
| * code must be appended. |
| * @param newTryCatchBlocks the exception handler list to which the |
| * instantiated handlers must be appended. |
| */ |
| 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) { |
| // TODO use of default toString(). |
| 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) { |
| // TODO use of default toString(). |
| 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) { |
| // TODO use of default toString(). |
| 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 static 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; |
| |
| private 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 #rangeLabel |
| */ |
| 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); |
| } |
| } |
| } |