You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2018/11/29 00:34:14 UTC
[27/46] tapestry-5 git commit: TAP5-2588: upgrading from ASM 6 to 7
for Java 9+ support
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1c71aec7/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java
----------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java
old mode 100644
new mode 100755
index c483e81..dc43c0f
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/JSRInlinerAdapter.java
@@ -1,48 +1,43 @@
-/***
- * ASM: a very small and fast Java bytecode manipulation framework
- * Copyright (c) 2000-2011 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.
- */
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 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 org.apache.tapestry5.internal.plastic.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 org.apache.tapestry5.internal.plastic.asm.Label;
import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
import org.apache.tapestry5.internal.plastic.asm.Opcodes;
-import org.apache.tapestry5.internal.plastic.asm.Type;
import org.apache.tapestry5.internal.plastic.asm.tree.AbstractInsnNode;
import org.apache.tapestry5.internal.plastic.asm.tree.InsnList;
import org.apache.tapestry5.internal.plastic.asm.tree.InsnNode;
@@ -55,698 +50,514 @@ import org.apache.tapestry5.internal.plastic.asm.tree.TableSwitchInsnNode;
import org.apache.tapestry5.internal.plastic.asm.tree.TryCatchBlockNode;
/**
- * A {@link org.objectweb.asm.MethodVisitor} that removes JSR instructions and
- * inlines the referenced subroutines.
- *
- * <b>Explanation of how it works</b> TODO
- *
+ * A {@link org.apache.tapestry5.internal.plastic.asm.MethodVisitor} that removes JSR instructions and inlines the
+ * referenced subroutines.
+ *
* @author Niko Matsakis
*/
+// DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
public class JSRInlinerAdapter extends MethodNode implements Opcodes {
- private static final boolean LOGGING = false;
-
- /**
- * For each label that is jumped to by a JSR, we create a BitSet instance.
- */
- private final Map<LabelNode, BitSet> subroutineHeads = new HashMap<LabelNode, BitSet>();
-
- /**
- * 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 BitSet mainSubroutine = new BitSet();
-
- /**
- * This BitSet contains the index of every instruction that belongs to more
- * than one subroutine. This should not happen often.
- */
- final BitSet dualCitizens = new BitSet();
-
- /**
- * Creates a new JSRInliner. <i>Subclasses must not use this
- * constructor</i>. Instead, they must use the
- * {@link #JSRInlinerAdapter(int, MethodVisitor, int, String, String, String, String[])}
- * version.
- *
- * @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>.
- * @throws IllegalStateException
- * If a subclass calls this constructor.
- */
- public JSRInlinerAdapter(final MethodVisitor mv, final int access,
- final String name, final String desc, final String signature,
- final String[] exceptions) {
- this(Opcodes.ASM6, mv, access, name, desc, signature, exceptions);
- if (getClass() != JSRInlinerAdapter.class) {
- throw new IllegalStateException();
- }
+ /**
+ * The instructions that belong to the main "subroutine". Bit i is set iff instruction at index i
+ * belongs to this main "subroutine".
+ */
+ private final BitSet mainSubroutineInsns = new BitSet();
+
+ /**
+ * The instructions that belong to each subroutine. For each label which is the target of a JSR
+ * instruction, bit i of the corresponding BitSet in this map is set iff instruction at index i
+ * belongs to this subroutine.
+ */
+ private final Map<LabelNode, BitSet> subroutinesInsns = new HashMap<LabelNode, BitSet>();
+
+ /**
+ * The instructions that belong to more that one subroutine. Bit i is set iff instruction at index
+ * i belongs to more than one subroutine.
+ */
+ final BitSet sharedSubroutineInsns = new BitSet();
+
+ /**
+ * Constructs a new {@link JSRInlinerAdapter}. <i>Subclasses must not use this constructor</i>.
+ * Instead, they must use the {@link #JSRInlinerAdapter(int, MethodVisitor, int, String, String,
+ * String, String[])} version.
+ *
+ * @param methodVisitor the method visitor to send the resulting inlined method code to, or <code>
+ * null</code>.
+ * @param access the method's access flags.
+ * @param name the method's name.
+ * @param descriptor the method's descriptor.
+ * @param signature the method's signature. May be {@literal null}.
+ * @param exceptions the internal names of the method's exception classes. May be {@literal null}.
+ * @throws IllegalStateException if a subclass calls this constructor.
+ */
+ public JSRInlinerAdapter(
+ final MethodVisitor methodVisitor,
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final String[] exceptions) {
+ this(Opcodes.ASM7, methodVisitor, access, name, descriptor, signature, exceptions);
+ if (getClass() != JSRInlinerAdapter.class) {
+ throw new IllegalStateException();
}
-
- /**
- * Creates a new JSRInliner.
- *
- * @param api
- * the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
- * @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>.
- */
- protected JSRInlinerAdapter(final int api, final MethodVisitor mv,
- final int access, final String name, final String desc,
- final String signature, final String[] exceptions) {
- super(api, access, name, desc, signature, exceptions);
- this.mv = mv;
+ }
+
+ /**
+ * Constructs a new {@link JSRInlinerAdapter}.
+ *
+ * @param api the ASM API version implemented by this visitor. Must be one of {@link
+ * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+ * @param methodVisitor the method visitor to send the resulting inlined method code to, or <code>
+ * null</code>.
+ * @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 descriptor the method's descriptor.
+ * @param signature the method's signature. May be {@literal null}.
+ * @param exceptions the internal names of the method's exception classes. May be {@literal null}.
+ */
+ protected JSRInlinerAdapter(
+ final int api,
+ final MethodVisitor methodVisitor,
+ final int access,
+ final String name,
+ final String descriptor,
+ final String signature,
+ final String[] exceptions) {
+ super(api, access, name, descriptor, signature, exceptions);
+ this.mv = methodVisitor;
+ }
+
+ @Override
+ public void visitJumpInsn(final int opcode, final Label label) {
+ super.visitJumpInsn(opcode, label);
+ LabelNode labelNode = ((JumpInsnNode) instructions.getLast()).label;
+ if (opcode == JSR && !subroutinesInsns.containsKey(labelNode)) {
+ subroutinesInsns.put(labelNode, new BitSet());
}
-
- /**
- * Detects a JSR instruction and sets a flag to indicate we will need to do
- * inlining.
- */
- @Override
- 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 BitSet());
- }
+ }
+
+ @Override
+ public void visitEnd() {
+ if (!subroutinesInsns.isEmpty()) {
+ // If the code contains at least one JSR instruction, inline the subroutines.
+ findSubroutinesInsns();
+ emitCode();
}
-
- /**
- * If any JSRs were seen, triggers the inlining process. Otherwise, forwards
- * the byte codes untouched.
- */
- @Override
- public void visitEnd() {
- if (!subroutineHeads.isEmpty()) {
- markSubroutines();
- if (LOGGING) {
- log(mainSubroutine.toString());
- Iterator<BitSet> it = subroutineHeads.values().iterator();
- while (it.hasNext()) {
- BitSet sub = it.next();
- log(sub.toString());
- }
- }
- emitCode();
+ if (mv != null) {
+ accept(mv);
+ }
+ }
+
+ /** Determines, for each instruction, to which subroutine(s) it belongs. */
+ private void findSubroutinesInsns() {
+ // Find the instructions that belong to main subroutine.
+ BitSet visitedInsns = new BitSet();
+ findSubroutineInsns(0, mainSubroutineInsns, visitedInsns);
+ // For each subroutine, find the instructions that belong to this subroutine.
+ for (Map.Entry<LabelNode, BitSet> entry : subroutinesInsns.entrySet()) {
+ LabelNode jsrLabelNode = entry.getKey();
+ BitSet subroutineInsns = entry.getValue();
+ findSubroutineInsns(instructions.indexOf(jsrLabelNode), subroutineInsns, visitedInsns);
+ }
+ }
+
+ /**
+ * Finds the instructions that belong to the subroutine starting at the given instruction index.
+ * For this the control flow graph is visited with a depth first search (this includes the normal
+ * control flow and the exception handlers).
+ *
+ * @param startInsnIndex the index of the first instruction of the subroutine.
+ * @param subroutineInsns where the indices of the instructions of the subroutine must be stored.
+ * @param visitedInsns the indices of the instructions that have been visited so far (including in
+ * previous calls to this method). This bitset is updated by this method each time a new
+ * instruction is visited. It is used to make sure each instruction is visited at most once.
+ */
+ private void findSubroutineInsns(
+ final int startInsnIndex, final BitSet subroutineInsns, final BitSet visitedInsns) {
+ // First find the instructions reachable via normal execution.
+ findReachableInsns(startInsnIndex, subroutineInsns, visitedInsns);
+
+ // Then find the instructions reachable via the applicable exception handlers.
+ while (true) {
+ boolean applicableHandlerFound = false;
+ for (TryCatchBlockNode tryCatchBlockNode : tryCatchBlocks) {
+ // If the handler has already been processed, skip it.
+ int handlerIndex = instructions.indexOf(tryCatchBlockNode.handler);
+ if (subroutineInsns.get(handlerIndex)) {
+ continue;
}
- // Forward the translate opcodes on if appropriate:
- if (mv != null) {
- accept(mv);
+ // If an instruction in the exception handler range belongs to the subroutine, the handler
+ // can be reached from the routine, and its instructions must be added to the subroutine.
+ int startIndex = instructions.indexOf(tryCatchBlockNode.start);
+ int endIndex = instructions.indexOf(tryCatchBlockNode.end);
+ int firstSubroutineInsnAfterTryCatchStart = subroutineInsns.nextSetBit(startIndex);
+ if (firstSubroutineInsnAfterTryCatchStart >= startIndex
+ && firstSubroutineInsnAfterTryCatchStart < endIndex) {
+ findReachableInsns(handlerIndex, subroutineInsns, visitedInsns);
+ applicableHandlerFound = true;
}
+ }
+ // If an applicable exception handler has been found, other handlers may become applicable, so
+ // we must examine them again.
+ if (!applicableHandlerFound) {
+ return;
+ }
}
-
- /**
- * 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<Map.Entry<LabelNode, BitSet>> it = subroutineHeads
- .entrySet().iterator(); it.hasNext();) {
- Map.Entry<LabelNode, BitSet> entry = it.next();
- LabelNode lab = entry.getKey();
- BitSet sub = entry.getValue();
- int index = instructions.indexOf(lab);
- markSubroutineWalk(sub, index, anyvisited);
+ }
+
+ /**
+ * Finds the instructions that are reachable from the given instruction, without following any JSR
+ * instruction nor any exception handler. For this the control flow graph is visited with a depth
+ * first search.
+ *
+ * @param insnIndex the index of an instruction of the subroutine.
+ * @param subroutineInsns where the indices of the instructions of the subroutine must be stored.
+ * @param visitedInsns the indices of the instructions that have been visited so far (including in
+ * previous calls to this method). This bitset is updated by this method each time a new
+ * instruction is visited. It is used to make sure each instruction is visited at most once.
+ */
+ private void findReachableInsns(
+ final int insnIndex, final BitSet subroutineInsns, final BitSet visitedInsns) {
+ int currentInsnIndex = insnIndex;
+ // We implicitly assume below that execution can always fall through to the next instruction
+ // after a JSR. But a subroutine may never return, in which case the code after the JSR is
+ // unreachable and can be anything. In particular, it can seem to fall off the end of the
+ // method, so we must handle this case here (we could instead detect whether execution can
+ // return or not from a JSR, but this is more complicated).
+ while (currentInsnIndex < instructions.size()) {
+ // Visit each instruction at most once.
+ if (subroutineInsns.get(currentInsnIndex)) {
+ return;
+ }
+ subroutineInsns.set(currentInsnIndex);
+
+ // Check if this instruction has already been visited by another subroutine.
+ if (visitedInsns.get(currentInsnIndex)) {
+ sharedSubroutineInsns.set(currentInsnIndex);
+ }
+ visitedInsns.set(currentInsnIndex);
+
+ AbstractInsnNode currentInsnNode = instructions.get(currentInsnIndex);
+ if (currentInsnNode.getType() == AbstractInsnNode.JUMP_INSN
+ && currentInsnNode.getOpcode() != JSR) {
+ // Don't follow JSR instructions in the control flow graph.
+ JumpInsnNode jumpInsnNode = (JumpInsnNode) currentInsnNode;
+ findReachableInsns(instructions.indexOf(jumpInsnNode.label), subroutineInsns, visitedInsns);
+ } else if (currentInsnNode.getType() == AbstractInsnNode.TABLESWITCH_INSN) {
+ TableSwitchInsnNode tableSwitchInsnNode = (TableSwitchInsnNode) currentInsnNode;
+ findReachableInsns(
+ instructions.indexOf(tableSwitchInsnNode.dflt), subroutineInsns, visitedInsns);
+ for (LabelNode labelNode : tableSwitchInsnNode.labels) {
+ findReachableInsns(instructions.indexOf(labelNode), subroutineInsns, visitedInsns);
}
+ } else if (currentInsnNode.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) {
+ LookupSwitchInsnNode lookupSwitchInsnNode = (LookupSwitchInsnNode) currentInsnNode;
+ findReachableInsns(
+ instructions.indexOf(lookupSwitchInsnNode.dflt), subroutineInsns, visitedInsns);
+ for (LabelNode labelNode : lookupSwitchInsnNode.labels) {
+ findReachableInsns(instructions.indexOf(labelNode), subroutineInsns, visitedInsns);
+ }
+ }
+
+ // Check if this instruction falls through to the next instruction; if not, return.
+ switch (instructions.get(currentInsnIndex).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 from a parent subroutine.
+ return;
+ default:
+ // Go to the next instruction.
+ currentInsnIndex++;
+ break;
+ }
}
-
- /**
- * 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 BitSet sub, final int index,
- final BitSet anyvisited) {
- if (LOGGING) {
- log("markSubroutineWalk: sub=" + sub + " index=" + index);
+ }
+
+ /**
+ * Creates the new instructions, inlining each instantiation of each subroutine until the code is
+ * fully elaborated.
+ */
+ private void emitCode() {
+ LinkedList<Instantiation> worklist = new LinkedList<Instantiation>();
+ // Create an instantiation of the main "subroutine", which is just the main routine.
+ worklist.add(new Instantiation(null, mainSubroutineInsns));
+
+ // Emit instantiations of each subroutine we encounter, including the main subroutine.
+ InsnList newInstructions = new InsnList();
+ List<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<TryCatchBlockNode>();
+ List<LocalVariableNode> newLocalVariables = new ArrayList<LocalVariableNode>();
+ while (!worklist.isEmpty()) {
+ Instantiation instantiation = worklist.removeFirst();
+ emitInstantiation(
+ instantiation, worklist, newInstructions, newTryCatchBlocks, newLocalVariables);
+ }
+ instructions = newInstructions;
+ tryCatchBlocks = newTryCatchBlocks;
+ localVariables = newLocalVariables;
+ }
+
+ /**
+ * Emits an instantiation of a subroutine, specified by <code>instantiation</code>. May add new
+ * instantiations that are invoked by this one to the <code>worklist</code>, and new try/catch
+ * blocks to <code>newTryCatchBlocks</code>.
+ *
+ * @param instantiation 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.
+ * @param newLocalVariables the local variables list to which the instantiated local variables
+ * must be appended.
+ */
+ private void emitInstantiation(
+ final Instantiation instantiation,
+ final List<Instantiation> worklist,
+ final InsnList newInstructions,
+ final List<TryCatchBlockNode> newTryCatchBlocks,
+ final List<LocalVariableNode> newLocalVariables) {
+ LabelNode previousLabelNode = null;
+ for (int i = 0; i < instructions.size(); ++i) {
+ AbstractInsnNode insnNode = instructions.get(i);
+ if (insnNode.getType() == AbstractInsnNode.LABEL) {
+ // Always clone all labels, while avoiding to add the same label more than once.
+ LabelNode labelNode = (LabelNode) insnNode;
+ LabelNode clonedLabelNode = instantiation.getClonedLabel(labelNode);
+ if (clonedLabelNode != previousLabelNode) {
+ newInstructions.add(clonedLabelNode);
+ previousLabelNode = clonedLabelNode;
}
-
- // 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<TryCatchBlockNode> it = tryCatchBlocks.iterator(); it
- .hasNext();) {
- TryCatchBlockNode trycatch = 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.get(handlerindex)) {
- continue;
- }
-
- int startindex = instructions.indexOf(trycatch.start);
- int endindex = instructions.indexOf(trycatch.end);
- int nextbit = sub.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;
- }
+ } else if (instantiation.findOwner(i) == instantiation) {
+ // Don't emit instructions that were already emitted by an ancestor subroutine. 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 (insnNode.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 oldest
+ // instantiation that claims to own this instruction.
+ LabelNode retLabel = null;
+ for (Instantiation retLabelOwner = instantiation;
+ retLabelOwner != null;
+ retLabelOwner = retLabelOwner.parent) {
+ if (retLabelOwner.subroutineInsns.get(i)) {
+ retLabel = retLabelOwner.returnLabel;
}
+ }
+ if (retLabel == null) {
+ // This is only possible if the mainSubroutine owns a RET instruction, which should
+ // never happen for verifiable code.
+ throw new IllegalArgumentException(
+ "Instruction #" + i + " is a RET not owned by any subroutine");
+ }
+ newInstructions.add(new JumpInsnNode(GOTO, retLabel));
+ } else if (insnNode.getOpcode() == JSR) {
+ LabelNode jsrLabelNode = ((JumpInsnNode) insnNode).label;
+ BitSet subroutineInsns = subroutinesInsns.get(jsrLabelNode);
+ Instantiation newInstantiation = new Instantiation(instantiation, subroutineInsns);
+ LabelNode clonedJsrLabelNode = newInstantiation.getClonedLabelForJumpInsn(jsrLabelNode);
+ // Replace the JSR instruction with a GOTO to the instantiated subroutine, and push NULL
+ // for what was once the return address value. This hack allows us to avoid doing any sort
+ // of data flow analysis to figure out which instructions manipulate the old return
+ // address value pointer which is now known to be unneeded.
+ newInstructions.add(new InsnNode(ACONST_NULL));
+ newInstructions.add(new JumpInsnNode(GOTO, clonedJsrLabelNode));
+ newInstructions.add(newInstantiation.returnLabel);
+ // Insert this new instantiation into the queue to be emitted later.
+ worklist.add(newInstantiation);
+ } else {
+ newInstructions.add(insnNode.clone(instantiation));
}
+ }
}
- /**
- * 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 BitSet sub, int index,
- final BitSet anyvisited) {
- while (true) {
- AbstractInsnNode node = instructions.get(index);
-
- // don't visit a node twice
- if (sub.get(index)) {
- return;
- }
- sub.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 = 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 = 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++;
-
- // We implicitly assumed above that execution can always fall
- // through to the next instruction after a JSR. But a subroutine may
- // never return, in which case the code after the JSR is unreachable
- // and can be anything. In particular, it can seem to fall off the
- // end of the method, so we must handle this case here (we could
- // instead detect whether execution can return or not from a JSR,
- // but this is more complicated).
- if (index >= instructions.size()) {
- return;
- }
+ // Emit the try/catch blocks that are relevant for this instantiation.
+ for (TryCatchBlockNode tryCatchBlockNode : tryCatchBlocks) {
+ final LabelNode start = instantiation.getClonedLabel(tryCatchBlockNode.start);
+ final LabelNode end = instantiation.getClonedLabel(tryCatchBlockNode.end);
+ if (start != end) {
+ final LabelNode handler =
+ instantiation.getClonedLabelForJumpInsn(tryCatchBlockNode.handler);
+ if (start == null || end == null || handler == null) {
+ throw new AssertionError("Internal error!");
}
+ newTryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, tryCatchBlockNode.type));
+ }
}
- /**
- * Creates the new instructions, inlining each instantiation of each
- * subroutine until the code is fully elaborated.
- */
- private void emitCode() {
- LinkedList<Instantiation> worklist = new LinkedList<Instantiation>();
- // 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<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<TryCatchBlockNode>();
- List<LocalVariableNode> newLocalVariables = new ArrayList<LocalVariableNode>();
- while (!worklist.isEmpty()) {
- Instantiation inst = worklist.removeFirst();
- emitSubroutine(inst, worklist, newInstructions, newTryCatchBlocks,
- newLocalVariables);
- }
- instructions = newInstructions;
- tryCatchBlocks = newTryCatchBlocks;
- localVariables = newLocalVariables;
+ // Emit the local variable nodes that are relevant for this instantiation.
+ for (LocalVariableNode localVariableNode : localVariables) {
+ final LabelNode start = instantiation.getClonedLabel(localVariableNode.start);
+ final LabelNode end = instantiation.getClonedLabel(localVariableNode.end);
+ if (start != end) {
+ newLocalVariables.add(
+ new LocalVariableNode(
+ localVariableNode.name,
+ localVariableNode.desc,
+ localVariableNode.signature,
+ start,
+ end,
+ localVariableNode.index));
+ }
}
+ }
+
+ /** An instantiation of a subroutine. */
+ private class Instantiation extends AbstractMap<LabelNode, LabelNode> {
/**
- * 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.
+ * The instantiation from which this one was created (or {@literal null} for the instantiation
+ * of the main "subroutine").
*/
- private void emitSubroutine(final Instantiation instant,
- final List<Instantiation> worklist, final InsnList newInstructions,
- final List<TryCatchBlockNode> newTryCatchBlocks,
- final List<LocalVariableNode> newLocalVariables) {
- LabelNode duplbl = null;
-
- if (LOGGING) {
- log("--------------------------------------------------------");
- log("Emitting instantiation of subroutine " + instant.subroutine);
- }
+ final Instantiation parent;
- // 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;
- }
+ /**
+ * The original instructions that belong to the subroutine which is instantiated. Bit i is set
+ * iff instruction at index i belongs to this subroutine.
+ */
+ final BitSet subroutineInsns;
- // 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;
- }
+ /**
+ * A map from labels from the original code to labels pointing at code specific to this
+ * instantiation, for use in remapping try/catch blocks, as well as jumps.
+ *
+ * <p>Note that in the presence of instructions belonging to several subroutines, we map the
+ * target label of a GOTO to the label used by the oldest instantiation (parent instantiations
+ * are older than their children). This avoids code duplication during inlining in most cases.
+ */
+ final Map<LabelNode, LabelNode> clonedLabels;
- if (LOGGING) {
- log("Emitting inst #" + i);
- }
+ /** The return label for this instantiation, to which all original returns will be mapped. */
+ final LabelNode returnLabel;
- 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.get(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;
- BitSet sub = 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));
- }
+ Instantiation(final Instantiation parent, final BitSet subroutineInsns) {
+ for (Instantiation instantiation = parent;
+ instantiation != null;
+ instantiation = instantiation.parent) {
+ if (instantiation.subroutineInsns == subroutineInsns) {
+ throw new IllegalArgumentException("Recursive invocation of " + subroutineInsns);
}
-
- // Emit try/catch blocks that are relevant to this method.
- for (Iterator<TryCatchBlockNode> it = tryCatchBlocks.iterator(); it
- .hasNext();) {
- TryCatchBlockNode trycatch = 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));
+ }
+
+ this.parent = parent;
+ this.subroutineInsns = subroutineInsns;
+ this.returnLabel = parent == null ? null : new LabelNode();
+ this.clonedLabels = new HashMap<LabelNode, LabelNode>();
+
+ // Create a clone of each label in the original code of the subroutine. Note that we collapse
+ // labels which point at the same instruction into one.
+ LabelNode clonedLabelNode = null;
+ for (int insnIndex = 0; insnIndex < instructions.size(); insnIndex++) {
+ AbstractInsnNode insnNode = instructions.get(insnIndex);
+ if (insnNode.getType() == AbstractInsnNode.LABEL) {
+ LabelNode labelNode = (LabelNode) insnNode;
+ // If we already have a label pointing at this spot, don't recreate it.
+ if (clonedLabelNode == null) {
+ clonedLabelNode = new LabelNode();
+ }
+ clonedLabels.put(labelNode, clonedLabelNode);
+ } else if (findOwner(insnIndex) == this) {
+ // We will emit this instruction, so clear the duplicateLabelNode flag since the next
+ // Label will refer to a distinct instruction.
+ clonedLabelNode = null;
}
+ }
+ }
- for (Iterator<LocalVariableNode> it = localVariables.iterator(); it
- .hasNext();) {
- LocalVariableNode lvnode = 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));
+ /**
+ * Returns the "owner" of a particular instruction relative to this instantiation: the owner
+ * refers to the Instantiation which will emit the version of this instruction that we will
+ * execute.
+ *
+ * <p>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.
+ *
+ * <p>Sometimes, however, an instruction can belong to multiple subroutines; this is called a
+ * shared instruction, and occurs when multiple subroutines branch to common points of control.
+ * In this case, the owner is the oldest instantiation which owns the instruction in question
+ * (parent instantiations are older than their children).
+ *
+ * @param insnIndex the index of an instruction in the original code.
+ * @return the "owner" of a particular instruction relative to this instantiation.
+ */
+ Instantiation findOwner(final int insnIndex) {
+ if (!subroutineInsns.get(insnIndex)) {
+ return null;
+ }
+ if (!sharedSubroutineInsns.get(insnIndex)) {
+ return this;
+ }
+ Instantiation owner = this;
+ for (Instantiation instantiation = parent;
+ instantiation != null;
+ instantiation = instantiation.parent) {
+ if (instantiation.subroutineInsns.get(insnIndex)) {
+ owner = instantiation;
}
+ }
+ return owner;
}
- private static void log(final String str) {
- System.err.println(str);
+ /**
+ * Returns the clone of the given original label that is appropriate for use in a jump
+ * instruction.
+ *
+ * @param labelNode a label of the original code.
+ * @return a clone of the given label for use in a jump instruction in the inlined code.
+ */
+ LabelNode getClonedLabelForJumpInsn(final LabelNode labelNode) {
+ // findOwner should never return null, because owner is null only if an instruction cannot be
+ // reached from this subroutine.
+ return findOwner(instructions.indexOf(labelNode)).clonedLabels.get(labelNode);
}
/**
- * 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.
+ * Returns the clone of the given original label that is appropriate for use by a try/catch
+ * block or a variable annotation.
+ *
+ * @param labelNode a label of the original code.
+ * @return a clone of the given label for use by a try/catch block or a variable annotation in
+ * the inlined code.
*/
- private class Instantiation extends AbstractMap<LabelNode, LabelNode> {
-
- /**
- * Previous instantiations; the stack must be statically predictable to
- * be inlinable.
- */
- final Instantiation previous;
-
- /**
- * The subroutine this is an instantiation of.
- */
- public final BitSet 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<LabelNode, LabelNode> rangeTable = new HashMap<LabelNode, LabelNode>();
-
- /**
- * All returns for this instantiation will be mapped to this label
- */
- public final LabelNode returnLabel;
-
- Instantiation(final Instantiation prev, final BitSet 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.get(i)) {
- return null;
- }
- if (!dualCitizens.get(i)) {
- return this;
- }
- Instantiation own = this;
- for (Instantiation p = previous; p != null; p = p.previous) {
- if (p.subroutine.get(i)) {
- own = p;
- }
- }
- return own;
- }
+ LabelNode getClonedLabel(final LabelNode labelNode) {
+ return clonedLabels.get(labelNode);
+ }
- /**
- * 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 owner.rangeTable.get(l);
- }
+ // AbstractMap implementation
- /**
- * 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 rangeTable.get(l);
- }
+ @Override
+ public Set<Map.Entry<LabelNode, LabelNode>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
- // AbstractMap implementation
+ @Override
+ public LabelNode get(final Object key) {
+ return getClonedLabelForJumpInsn((LabelNode) key);
+ }
- @Override
- public Set<Map.Entry<LabelNode, LabelNode>> entrySet() {
- return null;
- }
+ @Override
+ public boolean equals(final Object other) {
+ throw new UnsupportedOperationException();
+ }
- @Override
- public LabelNode get(final Object o) {
- return gotoLabel((LabelNode) o);
- }
+ @Override
+ public int hashCode() {
+ throw new UnsupportedOperationException();
}
+ }
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/1c71aec7/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java
----------------------------------------------------------------------
diff --git a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java
old mode 100644
new mode 100755
index a7d2f1d..08657ee
--- a/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java
+++ b/plastic/src/external/java/org/apache/tapestry5/internal/plastic/asm/commons/LocalVariablesSorter.java
@@ -1,32 +1,30 @@
-/***
- * ASM: a very small and fast Java bytecode manipulation framework
- * Copyright (c) 2000-2011 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.
- */
+// ASM: a very small and fast Java bytecode manipulation framework
+// Copyright (c) 2000-2011 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 org.apache.tapestry5.internal.plastic.asm.commons;
import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
@@ -37,331 +35,317 @@ import org.apache.tapestry5.internal.plastic.asm.Type;
import org.apache.tapestry5.internal.plastic.asm.TypePath;
/**
- * A {@link MethodVisitor} that renumbers local variables in their order of
- * appearance. This adapter allows one to easily add new local variables to a
- * method. It may be used by inheriting from this class, but the preferred way
- * of using it is via delegation: the next visitor in the chain can indeed add
- * new locals when needed by calling {@link #newLocal} on this adapter (this
- * requires a reference back to this {@link LocalVariablesSorter}).
- *
+ * A {@link MethodVisitor} that renumbers local variables in their order of appearance. This adapter
+ * allows one to easily add new local variables to a method. It may be used by inheriting from this
+ * class, but the preferred way of using it is via delegation: the next visitor in the chain can
+ * indeed add new locals when needed by calling {@link #newLocal} on this adapter (this requires a
+ * reference back to this {@link LocalVariablesSorter}).
+ *
* @author Chris Nokleberg
* @author Eugene Kuleshov
* @author Eric Bruneton
*/
public class LocalVariablesSorter extends MethodVisitor {
- private static final Type OBJECT_TYPE = Type
- .getObjectType("java/lang/Object");
-
- /**
- * Mapping from old to new local variable indexes. A local variable at index
- * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
- * index i of size 2 is remapped to 'mapping[2*i+1]'.
- */
- private int[] mapping = new int[40];
-
- /**
- * Array used to store stack map local variable types after remapping.
- */
- private Object[] newLocals = new Object[20];
-
- /**
- * Index of the first local variable, after formal parameters.
- */
- protected final int firstLocal;
-
- /**
- * Index of the next local variable to be created by {@link #newLocal}.
- */
- protected int nextLocal;
-
- /**
- * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use
- * this constructor</i>. Instead, they must use the
- * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version.
- *
- * @param access
- * access flags of the adapted method.
- * @param desc
- * the method's descriptor (see {@link Type Type}).
- * @param mv
- * the method visitor to which this adapter delegates calls.
- * @throws IllegalStateException
- * If a subclass calls this constructor.
- */
- public LocalVariablesSorter(final int access, final String desc,
- final MethodVisitor mv) {
- this(Opcodes.ASM6, access, desc, mv);
- if (getClass() != LocalVariablesSorter.class) {
- throw new IllegalStateException();
- }
- }
-
- /**
- * Creates a new {@link LocalVariablesSorter}.
- *
- * @param api
- * the ASM API version implemented by this visitor. Must be one
- * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
- * @param access
- * access flags of the adapted method.
- * @param desc
- * the method's descriptor (see {@link Type Type}).
- * @param mv
- * the method visitor to which this adapter delegates calls.
- */
- protected LocalVariablesSorter(final int api, final int access,
- final String desc, final MethodVisitor mv) {
- super(api, mv);
- Type[] args = Type.getArgumentTypes(desc);
- nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
- for (int i = 0; i < args.length; i++) {
- nextLocal += args[i].getSize();
- }
- firstLocal = nextLocal;
+ /** The type of the java.lang.Object class. */
+ private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
+
+ /**
+ * The mapping from old to new local variable indices. A local variable at index i of size 1 is
+ * remapped to 'mapping[2*i]', while a local variable at index i of size 2 is remapped to
+ * 'mapping[2*i+1]'.
+ */
+ private int[] remappedVariableIndices = new int[40];
+
+ /**
+ * The local variable types after remapping. The format of this array is the same as in {@link
+ * MethodVisitor#visitFrame}, except that long and double types use two slots.
+ */
+ private Object[] remappedLocalTypes = new Object[20];
+
+ /** The index of the first local variable, after formal parameters. */
+ protected final int firstLocal;
+
+ /** The index of the next local variable to be created by {@link #newLocal}. */
+ protected int nextLocal;
+
+ /**
+ * Constructs a new {@link LocalVariablesSorter}. <i>Subclasses must not use this constructor</i>.
+ * Instead, they must use the {@link #LocalVariablesSorter(int, int, String, MethodVisitor)}
+ * version.
+ *
+ * @param access access flags of the adapted method.
+ * @param descriptor the method's descriptor (see {@link Type}).
+ * @param methodVisitor the method visitor to which this adapter delegates calls.
+ * @throws IllegalStateException if a subclass calls this constructor.
+ */
+ public LocalVariablesSorter(
+ final int access, final String descriptor, final MethodVisitor methodVisitor) {
+ this(Opcodes.ASM7, access, descriptor, methodVisitor);
+ if (getClass() != LocalVariablesSorter.class) {
+ throw new IllegalStateException();
}
-
- @Override
- public void visitVarInsn(final int opcode, final int var) {
- Type type;
- switch (opcode) {
- case Opcodes.LLOAD:
- case Opcodes.LSTORE:
- type = Type.LONG_TYPE;
- break;
-
- case Opcodes.DLOAD:
- case Opcodes.DSTORE:
- type = Type.DOUBLE_TYPE;
- break;
-
- case Opcodes.FLOAD:
- case Opcodes.FSTORE:
- type = Type.FLOAT_TYPE;
- break;
-
- case Opcodes.ILOAD:
- case Opcodes.ISTORE:
- type = Type.INT_TYPE;
- break;
-
- default:
- // case Opcodes.ALOAD:
- // case Opcodes.ASTORE:
- // case RET:
- type = OBJECT_TYPE;
- break;
- }
- mv.visitVarInsn(opcode, remap(var, type));
+ }
+
+ /**
+ * Constructs a new {@link LocalVariablesSorter}.
+ *
+ * @param api the ASM API version implemented by this visitor. Must be one of {@link
+ * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
+ * @param access access flags of the adapted method.
+ * @param descriptor the method's descriptor (see {@link Type}).
+ * @param methodVisitor the method visitor to which this adapter delegates calls.
+ */
+ protected LocalVariablesSorter(
+ final int api, final int access, final String descriptor, final MethodVisitor methodVisitor) {
+ super(api, methodVisitor);
+ nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
+ for (Type argumentType : Type.getArgumentTypes(descriptor)) {
+ nextLocal += argumentType.getSize();
}
-
- @Override
- public void visitIincInsn(final int var, final int increment) {
- mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
+ firstLocal = nextLocal;
+ }
+
+ @Override
+ public void visitVarInsn(final int opcode, final int var) {
+ Type varType;
+ switch (opcode) {
+ case Opcodes.LLOAD:
+ case Opcodes.LSTORE:
+ varType = Type.LONG_TYPE;
+ break;
+ case Opcodes.DLOAD:
+ case Opcodes.DSTORE:
+ varType = Type.DOUBLE_TYPE;
+ break;
+ case Opcodes.FLOAD:
+ case Opcodes.FSTORE:
+ varType = Type.FLOAT_TYPE;
+ break;
+ case Opcodes.ILOAD:
+ case Opcodes.ISTORE:
+ varType = Type.INT_TYPE;
+ break;
+ case Opcodes.ALOAD:
+ case Opcodes.ASTORE:
+ case Opcodes.RET:
+ varType = OBJECT_TYPE;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid opcode " + opcode);
}
-
- @Override
- public void visitMaxs(final int maxStack, final int maxLocals) {
- mv.visitMaxs(maxStack, nextLocal);
+ super.visitVarInsn(opcode, remap(var, varType));
+ }
+
+ @Override
+ public void visitIincInsn(final int var, final int increment) {
+ super.visitIincInsn(remap(var, Type.INT_TYPE), increment);
+ }
+
+ @Override
+ public void visitMaxs(final int maxStack, final int maxLocals) {
+ super.visitMaxs(maxStack, nextLocal);
+ }
+
+ @Override
+ public void visitLocalVariable(
+ final String name,
+ final String descriptor,
+ final String signature,
+ final Label start,
+ final Label end,
+ final int index) {
+ int remappedIndex = remap(index, Type.getType(descriptor));
+ super.visitLocalVariable(name, descriptor, signature, start, end, remappedIndex);
+ }
+
+ @Override
+ public AnnotationVisitor visitLocalVariableAnnotation(
+ final int typeRef,
+ final TypePath typePath,
+ final Label[] start,
+ final Label[] end,
+ final int[] index,
+ final String descriptor,
+ final boolean visible) {
+ Type type = Type.getType(descriptor);
+ int[] remappedIndex = new int[index.length];
+ for (int i = 0; i < remappedIndex.length; ++i) {
+ remappedIndex[i] = remap(index[i], type);
}
-
- @Override
- public void visitLocalVariable(final String name, final String desc,
- final String signature, final Label start, final Label end,
- final int index) {
- int newIndex = remap(index, Type.getType(desc));
- mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
+ return super.visitLocalVariableAnnotation(
+ typeRef, typePath, start, end, remappedIndex, descriptor, visible);
+ }
+
+ @Override
+ public void visitFrame(
+ final int type,
+ final int numLocal,
+ final Object[] local,
+ final int numStack,
+ final Object[] stack) {
+ if (type != Opcodes.F_NEW) { // Uncompressed frame.
+ throw new IllegalArgumentException(
+ "LocalVariablesSorter only accepts expanded frames (see ClassReader.EXPAND_FRAMES)");
}
- @Override
- public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
- TypePath typePath, Label[] start, Label[] end, int[] index,
- String desc, boolean visible) {
- Type t = Type.getType(desc);
- int[] newIndex = new int[index.length];
- for (int i = 0; i < newIndex.length; ++i) {
- newIndex[i] = remap(index[i], t);
+ // Create a copy of remappedLocals.
+ Object[] oldRemappedLocals = new Object[remappedLocalTypes.length];
+ System.arraycopy(remappedLocalTypes, 0, oldRemappedLocals, 0, oldRemappedLocals.length);
+
+ updateNewLocals(remappedLocalTypes);
+
+ // Copy the types from 'local' to 'remappedLocals'. 'remappedLocals' already contains the
+ // variables added with 'newLocal'.
+ int oldVar = 0; // Old local variable index.
+ for (int i = 0; i < numLocal; ++i) {
+ Object localType = local[i];
+ if (localType != Opcodes.TOP) {
+ Type varType = OBJECT_TYPE;
+ if (localType == Opcodes.INTEGER) {
+ varType = Type.INT_TYPE;
+ } else if (localType == Opcodes.FLOAT) {
+ varType = Type.FLOAT_TYPE;
+ } else if (localType == Opcodes.LONG) {
+ varType = Type.LONG_TYPE;
+ } else if (localType == Opcodes.DOUBLE) {
+ varType = Type.DOUBLE_TYPE;
+ } else if (localType instanceof String) {
+ varType = Type.getObjectType((String) localType);
}
- return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end,
- newIndex, desc, visible);
+ setFrameLocal(remap(oldVar, varType), localType);
+ }
+ oldVar += localType == Opcodes.LONG || localType == Opcodes.DOUBLE ? 2 : 1;
}
- @Override
- public void visitFrame(final int type, final int nLocal,
- final Object[] local, final int nStack, final Object[] stack) {
- if (type != Opcodes.F_NEW) { // uncompressed frame
- throw new IllegalStateException(
- "ClassReader.accept() should be called with EXPAND_FRAMES flag");
- }
-
- // creates a copy of newLocals
- Object[] oldLocals = new Object[newLocals.length];
- System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
-
- updateNewLocals(newLocals);
-
- // copies types from 'local' to 'newLocals'
- // 'newLocals' already contains the variables added with 'newLocal'
-
- int index = 0; // old local variable index
- int number = 0; // old local variable number
- for (; number < nLocal; ++number) {
- Object t = local[number];
- int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
- if (t != Opcodes.TOP) {
- Type typ = OBJECT_TYPE;
- if (t == Opcodes.INTEGER) {
- typ = Type.INT_TYPE;
- } else if (t == Opcodes.FLOAT) {
- typ = Type.FLOAT_TYPE;
- } else if (t == Opcodes.LONG) {
- typ = Type.LONG_TYPE;
- } else if (t == Opcodes.DOUBLE) {
- typ = Type.DOUBLE_TYPE;
- } else if (t instanceof String) {
- typ = Type.getObjectType((String) t);
- }
- setFrameLocal(remap(index, typ), t);
- }
- index += size;
- }
-
- // removes TOP after long and double types as well as trailing TOPs
-
- index = 0;
- number = 0;
- for (int i = 0; index < newLocals.length; ++i) {
- Object t = newLocals[index++];
- if (t != null && t != Opcodes.TOP) {
- newLocals[i] = t;
- number = i + 1;
- if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {
- index += 1;
- }
- } else {
- newLocals[i] = Opcodes.TOP;
- }
- }
-
- // visits remapped frame
- mv.visitFrame(type, number, newLocals, nStack, stack);
-
- // restores original value of 'newLocals'
- newLocals = oldLocals;
+ // Remove TOP after long and double types as well as trailing TOPs.
+ oldVar = 0;
+ int newVar = 0;
+ int remappedNumLocal = 0;
+ while (oldVar < remappedLocalTypes.length) {
+ Object localType = remappedLocalTypes[oldVar];
+ oldVar += localType == Opcodes.LONG || localType == Opcodes.DOUBLE ? 2 : 1;
+ if (localType != null && localType != Opcodes.TOP) {
+ remappedLocalTypes[newVar++] = localType;
+ remappedNumLocal = newVar;
+ } else {
+ remappedLocalTypes[newVar++] = Opcodes.TOP;
+ }
}
- // -------------
-
- /**
- * Creates a new local variable of the given type.
- *
- * @param type
- * the type of the local variable to be created.
- * @return the identifier of the newly created local variable.
- */
- public int newLocal(final Type type) {
- Object t;
- switch (type.getSort()) {
- case Type.BOOLEAN:
- case Type.CHAR:
- case Type.BYTE:
- case Type.SHORT:
- case Type.INT:
- t = Opcodes.INTEGER;
- break;
- case Type.FLOAT:
- t = Opcodes.FLOAT;
- break;
- case Type.LONG:
- t = Opcodes.LONG;
- break;
- case Type.DOUBLE:
- t = Opcodes.DOUBLE;
- break;
- case Type.ARRAY:
- t = type.getDescriptor();
- break;
- // case Type.OBJECT:
- default:
- t = type.getInternalName();
- break;
- }
- int local = newLocalMapping(type);
- setLocalType(local, type);
- setFrameLocal(local, t);
- return local;
+ // Visit the remapped frame.
+ super.visitFrame(type, remappedNumLocal, remappedLocalTypes, numStack, stack);
+
+ // Restore the original value of 'remappedLocals'.
+ remappedLocalTypes = oldRemappedLocals;
+ }
+
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Constructs a new local variable of the given type.
+ *
+ * @param type the type of the local variable to be created.
+ * @return the identifier of the newly created local variable.
+ */
+ public int newLocal(final Type type) {
+ Object localType;
+ switch (type.getSort()) {
+ case Type.BOOLEAN:
+ case Type.CHAR:
+ case Type.BYTE:
+ case Type.SHORT:
+ case Type.INT:
+ localType = Opcodes.INTEGER;
+ break;
+ case Type.FLOAT:
+ localType = Opcodes.FLOAT;
+ break;
+ case Type.LONG:
+ localType = Opcodes.LONG;
+ break;
+ case Type.DOUBLE:
+ localType = Opcodes.DOUBLE;
+ break;
+ case Type.ARRAY:
+ localType = type.getDescriptor();
+ break;
+ case Type.OBJECT:
+ localType = type.getInternalName();
+ break;
+ default:
+ throw new AssertionError();
}
-
- /**
- * Notifies subclasses that a new stack map frame is being visited. The
- * array argument contains the stack map frame types corresponding to the
- * local variables added with {@link #newLocal}. This method can update
- * these types in place for the stack map frame being visited. The default
- * implementation of this method does nothing, i.e. a local variable added
- * with {@link #newLocal} will have the same type in all stack map frames.
- * But this behavior is not always the desired one, for instance if a local
- * variable is added in the middle of a try/catch block: the frame for the
- * exception handler should have a TOP type for this new local.
- *
- * @param newLocals
- * the stack map frame types corresponding to the local variables
- * added with {@link #newLocal} (and null for the others). The
- * format of this array is the same as in
- * {@link MethodVisitor#visitFrame}, except that long and double
- * types use two slots. The types for the current stack map frame
- * must be updated in place in this array.
- */
- protected void updateNewLocals(Object[] newLocals) {
- }
-
- /**
- * Notifies subclasses that a local variable has been added or remapped. The
- * default implementation of this method does nothing.
- *
- * @param local
- * a local variable identifier, as returned by {@link #newLocal
- * newLocal()}.
- * @param type
- * the type of the value being stored in the local variable.
- */
- protected void setLocalType(final int local, final Type type) {
+ int local = newLocalMapping(type);
+ setLocalType(local, type);
+ setFrameLocal(local, localType);
+ return local;
+ }
+
+ /**
+ * Notifies subclasses that a new stack map frame is being visited. The array argument contains
+ * the stack map frame types corresponding to the local variables added with {@link #newLocal}.
+ * This method can update these types in place for the stack map frame being visited. The default
+ * implementation of this method does nothing, i.e. a local variable added with {@link #newLocal}
+ * will have the same type in all stack map frames. But this behavior is not always the desired
+ * one, for instance if a local variable is added in the middle of a try/catch block: the frame
+ * for the exception handler should have a TOP type for this new local.
+ *
+ * @param newLocals the stack map frame types corresponding to the local variables added with
+ * {@link #newLocal} (and null for the others). The format of this array is the same as in
+ * {@link MethodVisitor#visitFrame}, except that long and double types use two slots. The
+ * types for the current stack map frame must be updated in place in this array.
+ */
+ protected void updateNewLocals(final Object[] newLocals) {
+ // The default implementation does nothing.
+ }
+
+ /**
+ * Notifies subclasses that a local variable has been added or remapped. The default
+ * implementation of this method does nothing.
+ *
+ * @param local a local variable identifier, as returned by {@link #newLocal}.
+ * @param type the type of the value being stored in the local variable.
+ */
+ protected void setLocalType(final int local, final Type type) {
+ // The default implementation does nothing.
+ }
+
+ private void setFrameLocal(final int local, final Object type) {
+ int numLocals = remappedLocalTypes.length;
+ if (local >= numLocals) {
+ Object[] newRemappedLocalTypes = new Object[Math.max(2 * numLocals, local + 1)];
+ System.arraycopy(remappedLocalTypes, 0, newRemappedLocalTypes, 0, numLocals);
+ remappedLocalTypes = newRemappedLocalTypes;
}
+ remappedLocalTypes[local] = type;
+ }
- private void setFrameLocal(final int local, final Object type) {
- int l = newLocals.length;
- if (local >= l) {
- Object[] a = new Object[Math.max(2 * l, local + 1)];
- System.arraycopy(newLocals, 0, a, 0, l);
- newLocals = a;
- }
- newLocals[local] = type;
+ private int remap(final int var, final Type type) {
+ if (var + type.getSize() <= firstLocal) {
+ return var;
}
-
- private int remap(final int var, final Type type) {
- if (var + type.getSize() <= firstLocal) {
- return var;
- }
- int key = 2 * var + type.getSize() - 1;
- int size = mapping.length;
- if (key >= size) {
- int[] newMapping = new int[Math.max(2 * size, key + 1)];
- System.arraycopy(mapping, 0, newMapping, 0, size);
- mapping = newMapping;
- }
- int value = mapping[key];
- if (value == 0) {
- value = newLocalMapping(type);
- setLocalType(value, type);
- mapping[key] = value + 1;
- } else {
- value--;
- }
- return value;
+ int key = 2 * var + type.getSize() - 1;
+ int size = remappedVariableIndices.length;
+ if (key >= size) {
+ int[] newRemappedVariableIndices = new int[Math.max(2 * size, key + 1)];
+ System.arraycopy(remappedVariableIndices, 0, newRemappedVariableIndices, 0, size);
+ remappedVariableIndices = newRemappedVariableIndices;
}
-
- protected int newLocalMapping(final Type type) {
- int local = nextLocal;
- nextLocal += type.getSize();
- return local;
+ int value = remappedVariableIndices[key];
+ if (value == 0) {
+ value = newLocalMapping(type);
+ setLocalType(value, type);
+ remappedVariableIndices[key] = value + 1;
+ } else {
+ value--;
}
+ return value;
+ }
+
+ protected int newLocalMapping(final Type type) {
+ int local = nextLocal;
+ nextLocal += type.getSize();
+ return local;
+ }
}