001: /***
002: * ASM: a very small and fast Java bytecode manipulation framework
003: * Copyright (c) 2000-2005 INRIA, France Telecom
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: * 2. Redistributions in binary form must reproduce the above copyright
012: * notice, this list of conditions and the following disclaimer in the
013: * documentation and/or other materials provided with the distribution.
014: * 3. Neither the name of the copyright holders nor the names of its
015: * contributors may be used to endorse or promote products derived from
016: * this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028: * THE POSSIBILITY OF SUCH DAMAGE.
029: */package com.tc.asm.commons;
030:
031: import java.util.AbstractMap;
032: import java.util.ArrayList;
033: import java.util.BitSet;
034: import java.util.HashMap;
035: import java.util.Iterator;
036: import java.util.LinkedList;
037: import java.util.List;
038: import java.util.Map;
039: import java.util.Set;
040:
041: import com.tc.asm.Label;
042: import com.tc.asm.MethodVisitor;
043: import com.tc.asm.Opcodes;
044: import com.tc.asm.Type;
045: import com.tc.asm.tree.AbstractInsnNode;
046: import com.tc.asm.tree.InsnList;
047: import com.tc.asm.tree.InsnNode;
048: import com.tc.asm.tree.JumpInsnNode;
049: import com.tc.asm.tree.LabelNode;
050: import com.tc.asm.tree.LocalVariableNode;
051: import com.tc.asm.tree.LookupSwitchInsnNode;
052: import com.tc.asm.tree.MethodNode;
053: import com.tc.asm.tree.TableSwitchInsnNode;
054: import com.tc.asm.tree.TryCatchBlockNode;
055:
056: /**
057: * A {@link com.tc.asm.MethodAdapter} that removes JSR instructions and
058: * inlines the referenced subroutines.
059: *
060: * <b>Explanation of how it works</b> TODO
061: *
062: * @author Niko Matsakis
063: */
064: public class JSRInlinerAdapter extends MethodNode implements Opcodes {
065:
066: private final static boolean LOGGING = false;
067:
068: /**
069: * The visitor to which we will emit a translation of this method without
070: * internal subroutines.
071: */
072: private MethodVisitor mv;
073:
074: /**
075: * For each label that is jumped to by a JSR, we create a Subroutine
076: * instance. Map<LabelNode,Subroutine> is the generic type.
077: */
078: private final Map subroutineHeads = new HashMap();
079:
080: /**
081: * This subroutine instance denotes the line of execution that is not
082: * contained within any subroutine; i.e., the "subroutine" that is executing
083: * when a method first begins.
084: */
085: private final Subroutine mainSubroutine = new Subroutine();
086:
087: /**
088: * This BitSet contains the index of every instruction that belongs to more
089: * than one subroutine. This should not happen often.
090: */
091: private final BitSet dualCitizens = new BitSet();
092:
093: /**
094: * Creates a new JSRInliner.
095: *
096: * @param mv the <code>MethodVisitor</code> to send the resulting inlined
097: * method code to (use <code>null</code> for none).
098: * @param access the method's access flags (see {@link Opcodes}). This
099: * parameter also indicates if the method is synthetic and/or
100: * deprecated.
101: * @param name the method's name.
102: * @param desc the method's descriptor (see {@link Type}).
103: * @param signature the method's signature. May be <tt>null</tt>.
104: * @param exceptions the internal names of the method's exception classes
105: * (see {@link Type#getInternalName() getInternalName}). May be
106: * <tt>null</tt>.
107: */
108: public JSRInlinerAdapter(final MethodVisitor mv, final int access,
109: final String name, final String desc,
110: final String signature, final String[] exceptions) {
111: super (access, name, desc, signature, exceptions);
112: this .mv = mv;
113: }
114:
115: /**
116: * Detects a JSR instruction and sets a flag to indicate we will need to do
117: * inlining.
118: */
119: public void visitJumpInsn(final int opcode, final Label lbl) {
120: super .visitJumpInsn(opcode, lbl);
121: LabelNode ln = ((JumpInsnNode) instructions.getLast()).label;
122: if (opcode == JSR && !subroutineHeads.containsKey(ln)) {
123: subroutineHeads.put(ln, new Subroutine());
124: }
125: }
126:
127: /**
128: * If any JSRs were seen, triggers the inlining process. Otherwise, forwards
129: * the byte codes untouched.
130: */
131: public void visitEnd() {
132: if (!subroutineHeads.isEmpty()) {
133: markSubroutines();
134: if (LOGGING) {
135: log(mainSubroutine.toString());
136: Iterator it = subroutineHeads.values().iterator();
137: while (it.hasNext()) {
138: Subroutine sub = (Subroutine) it.next();
139: log(sub.toString());
140: }
141: }
142: emitCode();
143: }
144:
145: // Forward the translate opcodes on if appropriate:
146: if (mv != null) {
147: accept(mv);
148: }
149: }
150:
151: /**
152: * Walks the method and determines which internal subroutine(s), if any,
153: * each instruction is a method of.
154: */
155: private void markSubroutines() {
156: BitSet anyvisited = new BitSet();
157:
158: // First walk the main subroutine and find all those instructions which
159: // can be reached without invoking any JSR at all
160: markSubroutineWalk(mainSubroutine, 0, anyvisited);
161:
162: // Go through the head of each subroutine and find any nodes reachable
163: // to that subroutine without following any JSR links.
164: for (Iterator it = subroutineHeads.entrySet().iterator(); it
165: .hasNext();) {
166: Map.Entry entry = (Map.Entry) it.next();
167: LabelNode lab = (LabelNode) entry.getKey();
168: Subroutine sub = (Subroutine) entry.getValue();
169: int index = instructions.indexOf(lab);
170: markSubroutineWalk(sub, index, anyvisited);
171: }
172: }
173:
174: /**
175: * Performs a depth first search walking the normal byte code path starting
176: * at <code>index</code>, and adding each instruction encountered into
177: * the subroutine <code>sub</code>. After this walk is complete, iterates
178: * over the exception handlers to ensure that we also include those byte
179: * codes which are reachable through an exception that may be thrown during
180: * the execution of the subroutine. Invoked from
181: * <code>markSubroutines()</code>.
182: *
183: * @param sub TODO.
184: * @param index TODO.
185: * @param anyvisited TODO.
186: */
187: private void markSubroutineWalk(final Subroutine sub,
188: final int index, final BitSet anyvisited) {
189: if (LOGGING) {
190: log("markSubroutineWalk: sub=" + sub + " index=" + index);
191: }
192:
193: // First find those instructions reachable via normal execution
194: markSubroutineWalkDFS(sub, index, anyvisited);
195:
196: // Now, make sure we also include any applicable exception handlers
197: boolean loop = true;
198: while (loop) {
199: loop = false;
200: for (Iterator it = tryCatchBlocks.iterator(); it.hasNext();) {
201: TryCatchBlockNode trycatch = (TryCatchBlockNode) it
202: .next();
203:
204: if (LOGGING) {
205: log("Scanning try/catch " + trycatch);
206: }
207:
208: // If the handler has already been processed, skip it.
209: int handlerindex = instructions
210: .indexOf(trycatch.handler);
211: if (sub.instructions.get(handlerindex)) {
212: continue;
213: }
214:
215: int startindex = instructions.indexOf(trycatch.start);
216: int endindex = instructions.indexOf(trycatch.end);
217: int nextbit = sub.instructions.nextSetBit(startindex);
218: if (nextbit != -1 && nextbit < endindex) {
219: if (LOGGING) {
220: log("Adding exception handler: " + startindex
221: + "-" + endindex + " due to " + nextbit
222: + " handler " + handlerindex);
223: }
224: markSubroutineWalkDFS(sub, handlerindex, anyvisited);
225: loop = true;
226: }
227: }
228: }
229: }
230:
231: /**
232: * Performs a simple DFS of the instructions, assigning each to the
233: * subroutine <code>sub</code>. Starts from <code>index</code>.
234: * Invoked only by <code>markSubroutineWalk()</code>.
235: *
236: * @param sub TODO.
237: * @param index TODO.
238: * @param anyvisited TODO.
239: */
240: private void markSubroutineWalkDFS(final Subroutine sub, int index,
241: final BitSet anyvisited) {
242: while (true) {
243: AbstractInsnNode node = instructions.get(index);
244:
245: // don't visit a node twice
246: if (sub.instructions.get(index)) {
247: return;
248: }
249: sub.instructions.set(index);
250:
251: // check for those nodes already visited by another subroutine
252: if (anyvisited.get(index)) {
253: dualCitizens.set(index);
254: if (LOGGING) {
255: log("Instruction #" + index + " is dual citizen.");
256: }
257: }
258: anyvisited.set(index);
259:
260: if (node.getType() == AbstractInsnNode.JUMP_INSN
261: && node.getOpcode() != JSR) {
262: // we do not follow recursively called subroutines here; but any
263: // other sort of branch we do follow
264: JumpInsnNode jnode = (JumpInsnNode) node;
265: int destidx = instructions.indexOf(jnode.label);
266: markSubroutineWalkDFS(sub, destidx, anyvisited);
267: }
268: if (node.getType() == AbstractInsnNode.TABLESWITCH_INSN) {
269: TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
270: int destidx = instructions.indexOf(tsnode.dflt);
271: markSubroutineWalkDFS(sub, destidx, anyvisited);
272: for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
273: LabelNode l = (LabelNode) tsnode.labels.get(i);
274: destidx = instructions.indexOf(l);
275: markSubroutineWalkDFS(sub, destidx, anyvisited);
276: }
277: }
278: if (node.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) {
279: LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
280: int destidx = instructions.indexOf(lsnode.dflt);
281: markSubroutineWalkDFS(sub, destidx, anyvisited);
282: for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
283: LabelNode l = (LabelNode) lsnode.labels.get(i);
284: destidx = instructions.indexOf(l);
285: markSubroutineWalkDFS(sub, destidx, anyvisited);
286: }
287: }
288:
289: // check to see if this opcode falls through to the next instruction
290: // or not; if not, return.
291: switch (instructions.get(index).getOpcode()) {
292: case GOTO:
293: case RET:
294: case TABLESWITCH:
295: case LOOKUPSWITCH:
296: case IRETURN:
297: case LRETURN:
298: case FRETURN:
299: case DRETURN:
300: case ARETURN:
301: case RETURN:
302: case ATHROW:
303: /*
304: * note: this either returns from this subroutine, or a
305: * parent subroutine which invoked it
306: */
307: return;
308: }
309:
310: // Use tail recursion here in the form of an outer while loop to
311: // avoid our stack growing needlessly:
312: index++;
313: }
314: }
315:
316: /**
317: * Creates the new instructions, inlining each instantiation of each
318: * subroutine until the code is fully elaborated.
319: */
320: private void emitCode() {
321: LinkedList worklist = new LinkedList();
322: // Create an instantiation of the "root" subroutine, which is just the
323: // main routine
324: worklist.add(new Instantiation(null, mainSubroutine));
325:
326: // Emit instantiations of each subroutine we encounter, including the
327: // main subroutine
328: InsnList newInstructions = new InsnList();
329: List newTryCatchBlocks = new ArrayList();
330: List newLocalVariables = new ArrayList();
331: while (!worklist.isEmpty()) {
332: Instantiation inst = (Instantiation) worklist.removeFirst();
333: emitSubroutine(inst, worklist, newInstructions,
334: newTryCatchBlocks, newLocalVariables);
335: }
336: instructions = newInstructions;
337: tryCatchBlocks = newTryCatchBlocks;
338: localVariables = newLocalVariables;
339: }
340:
341: /**
342: * Emits one instantiation of one subroutine, specified by
343: * <code>instant</code>. May add new instantiations that are invoked by
344: * this one to the <code>worklist</code> parameter, and new try/catch
345: * blocks to <code>newTryCatchBlocks</code>.
346: *
347: * @param instant TODO.
348: * @param workList TODO.
349: * @param newInstructions TODO.
350: * @param newTryCatchBlocks TODO.
351: */
352: private void emitSubroutine(final Instantiation instant,
353: final List worklist, final InsnList newInstructions,
354: final List newTryCatchBlocks, final List newLocalVariables) {
355: LabelNode duplbl = null;
356:
357: if (LOGGING) {
358: log("--------------------------------------------------------");
359: log("Emitting instantiation of subroutine "
360: + instant.subroutine);
361: }
362:
363: // Emit the relevant instructions for this instantiation, translating
364: // labels and jump targets as we go:
365: for (int i = 0, c = instructions.size(); i < c; i++) {
366: AbstractInsnNode insn = instructions.get(i);
367: Instantiation owner = instant.findOwner(i);
368:
369: // Always remap labels:
370: if (insn.getType() == AbstractInsnNode.LABEL) {
371: // Translate labels into their renamed equivalents.
372: // Avoid adding the same label more than once. Note
373: // that because we own this instruction the gotoTable
374: // and the rangeTable will always agree.
375: LabelNode ilbl = (LabelNode) insn;
376: LabelNode remap = instant.rangeLabel(ilbl);
377: if (LOGGING) {
378: log("Translating lbl #" + i + ":" + ilbl + " to "
379: + remap);
380: }
381: if (remap != duplbl) {
382: newInstructions.add(remap);
383: duplbl = remap;
384: }
385: continue;
386: }
387:
388: // We don't want to emit instructions that were already
389: // emitted by a subroutine higher on the stack. Note that
390: // it is still possible for a given instruction to be
391: // emitted twice because it may belong to two subroutines
392: // that do not invoke each other.
393: if (owner != instant) {
394: continue;
395: }
396:
397: if (LOGGING) {
398: log("Emitting inst #" + i);
399: }
400:
401: if (insn.getOpcode() == RET) {
402: // Translate RET instruction(s) to a jump to the return label
403: // for the appropriate instantiation. The problem is that the
404: // subroutine may "fall through" to the ret of a parent
405: // subroutine; therefore, to find the appropriate ret label we
406: // find the lowest subroutine on the stack that claims to own
407: // this instruction. See the class javadoc comment for an
408: // explanation on why this technique is safe (note: it is only
409: // safe if the input is verifiable).
410: LabelNode retlabel = null;
411: for (Instantiation p = instant; p != null; p = p.previous) {
412: if (p.subroutine.ownsInstruction(i)) {
413: retlabel = p.returnLabel;
414: }
415: }
416: if (retlabel == null) {
417: // This is only possible if the mainSubroutine owns a RET
418: // instruction, which should never happen for verifiable
419: // code.
420: throw new RuntimeException("Instruction #" + i
421: + " is a RET not owned by any subroutine");
422: }
423: newInstructions.add(new JumpInsnNode(GOTO, retlabel));
424: } else if (insn.getOpcode() == JSR) {
425: LabelNode lbl = ((JumpInsnNode) insn).label;
426: Subroutine sub = (Subroutine) subroutineHeads.get(lbl);
427: Instantiation newinst = new Instantiation(instant, sub);
428: LabelNode startlbl = newinst.gotoLabel(lbl);
429:
430: if (LOGGING) {
431: log(" Creating instantiation of subr " + sub);
432: }
433:
434: // Rather than JSRing, we will jump to the inline version and
435: // push NULL for what was once the return value. This hack
436: // allows us to avoid doing any sort of data flow analysis to
437: // figure out which instructions manipulate the old return value
438: // pointer which is now known to be unneeded.
439: newInstructions.add(new InsnNode(ACONST_NULL));
440: newInstructions.add(new JumpInsnNode(GOTO, startlbl));
441: newInstructions.add(newinst.returnLabel);
442:
443: // Insert this new instantiation into the queue to be emitted
444: // later.
445: worklist.add(newinst);
446: } else {
447: newInstructions.add(insn.clone(instant));
448: }
449: }
450:
451: // Emit try/catch blocks that are relevant to this method.
452: for (Iterator it = tryCatchBlocks.iterator(); it.hasNext();) {
453: TryCatchBlockNode trycatch = (TryCatchBlockNode) it.next();
454:
455: if (LOGGING) {
456: log("try catch block original labels=" + trycatch.start
457: + "-" + trycatch.end + "->" + trycatch.handler);
458: }
459:
460: final LabelNode start = instant.rangeLabel(trycatch.start);
461: final LabelNode end = instant.rangeLabel(trycatch.end);
462:
463: // Ignore empty try/catch regions
464: if (start == end) {
465: if (LOGGING) {
466: log(" try catch block empty in this subroutine");
467: }
468: continue;
469: }
470:
471: final LabelNode handler = instant
472: .gotoLabel(trycatch.handler);
473:
474: if (LOGGING) {
475: log(" try catch block new labels=" + start + "-" + end
476: + "->" + handler);
477: }
478:
479: if (start == null || end == null || handler == null) {
480: throw new RuntimeException("Internal error!");
481: }
482:
483: newTryCatchBlocks.add(new TryCatchBlockNode(start, end,
484: handler, trycatch.type));
485: }
486:
487: for (Iterator it = localVariables.iterator(); it.hasNext();) {
488: LocalVariableNode lvnode = (LocalVariableNode) it.next();
489: if (LOGGING) {
490: log("local var " + lvnode.name);
491: }
492: final LabelNode start = instant.rangeLabel(lvnode.start);
493: final LabelNode end = instant.rangeLabel(lvnode.end);
494: if (start == end) {
495: if (LOGGING) {
496: log(" local variable empty in this sub");
497: }
498: continue;
499: }
500: newLocalVariables.add(new LocalVariableNode(lvnode.name,
501: lvnode.desc, lvnode.signature, start, end,
502: lvnode.index));
503: }
504: }
505:
506: private void log(final String str) {
507: System.err.println(str);
508: }
509:
510: protected static class Subroutine {
511:
512: public final BitSet instructions = new BitSet();
513:
514: public void addInstruction(final int idx) {
515: instructions.set(idx);
516: }
517:
518: public boolean ownsInstruction(final int idx) {
519: return instructions.get(idx);
520: }
521:
522: public String toString() {
523: return "Subroutine: " + instructions;
524: }
525: }
526:
527: /**
528: * A class that represents an instantiation of a subroutine. Each
529: * instantiation has an associate "stack" --- which is a listing of those
530: * instantiations that were active when this particular instance of this
531: * subroutine was invoked. Each instantiation also has a map from the
532: * original labels of the program to the labels appropriate for this
533: * instantiation, and finally a label to return to.
534: */
535: private class Instantiation extends AbstractMap {
536:
537: /**
538: * Previous instantiations; the stack must be statically predictable to
539: * be inlinable.
540: */
541: final Instantiation previous;
542:
543: /**
544: * The subroutine this is an instantiation of.
545: */
546: public final Subroutine subroutine;
547:
548: /**
549: * This table maps Labels from the original source to Labels pointing at
550: * code specific to this instantiation, for use in remapping try/catch
551: * blocks,as well as gotos.
552: *
553: * Note that in the presence of dual citizens instructions, that is,
554: * instructions which belong to more than one subroutine due to the
555: * merging of control flow without a RET instruction, we will map the
556: * target label of a GOTO to the label used by the instantiation lowest
557: * on the stack. This avoids code duplication during inlining in most
558: * cases.
559: *
560: * @see #findOwner(int)
561: */
562: public final Map rangeTable = new HashMap();
563:
564: /**
565: * All returns for this instantiation will be mapped to this label
566: */
567: public final LabelNode returnLabel;
568:
569: public Instantiation(final Instantiation prev,
570: final Subroutine sub) {
571: previous = prev;
572: subroutine = sub;
573: for (Instantiation p = prev; p != null; p = p.previous) {
574: if (p.subroutine == sub) {
575: throw new RuntimeException(
576: "Recursive invocation of " + sub);
577: }
578: }
579:
580: // Determine the label to return to when this subroutine terminates
581: // via RET: note that the main subroutine never terminates via RET.
582: if (prev != null) {
583: returnLabel = new LabelNode();
584: } else {
585: returnLabel = null;
586: }
587:
588: // Each instantiation will remap the labels from the code above to
589: // refer to its particular copy of its own instructions. Note that
590: // we collapse labels which point at the same instruction into one:
591: // this is fairly common as we are often ignoring large chunks of
592: // instructions, so what were previously distinct labels become
593: // duplicates.
594: LabelNode duplbl = null;
595: for (int i = 0, c = instructions.size(); i < c; i++) {
596: AbstractInsnNode insn = instructions.get(i);
597:
598: if (insn.getType() == AbstractInsnNode.LABEL) {
599: LabelNode ilbl = (LabelNode) insn;
600:
601: if (duplbl == null) {
602: // if we already have a label pointing at this spot,
603: // don't recreate it.
604: duplbl = new LabelNode();
605: }
606:
607: // Add an entry in the rangeTable for every label
608: // in the original code which points at the next
609: // instruction of our own to be emitted.
610: rangeTable.put(ilbl, duplbl);
611: } else if (findOwner(i) == this ) {
612: // We will emit this instruction, so clear the 'duplbl' flag
613: // since the next Label will refer to a distinct
614: // instruction.
615: duplbl = null;
616: }
617: }
618: }
619:
620: /**
621: * Returns the "owner" of a particular instruction relative to this
622: * instantiation: the owner referes to the Instantiation which will emit
623: * the version of this instruction that we will execute.
624: *
625: * Typically, the return value is either <code>this</code> or
626: * <code>null</code>. <code>this</code> indicates that this
627: * instantiation will generate the version of this instruction that we
628: * will execute, and <code>null</code> indicates that this
629: * instantiation never executes the given instruction.
630: *
631: * Sometimes, however, an instruction can belong to multiple
632: * subroutines; this is called a "dual citizen" instruction (though it
633: * may belong to more than 2 subroutines), and occurs when multiple
634: * subroutines branch to common points of control. In this case, the
635: * owner is the subroutine that appears lowest on the stack, and which
636: * also owns the instruction in question.
637: *
638: * @param i the index of the instruction in the original code
639: * @return the "owner" of a particular instruction relative to this
640: * instantiation.
641: */
642: public Instantiation findOwner(final int i) {
643: if (!subroutine.ownsInstruction(i)) {
644: return null;
645: }
646: if (!dualCitizens.get(i)) {
647: return this ;
648: }
649: Instantiation own = this ;
650: for (Instantiation p = previous; p != null; p = p.previous) {
651: if (p.subroutine.ownsInstruction(i)) {
652: own = p;
653: }
654: }
655: return own;
656: }
657:
658: /**
659: * Looks up the label <code>l</code> in the <code>gotoTable</code>,
660: * thus translating it from a Label in the original code, to a Label in
661: * the inlined code that is appropriate for use by an instruction that
662: * branched to the original label.
663: *
664: * @param l The label we will be translating
665: * @return a label for use by a branch instruction in the inlined code
666: * @see #gotoTable
667: */
668: public LabelNode gotoLabel(final LabelNode l) {
669: // owner should never be null, because owner is only null
670: // if an instruction cannot be reached from this subroutine
671: Instantiation owner = findOwner(instructions.indexOf(l));
672: return (LabelNode) owner.rangeTable.get(l);
673: }
674:
675: /**
676: * Looks up the label <code>l</code> in the <code>rangeTable</code>,
677: * thus translating it from a Label in the original code, to a Label in
678: * the inlined code that is appropriate for use by an try/catch or
679: * variable use annotation.
680: *
681: * @param l The label we will be translating
682: * @return a label for use by a try/catch or variable annotation in the
683: * original code
684: * @see #rangeTable
685: */
686: public LabelNode rangeLabel(final LabelNode l) {
687: return (LabelNode) rangeTable.get(l);
688: }
689:
690: // AbstractMap implementation
691:
692: public Set entrySet() {
693: return null;
694: }
695:
696: public Object get(final Object o) {
697: return gotoLabel((LabelNode) o);
698: }
699: }
700: }
|