http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/JSRInlinerAdapter.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/JSRInlinerAdapter.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/JSRInlinerAdapter.java new file mode 100644 index 0000000..b64f371 --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/JSRInlinerAdapter.java @@ -0,0 +1,746 @@ +/*** + * 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.tajo.org.objectweb.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.tajo.org.objectweb.asm.Label; +import org.apache.tajo.org.objectweb.asm.MethodVisitor; +import org.apache.tajo.org.objectweb.asm.tree.InsnNode; +import org.apache.tajo.org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.apache.tajo.org.objectweb.asm.tree.MethodNode; +import org.apache.tajo.org.objectweb.asm.Opcodes; +import org.apache.tajo.org.objectweb.asm.tree.AbstractInsnNode; +import org.apache.tajo.org.objectweb.asm.tree.InsnList; +import org.apache.tajo.org.objectweb.asm.tree.JumpInsnNode; +import org.apache.tajo.org.objectweb.asm.tree.LabelNode; +import org.apache.tajo.org.objectweb.asm.tree.LocalVariableNode; +import org.apache.tajo.org.objectweb.asm.tree.TableSwitchInsnNode; +import org.apache.tajo.org.objectweb.asm.tree.TryCatchBlockNode; + +/** + * A {@link org.apache.tajo.org.objectweb.asm.MethodVisitor} 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; + + /** + * 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, org.apache.tajo.org.objectweb.asm.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 org.apache.tajo.org.objectweb.asm.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 org.apache.tajo.org.objectweb.asm.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) { + this(Opcodes.ASM4, mv, access, name, desc, signature, exceptions); + } + + /** + * Creates a new JSRInliner. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}. + * @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 org.apache.tajo.org.objectweb.asm.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 org.apache.tajo.org.objectweb.asm.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; + } + + /** + * 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()); + } + } + + /** + * 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(); + } + + // 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<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); + } + } + + /** + * 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); + } + + // 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; + } + } + } + } + + /** + * 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; + } + } + } + + /** + * 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; + } + + /** + * 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<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); + } + + // 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.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)); + } + } + + // 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)); + } + + 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)); + } + } + + private static void log(final String str) { + System.err.println(str); + } + + /** + * 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<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; + } + + /** + * 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); + } + + /** + * 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); + } + + // AbstractMap implementation + + @Override + public Set<Map.Entry<LabelNode, LabelNode>> entrySet() { + return null; + } + + @Override + public LabelNode get(final Object o) { + return gotoLabel((LabelNode) o); + } + } +}
http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/LocalVariablesSorter.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/LocalVariablesSorter.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/LocalVariablesSorter.java new file mode 100644 index 0000000..0fdcb1e --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/LocalVariablesSorter.java @@ -0,0 +1,361 @@ +/*** + * 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.tajo.org.objectweb.asm.commons; + +import org.apache.tajo.org.objectweb.asm.Label; +import org.apache.tajo.org.objectweb.asm.MethodVisitor; +import org.apache.tajo.org.objectweb.asm.Type; +import org.apache.tajo.org.objectweb.asm.Opcodes; + +/** + * A {@link org.apache.tajo.org.objectweb.asm.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; + + /** + * Indicates if at least one local variable has moved due to remapping. + */ + private boolean changed; + + /** + * 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. + */ + public LocalVariablesSorter(final int access, final String desc, + final MethodVisitor mv) { + this(Opcodes.ASM4, access, desc, mv); + } + + /** + * Creates a new {@link LocalVariablesSorter}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4}. + * @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; + } + + @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)); + } + + @Override + public void visitIincInsn(final int var, final int increment) { + mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); + } + + @Override + public void visitMaxs(final int maxStack, final int maxLocals) { + mv.visitMaxs(maxStack, nextLocal); + } + + @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); + } + + @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"); + } + + if (!changed) { // optimization for the case where mapping = identity + mv.visitFrame(type, nLocal, local, nStack, stack); + return; + } + + // 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; + } + + // ------------- + + /** + * 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); + changed = true; + 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(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) { + } + + 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; + } + 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--; + } + if (value != var) { + changed = true; + } + return value; + } + + protected int newLocalMapping(final Type type) { + int local = nextLocal; + nextLocal += type.getSize(); + return local; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Method.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Method.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Method.java new file mode 100644 index 0000000..b9bc7c7 --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Method.java @@ -0,0 +1,282 @@ +/*** + * 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.tajo.org.objectweb.asm.commons; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.tajo.org.objectweb.asm.Type; + +/** + * A named method descriptor. + * + * @author Juozas Baliuka + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class Method { + + /** + * The method name. + */ + private final String name; + + /** + * The method descriptor. + */ + private final String desc; + + /** + * Maps primitive Java type names to their descriptors. + */ + private static final Map<String, String> DESCRIPTORS; + + static { + DESCRIPTORS = new HashMap<String, String>(); + DESCRIPTORS.put("void", "V"); + DESCRIPTORS.put("byte", "B"); + DESCRIPTORS.put("char", "C"); + DESCRIPTORS.put("double", "D"); + DESCRIPTORS.put("float", "F"); + DESCRIPTORS.put("int", "I"); + DESCRIPTORS.put("long", "J"); + DESCRIPTORS.put("short", "S"); + DESCRIPTORS.put("boolean", "Z"); + } + + /** + * Creates a new {@link Method}. + * + * @param name + * the method's name. + * @param desc + * the method's descriptor. + */ + public Method(final String name, final String desc) { + this.name = name; + this.desc = desc; + } + + /** + * Creates a new {@link Method}. + * + * @param name + * the method's name. + * @param returnType + * the method's return type. + * @param argumentTypes + * the method's argument types. + */ + public Method(final String name, final Type returnType, + final Type[] argumentTypes) { + this(name, Type.getMethodDescriptor(returnType, argumentTypes)); + } + + /** + * Creates a new {@link Method}. + * + * @param m + * a java.lang.reflect method descriptor + * @return a {@link Method} corresponding to the given Java method + * declaration. + */ + public static Method getMethod(java.lang.reflect.Method m) { + return new Method(m.getName(), Type.getMethodDescriptor(m)); + } + + /** + * Creates a new {@link Method}. + * + * @param c + * a java.lang.reflect constructor descriptor + * @return a {@link Method} corresponding to the given Java constructor + * declaration. + */ + public static Method getMethod(java.lang.reflect.Constructor<?> c) { + return new Method("<init>", Type.getConstructorDescriptor(c)); + } + + /** + * Returns a {@link Method} corresponding to the given Java method + * declaration. + * + * @param method + * a Java method declaration, without argument names, of the form + * "returnType name (argumentType1, ... argumentTypeN)", where + * the types are in plain Java (e.g. "int", "float", + * "java.util.List", ...). Classes of the java.lang package can + * be specified by their unqualified name; all other classes + * names must be fully qualified. + * @return a {@link Method} corresponding to the given Java method + * declaration. + * @throws IllegalArgumentException + * if <code>method</code> could not get parsed. + */ + public static Method getMethod(final String method) + throws IllegalArgumentException { + return getMethod(method, false); + } + + /** + * Returns a {@link Method} corresponding to the given Java method + * declaration. + * + * @param method + * a Java method declaration, without argument names, of the form + * "returnType name (argumentType1, ... argumentTypeN)", where + * the types are in plain Java (e.g. "int", "float", + * "java.util.List", ...). Classes of the java.lang package may + * be specified by their unqualified name, depending on the + * defaultPackage argument; all other classes names must be fully + * qualified. + * @param defaultPackage + * true if unqualified class names belong to the default package, + * or false if they correspond to java.lang classes. For instance + * "Object" means "Object" if this option is true, or + * "java.lang.Object" otherwise. + * @return a {@link Method} corresponding to the given Java method + * declaration. + * @throws IllegalArgumentException + * if <code>method</code> could not get parsed. + */ + public static Method getMethod(final String method, + final boolean defaultPackage) throws IllegalArgumentException { + int space = method.indexOf(' '); + int start = method.indexOf('(', space) + 1; + int end = method.indexOf(')', start); + if (space == -1 || start == -1 || end == -1) { + throw new IllegalArgumentException(); + } + String returnType = method.substring(0, space); + String methodName = method.substring(space + 1, start - 1).trim(); + StringBuffer sb = new StringBuffer(); + sb.append('('); + int p; + do { + String s; + p = method.indexOf(',', start); + if (p == -1) { + s = map(method.substring(start, end).trim(), defaultPackage); + } else { + s = map(method.substring(start, p).trim(), defaultPackage); + start = p + 1; + } + sb.append(s); + } while (p != -1); + sb.append(')'); + sb.append(map(returnType, defaultPackage)); + return new Method(methodName, sb.toString()); + } + + private static String map(final String type, final boolean defaultPackage) { + if ("".equals(type)) { + return type; + } + + StringBuffer sb = new StringBuffer(); + int index = 0; + while ((index = type.indexOf("[]", index) + 1) > 0) { + sb.append('['); + } + + String t = type.substring(0, type.length() - sb.length() * 2); + String desc = DESCRIPTORS.get(t); + if (desc != null) { + sb.append(desc); + } else { + sb.append('L'); + if (t.indexOf('.') < 0) { + if (!defaultPackage) { + sb.append("java/lang/"); + } + sb.append(t); + } else { + sb.append(t.replace('.', '/')); + } + sb.append(';'); + } + return sb.toString(); + } + + /** + * Returns the name of the method described by this object. + * + * @return the name of the method described by this object. + */ + public String getName() { + return name; + } + + /** + * Returns the descriptor of the method described by this object. + * + * @return the descriptor of the method described by this object. + */ + public String getDescriptor() { + return desc; + } + + /** + * Returns the return type of the method described by this object. + * + * @return the return type of the method described by this object. + */ + public Type getReturnType() { + return Type.getReturnType(desc); + } + + /** + * Returns the argument types of the method described by this object. + * + * @return the argument types of the method described by this object. + */ + public Type[] getArgumentTypes() { + return Type.getArgumentTypes(desc); + } + + @Override + public String toString() { + return name + desc; + } + + @Override + public boolean equals(final Object o) { + if (!(o instanceof Method)) { + return false; + } + Method other = (Method) o; + return name.equals(other.name) && desc.equals(other.desc); + } + + @Override + public int hashCode() { + return name.hashCode() ^ desc.hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Remapper.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Remapper.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Remapper.java new file mode 100644 index 0000000..3d02a3a --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/Remapper.java @@ -0,0 +1,223 @@ +/*** + * 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.tajo.org.objectweb.asm.commons; + +import org.apache.tajo.org.objectweb.asm.Type; +import org.apache.tajo.org.objectweb.asm.signature.SignatureReader; +import org.apache.tajo.org.objectweb.asm.signature.SignatureVisitor; +import org.apache.tajo.org.objectweb.asm.signature.SignatureWriter; +import org.apache.tajo.org.objectweb.asm.Handle; + +/** + * A class responsible for remapping types and names. Subclasses can override + * the following methods: + * + * <ul> + * <li>{@link #map(String)} - map type</li> + * <li>{@link #mapFieldName(String, String, String)} - map field name</li> + * <li>{@link #mapMethodName(String, String, String)} - map method name</li> + * </ul> + * + * @author Eugene Kuleshov + */ +public abstract class Remapper { + + public String mapDesc(String desc) { + Type t = Type.getType(desc); + switch (t.getSort()) { + case Type.ARRAY: + String s = mapDesc(t.getElementType().getDescriptor()); + for (int i = 0; i < t.getDimensions(); ++i) { + s = '[' + s; + } + return s; + case Type.OBJECT: + String newType = map(t.getInternalName()); + if (newType != null) { + return 'L' + newType + ';'; + } + } + return desc; + } + + private Type mapType(Type t) { + switch (t.getSort()) { + case Type.ARRAY: + String s = mapDesc(t.getElementType().getDescriptor()); + for (int i = 0; i < t.getDimensions(); ++i) { + s = '[' + s; + } + return Type.getType(s); + case Type.OBJECT: + s = map(t.getInternalName()); + return s != null ? Type.getObjectType(s) : t; + case Type.METHOD: + return Type.getMethodType(mapMethodDesc(t.getDescriptor())); + } + return t; + } + + public String mapType(String type) { + if (type == null) { + return null; + } + return mapType(Type.getObjectType(type)).getInternalName(); + } + + public String[] mapTypes(String[] types) { + String[] newTypes = null; + boolean needMapping = false; + for (int i = 0; i < types.length; i++) { + String type = types[i]; + String newType = map(type); + if (newType != null && newTypes == null) { + newTypes = new String[types.length]; + if (i > 0) { + System.arraycopy(types, 0, newTypes, 0, i); + } + needMapping = true; + } + if (needMapping) { + newTypes[i] = newType == null ? type : newType; + } + } + return needMapping ? newTypes : types; + } + + public String mapMethodDesc(String desc) { + if ("()V".equals(desc)) { + return desc; + } + + Type[] args = Type.getArgumentTypes(desc); + StringBuffer s = new StringBuffer("("); + for (int i = 0; i < args.length; i++) { + s.append(mapDesc(args[i].getDescriptor())); + } + Type returnType = Type.getReturnType(desc); + if (returnType == Type.VOID_TYPE) { + s.append(")V"); + return s.toString(); + } + s.append(')').append(mapDesc(returnType.getDescriptor())); + return s.toString(); + } + + public Object mapValue(Object value) { + if (value instanceof Type) { + return mapType((Type) value); + } + if (value instanceof Handle) { + Handle h = (Handle) value; + return new Handle(h.getTag(), mapType(h.getOwner()), mapMethodName( + h.getOwner(), h.getName(), h.getDesc()), + mapMethodDesc(h.getDesc())); + } + return value; + } + + /** + * + * @param typeSignature + * true if signature is a FieldTypeSignature, such as the + * signature parameter of the ClassVisitor.visitField or + * MethodVisitor.visitLocalVariable methods + */ + public String mapSignature(String signature, boolean typeSignature) { + if (signature == null) { + return null; + } + SignatureReader r = new SignatureReader(signature); + SignatureWriter w = new SignatureWriter(); + SignatureVisitor a = createRemappingSignatureAdapter(w); + if (typeSignature) { + r.acceptType(a); + } else { + r.accept(a); + } + return w.toString(); + } + + protected SignatureVisitor createRemappingSignatureAdapter( + SignatureVisitor v) { + return new RemappingSignatureAdapter(v, this); + } + + /** + * Map method name to the new name. Subclasses can override. + * + * @param owner + * owner of the method. + * @param name + * name of the method. + * @param desc + * descriptor of the method. + * @return new name of the method + */ + public String mapMethodName(String owner, String name, String desc) { + return name; + } + + /** + * Map invokedynamic method name to the new name. Subclasses can override. + * + * @param name + * name of the invokedynamic. + * @param desc + * descriptor of the invokedynamic. + * @return new invokdynamic name. + */ + public String mapInvokeDynamicMethodName(String name, String desc) { + return name; + } + + /** + * Map field name to the new name. Subclasses can override. + * + * @param owner + * owner of the field. + * @param name + * name of the field + * @param desc + * descriptor of the field + * @return new name of the field. + */ + public String mapFieldName(String owner, String name, String desc) { + return name; + } + + /** + * Map type name to the new name. Subclasses can override. + */ + public String map(String typeName) { + return typeName; + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingAnnotationAdapter.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingAnnotationAdapter.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingAnnotationAdapter.java new file mode 100644 index 0000000..1909ec2 --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingAnnotationAdapter.java @@ -0,0 +1,79 @@ +/*** + * 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.tajo.org.objectweb.asm.commons; + +import org.apache.tajo.org.objectweb.asm.AnnotationVisitor; +import org.apache.tajo.org.objectweb.asm.Opcodes; + +/** + * An {@link org.apache.tajo.org.objectweb.asm.AnnotationVisitor} adapter for type remapping. + * + * @author Eugene Kuleshov + */ +public class RemappingAnnotationAdapter extends AnnotationVisitor { + + protected final Remapper remapper; + + public RemappingAnnotationAdapter(final AnnotationVisitor av, + final Remapper remapper) { + this(Opcodes.ASM4, av, remapper); + } + + protected RemappingAnnotationAdapter(final int api, + final AnnotationVisitor av, final Remapper remapper) { + super(api, av); + this.remapper = remapper; + } + + @Override + public void visit(String name, Object value) { + av.visit(name, remapper.mapValue(value)); + } + + @Override + public void visitEnum(String name, String desc, String value) { + av.visitEnum(name, remapper.mapDesc(desc), value); + } + + @Override + public AnnotationVisitor visitAnnotation(String name, String desc) { + AnnotationVisitor v = av.visitAnnotation(name, remapper.mapDesc(desc)); + return v == null ? null : (v == av ? this + : new RemappingAnnotationAdapter(v, remapper)); + } + + @Override + public AnnotationVisitor visitArray(String name) { + AnnotationVisitor v = av.visitArray(name); + return v == null ? null : (v == av ? this + : new RemappingAnnotationAdapter(v, remapper)); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingClassAdapter.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingClassAdapter.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingClassAdapter.java new file mode 100644 index 0000000..fd35841 --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingClassAdapter.java @@ -0,0 +1,126 @@ +/*** + * 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.tajo.org.objectweb.asm.commons; + +import org.apache.tajo.org.objectweb.asm.AnnotationVisitor; +import org.apache.tajo.org.objectweb.asm.ClassVisitor; +import org.apache.tajo.org.objectweb.asm.FieldVisitor; +import org.apache.tajo.org.objectweb.asm.MethodVisitor; +import org.apache.tajo.org.objectweb.asm.Opcodes; + +/** + * A {@link org.apache.tajo.org.objectweb.asm.ClassVisitor} for type remapping. + * + * @author Eugene Kuleshov + */ +public class RemappingClassAdapter extends ClassVisitor { + + protected final Remapper remapper; + + protected String className; + + public RemappingClassAdapter(final ClassVisitor cv, final Remapper remapper) { + this(Opcodes.ASM4, cv, remapper); + } + + protected RemappingClassAdapter(final int api, final ClassVisitor cv, + final Remapper remapper) { + super(api, cv); + this.remapper = remapper; + } + + @Override + public void visit(int version, int access, String name, String signature, + String superName, String[] interfaces) { + this.className = name; + super.visit(version, access, remapper.mapType(name), remapper + .mapSignature(signature, false), remapper.mapType(superName), + interfaces == null ? null : remapper.mapTypes(interfaces)); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationVisitor av; + av = super.visitAnnotation(remapper.mapDesc(desc), visible); + return av == null ? null : createRemappingAnnotationAdapter(av); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, + String signature, Object value) { + FieldVisitor fv = super.visitField(access, + remapper.mapFieldName(className, name, desc), + remapper.mapDesc(desc), remapper.mapSignature(signature, true), + remapper.mapValue(value)); + return fv == null ? null : createRemappingFieldAdapter(fv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + String newDesc = remapper.mapMethodDesc(desc); + MethodVisitor mv = super.visitMethod(access, remapper.mapMethodName( + className, name, desc), newDesc, remapper.mapSignature( + signature, false), + exceptions == null ? null : remapper.mapTypes(exceptions)); + return mv == null ? null : createRemappingMethodAdapter(access, + newDesc, mv); + } + + @Override + public void visitInnerClass(String name, String outerName, + String innerName, int access) { + // TODO should innerName be changed? + super.visitInnerClass(remapper.mapType(name), outerName == null ? null + : remapper.mapType(outerName), innerName, access); + } + + @Override + public void visitOuterClass(String owner, String name, String desc) { + super.visitOuterClass(remapper.mapType(owner), name == null ? null + : remapper.mapMethodName(owner, name, desc), + desc == null ? null : remapper.mapMethodDesc(desc)); + } + + protected FieldVisitor createRemappingFieldAdapter(FieldVisitor fv) { + return new RemappingFieldAdapter(fv, remapper); + } + + protected MethodVisitor createRemappingMethodAdapter(int access, + String newDesc, MethodVisitor mv) { + return new RemappingMethodAdapter(access, newDesc, mv, remapper); + } + + protected AnnotationVisitor createRemappingAnnotationAdapter( + AnnotationVisitor av) { + return new RemappingAnnotationAdapter(av, remapper); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingFieldAdapter.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingFieldAdapter.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingFieldAdapter.java new file mode 100644 index 0000000..72744f9 --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingFieldAdapter.java @@ -0,0 +1,62 @@ +/*** + * 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.tajo.org.objectweb.asm.commons; + +import org.apache.tajo.org.objectweb.asm.AnnotationVisitor; +import org.apache.tajo.org.objectweb.asm.FieldVisitor; +import org.apache.tajo.org.objectweb.asm.Opcodes; + +/** + * A {@link FieldVisitor} adapter for type remapping. + * + * @author Eugene Kuleshov + */ +public class RemappingFieldAdapter extends FieldVisitor { + + private final Remapper remapper; + + public RemappingFieldAdapter(final FieldVisitor fv, final Remapper remapper) { + this(Opcodes.ASM4, fv, remapper); + } + + protected RemappingFieldAdapter(final int api, final FieldVisitor fv, + final Remapper remapper) { + super(api, fv); + this.remapper = remapper; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationVisitor av = fv.visitAnnotation(remapper.mapDesc(desc), + visible); + return av == null ? null : new RemappingAnnotationAdapter(av, remapper); + } +} http://git-wip-us.apache.org/repos/asf/tajo/blob/7603a3d4/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingMethodAdapter.java ---------------------------------------------------------------------- diff --git a/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingMethodAdapter.java b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingMethodAdapter.java new file mode 100644 index 0000000..357a44b --- /dev/null +++ b/tajo-thirdparty/asm/src/main/java/org/apache/tajo/org/objectweb/asm/commons/RemappingMethodAdapter.java @@ -0,0 +1,161 @@ +/*** + * 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.tajo.org.objectweb.asm.commons; + +import org.apache.tajo.org.objectweb.asm.AnnotationVisitor; +import org.apache.tajo.org.objectweb.asm.Handle; +import org.apache.tajo.org.objectweb.asm.Label; +import org.apache.tajo.org.objectweb.asm.MethodVisitor; +import org.apache.tajo.org.objectweb.asm.Opcodes; + +/** + * A {@link LocalVariablesSorter} for type mapping. + * + * @author Eugene Kuleshov + */ +public class RemappingMethodAdapter extends LocalVariablesSorter { + + protected final Remapper remapper; + + public RemappingMethodAdapter(final int access, final String desc, + final MethodVisitor mv, final Remapper remapper) { + this(Opcodes.ASM4, access, desc, mv, remapper); + } + + protected RemappingMethodAdapter(final int api, final int access, + final String desc, final MethodVisitor mv, final Remapper remapper) { + super(api, access, desc, mv); + this.remapper = remapper; + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + AnnotationVisitor av = mv.visitAnnotationDefault(); + return av == null ? av : new RemappingAnnotationAdapter(av, remapper); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationVisitor av = mv.visitAnnotation(remapper.mapDesc(desc), + visible); + return av == null ? av : new RemappingAnnotationAdapter(av, remapper); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, + String desc, boolean visible) { + AnnotationVisitor av = mv.visitParameterAnnotation(parameter, + remapper.mapDesc(desc), visible); + return av == null ? av : new RemappingAnnotationAdapter(av, remapper); + } + + @Override + public void visitFrame(int type, int nLocal, Object[] local, int nStack, + Object[] stack) { + super.visitFrame(type, nLocal, remapEntries(nLocal, local), nStack, + remapEntries(nStack, stack)); + } + + private Object[] remapEntries(int n, Object[] entries) { + for (int i = 0; i < n; i++) { + if (entries[i] instanceof String) { + Object[] newEntries = new Object[n]; + if (i > 0) { + System.arraycopy(entries, 0, newEntries, 0, i); + } + do { + Object t = entries[i]; + newEntries[i++] = t instanceof String ? remapper + .mapType((String) t) : t; + } while (i < n); + return newEntries; + } + } + return entries; + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, + String desc) { + super.visitFieldInsn(opcode, remapper.mapType(owner), + remapper.mapFieldName(owner, name, desc), + remapper.mapDesc(desc)); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + super.visitMethodInsn(opcode, remapper.mapType(owner), + remapper.mapMethodName(owner, name, desc), + remapper.mapMethodDesc(desc)); + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + for (int i = 0; i < bsmArgs.length; i++) { + bsmArgs[i] = remapper.mapValue(bsmArgs[i]); + } + super.visitInvokeDynamicInsn( + remapper.mapInvokeDynamicMethodName(name, desc), + remapper.mapMethodDesc(desc), (Handle) remapper.mapValue(bsm), + bsmArgs); + } + + @Override + public void visitTypeInsn(int opcode, String type) { + super.visitTypeInsn(opcode, remapper.mapType(type)); + } + + @Override + public void visitLdcInsn(Object cst) { + super.visitLdcInsn(remapper.mapValue(cst)); + } + + @Override + public void visitMultiANewArrayInsn(String desc, int dims) { + super.visitMultiANewArrayInsn(remapper.mapDesc(desc), dims); + } + + @Override + public void visitTryCatchBlock(Label start, Label end, Label handler, + String type) { + super.visitTryCatchBlock(start, end, handler, type == null ? null + : remapper.mapType(type)); + } + + @Override + public void visitLocalVariable(String name, String desc, String signature, + Label start, Label end, int index) { + super.visitLocalVariable(name, remapper.mapDesc(desc), + remapper.mapSignature(signature, true), start, end, index); + } +}
