001: /***
002: * ASM: a very small and fast Java bytecode manipulation framework
003: * Copyright (c) 2000-2007 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 org.objectweb.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 org.objectweb.asm.Label;
042: import org.objectweb.asm.MethodVisitor;
043: import org.objectweb.asm.Opcodes;
044: import org.objectweb.asm.Type;
045: import org.objectweb.asm.tree.AbstractInsnNode;
046: import org.objectweb.asm.tree.InsnList;
047: import org.objectweb.asm.tree.InsnNode;
048: import org.objectweb.asm.tree.JumpInsnNode;
049: import org.objectweb.asm.tree.LabelNode;
050: import org.objectweb.asm.tree.LookupSwitchInsnNode;
051: import org.objectweb.asm.tree.MethodNode;
052: import org.objectweb.asm.tree.TableSwitchInsnNode;
053: import org.objectweb.asm.tree.TryCatchBlockNode;
054: import org.objectweb.asm.tree.LocalVariableNode;
055:
056: /**
057: * A {@link org.objectweb.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 static final 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 final 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 the subroutine whose instructions must be computed.
184: * @param index an instruction of this subroutine.
185: * @param anyvisited indexes of the already visited instructions, i.e.
186: * marked as part of this subroutine or any previously computed
187: * subroutine.
188: */
189: private void markSubroutineWalk(final Subroutine sub,
190: final int index, final BitSet anyvisited) {
191: if (LOGGING) {
192: log("markSubroutineWalk: sub=" + sub + " index=" + index);
193: }
194:
195: // First find those instructions reachable via normal execution
196: markSubroutineWalkDFS(sub, index, anyvisited);
197:
198: // Now, make sure we also include any applicable exception handlers
199: boolean loop = true;
200: while (loop) {
201: loop = false;
202: for (Iterator it = tryCatchBlocks.iterator(); it.hasNext();) {
203: TryCatchBlockNode trycatch = (TryCatchBlockNode) it
204: .next();
205:
206: if (LOGGING) {
207: // TODO use of default toString().
208: log("Scanning try/catch " + trycatch);
209: }
210:
211: // If the handler has already been processed, skip it.
212: int handlerindex = instructions
213: .indexOf(trycatch.handler);
214: if (sub.instructions.get(handlerindex)) {
215: continue;
216: }
217:
218: int startindex = instructions.indexOf(trycatch.start);
219: int endindex = instructions.indexOf(trycatch.end);
220: int nextbit = sub.instructions.nextSetBit(startindex);
221: if (nextbit != -1 && nextbit < endindex) {
222: if (LOGGING) {
223: log("Adding exception handler: " + startindex
224: + '-' + endindex + " due to " + nextbit
225: + " handler " + handlerindex);
226: }
227: markSubroutineWalkDFS(sub, handlerindex, anyvisited);
228: loop = true;
229: }
230: }
231: }
232: }
233:
234: /**
235: * Performs a simple DFS of the instructions, assigning each to the
236: * subroutine <code>sub</code>. Starts from <code>index</code>.
237: * Invoked only by <code>markSubroutineWalk()</code>.
238: *
239: * @param sub the subroutine whose instructions must be computed.
240: * @param index an instruction of this subroutine.
241: * @param anyvisited indexes of the already visited instructions, i.e.
242: * marked as part of this subroutine or any previously computed
243: * subroutine.
244: */
245: private void markSubroutineWalkDFS(final Subroutine sub, int index,
246: final BitSet anyvisited) {
247: while (true) {
248: AbstractInsnNode node = instructions.get(index);
249:
250: // don't visit a node twice
251: if (sub.instructions.get(index)) {
252: return;
253: }
254: sub.instructions.set(index);
255:
256: // check for those nodes already visited by another subroutine
257: if (anyvisited.get(index)) {
258: dualCitizens.set(index);
259: if (LOGGING) {
260: log("Instruction #" + index + " is dual citizen.");
261: }
262: }
263: anyvisited.set(index);
264:
265: if (node.getType() == AbstractInsnNode.JUMP_INSN
266: && node.getOpcode() != JSR) {
267: // we do not follow recursively called subroutines here; but any
268: // other sort of branch we do follow
269: JumpInsnNode jnode = (JumpInsnNode) node;
270: int destidx = instructions.indexOf(jnode.label);
271: markSubroutineWalkDFS(sub, destidx, anyvisited);
272: }
273: if (node.getType() == AbstractInsnNode.TABLESWITCH_INSN) {
274: TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node;
275: int destidx = instructions.indexOf(tsnode.dflt);
276: markSubroutineWalkDFS(sub, destidx, anyvisited);
277: for (int i = tsnode.labels.size() - 1; i >= 0; --i) {
278: LabelNode l = (LabelNode) tsnode.labels.get(i);
279: destidx = instructions.indexOf(l);
280: markSubroutineWalkDFS(sub, destidx, anyvisited);
281: }
282: }
283: if (node.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) {
284: LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node;
285: int destidx = instructions.indexOf(lsnode.dflt);
286: markSubroutineWalkDFS(sub, destidx, anyvisited);
287: for (int i = lsnode.labels.size() - 1; i >= 0; --i) {
288: LabelNode l = (LabelNode) lsnode.labels.get(i);
289: destidx = instructions.indexOf(l);
290: markSubroutineWalkDFS(sub, destidx, anyvisited);
291: }
292: }
293:
294: // check to see if this opcode falls through to the next instruction
295: // or not; if not, return.
296: switch (instructions.get(index).getOpcode()) {
297: case GOTO:
298: case RET:
299: case TABLESWITCH:
300: case LOOKUPSWITCH:
301: case IRETURN:
302: case LRETURN:
303: case FRETURN:
304: case DRETURN:
305: case ARETURN:
306: case RETURN:
307: case ATHROW:
308: /*
309: * note: this either returns from this subroutine, or a
310: * parent subroutine which invoked it
311: */
312: return;
313: }
314:
315: // Use tail recursion here in the form of an outer while loop to
316: // avoid our stack growing needlessly:
317: index++;
318: }
319: }
320:
321: /**
322: * Creates the new instructions, inlining each instantiation of each
323: * subroutine until the code is fully elaborated.
324: */
325: private void emitCode() {
326: LinkedList worklist = new LinkedList();
327: // Create an instantiation of the "root" subroutine, which is just the
328: // main routine
329: worklist.add(new Instantiation(null, mainSubroutine));
330:
331: // Emit instantiations of each subroutine we encounter, including the
332: // main subroutine
333: InsnList newInstructions = new InsnList();
334: List newTryCatchBlocks = new ArrayList();
335: List newLocalVariables = new ArrayList();
336: while (!worklist.isEmpty()) {
337: Instantiation inst = (Instantiation) worklist.removeFirst();
338: emitSubroutine(inst, worklist, newInstructions,
339: newTryCatchBlocks, newLocalVariables);
340: }
341: instructions = newInstructions;
342: tryCatchBlocks = newTryCatchBlocks;
343: localVariables = newLocalVariables;
344: }
345:
346: /**
347: * Emits one instantiation of one subroutine, specified by
348: * <code>instant</code>. May add new instantiations that are invoked by
349: * this one to the <code>worklist</code> parameter, and new try/catch
350: * blocks to <code>newTryCatchBlocks</code>.
351: *
352: * @param instant the instantiation that must be performed.
353: * @param worklist list of the instantiations that remain to be done.
354: * @param newInstructions the instruction list to which the instantiated
355: * code must be appended.
356: * @param newTryCatchBlocks the exception handler list to which the
357: * instantiated handlers must be appended.
358: */
359: private void emitSubroutine(final Instantiation instant,
360: final List worklist, final InsnList newInstructions,
361: final List newTryCatchBlocks, final List newLocalVariables) {
362: LabelNode duplbl = null;
363:
364: if (LOGGING) {
365: log("--------------------------------------------------------");
366: log("Emitting instantiation of subroutine "
367: + instant.subroutine);
368: }
369:
370: // Emit the relevant instructions for this instantiation, translating
371: // labels and jump targets as we go:
372: for (int i = 0, c = instructions.size(); i < c; i++) {
373: AbstractInsnNode insn = instructions.get(i);
374: Instantiation owner = instant.findOwner(i);
375:
376: // Always remap labels:
377: if (insn.getType() == AbstractInsnNode.LABEL) {
378: // Translate labels into their renamed equivalents.
379: // Avoid adding the same label more than once. Note
380: // that because we own this instruction the gotoTable
381: // and the rangeTable will always agree.
382: LabelNode ilbl = (LabelNode) insn;
383: LabelNode remap = instant.rangeLabel(ilbl);
384: if (LOGGING) {
385: // TODO use of default toString().
386: log("Translating lbl #" + i + ':' + ilbl + " to "
387: + remap);
388: }
389: if (remap != duplbl) {
390: newInstructions.add(remap);
391: duplbl = remap;
392: }
393: continue;
394: }
395:
396: // We don't want to emit instructions that were already
397: // emitted by a subroutine higher on the stack. Note that
398: // it is still possible for a given instruction to be
399: // emitted twice because it may belong to two subroutines
400: // that do not invoke each other.
401: if (owner != instant) {
402: continue;
403: }
404:
405: if (LOGGING) {
406: log("Emitting inst #" + i);
407: }
408:
409: if (insn.getOpcode() == RET) {
410: // Translate RET instruction(s) to a jump to the return label
411: // for the appropriate instantiation. The problem is that the
412: // subroutine may "fall through" to the ret of a parent
413: // subroutine; therefore, to find the appropriate ret label we
414: // find the lowest subroutine on the stack that claims to own
415: // this instruction. See the class javadoc comment for an
416: // explanation on why this technique is safe (note: it is only
417: // safe if the input is verifiable).
418: LabelNode retlabel = null;
419: for (Instantiation p = instant; p != null; p = p.previous) {
420: if (p.subroutine.ownsInstruction(i)) {
421: retlabel = p.returnLabel;
422: }
423: }
424: if (retlabel == null) {
425: // This is only possible if the mainSubroutine owns a RET
426: // instruction, which should never happen for verifiable
427: // code.
428: throw new RuntimeException("Instruction #" + i
429: + " is a RET not owned by any subroutine");
430: }
431: newInstructions.add(new JumpInsnNode(GOTO, retlabel));
432: } else if (insn.getOpcode() == JSR) {
433: LabelNode lbl = ((JumpInsnNode) insn).label;
434: Subroutine sub = (Subroutine) subroutineHeads.get(lbl);
435: Instantiation newinst = new Instantiation(instant, sub);
436: LabelNode startlbl = newinst.gotoLabel(lbl);
437:
438: if (LOGGING) {
439: log(" Creating instantiation of subr " + sub);
440: }
441:
442: // Rather than JSRing, we will jump to the inline version and
443: // push NULL for what was once the return value. This hack
444: // allows us to avoid doing any sort of data flow analysis to
445: // figure out which instructions manipulate the old return value
446: // pointer which is now known to be unneeded.
447: newInstructions.add(new InsnNode(ACONST_NULL));
448: newInstructions.add(new JumpInsnNode(GOTO, startlbl));
449: newInstructions.add(newinst.returnLabel);
450:
451: // Insert this new instantiation into the queue to be emitted
452: // later.
453: worklist.add(newinst);
454: } else {
455: newInstructions.add(insn.clone(instant));
456: }
457: }
458:
459: // Emit try/catch blocks that are relevant to this method.
460: for (Iterator it = tryCatchBlocks.iterator(); it.hasNext();) {
461: TryCatchBlockNode trycatch = (TryCatchBlockNode) it.next();
462:
463: if (LOGGING) {
464: // TODO use of default toString().
465: log("try catch block original labels=" + trycatch.start
466: + '-' + trycatch.end + "->" + trycatch.handler);
467: }
468:
469: final LabelNode start = instant.rangeLabel(trycatch.start);
470: final LabelNode end = instant.rangeLabel(trycatch.end);
471:
472: // Ignore empty try/catch regions
473: if (start == end) {
474: if (LOGGING) {
475: log(" try catch block empty in this subroutine");
476: }
477: continue;
478: }
479:
480: final LabelNode handler = instant
481: .gotoLabel(trycatch.handler);
482:
483: if (LOGGING) {
484: // TODO use of default toString().
485: log(" try catch block new labels=" + start + '-' + end
486: + "->" + handler);
487: }
488:
489: if (start == null || end == null || handler == null) {
490: throw new RuntimeException("Internal error!");
491: }
492:
493: newTryCatchBlocks.add(new TryCatchBlockNode(start, end,
494: handler, trycatch.type));
495: }
496:
497: for (Iterator it = localVariables.iterator(); it.hasNext();) {
498: LocalVariableNode lvnode = (LocalVariableNode) it.next();
499: if (LOGGING) {
500: log("local var " + lvnode.name);
501: }
502: final LabelNode start = instant.rangeLabel(lvnode.start);
503: final LabelNode end = instant.rangeLabel(lvnode.end);
504: if (start == end) {
505: if (LOGGING) {
506: log(" local variable empty in this sub");
507: }
508: continue;
509: }
510: newLocalVariables.add(new LocalVariableNode(lvnode.name,
511: lvnode.desc, lvnode.signature, start, end,
512: lvnode.index));
513: }
514: }
515:
516: private static void log(final String str) {
517: System.err.println(str);
518: }
519:
520: protected static class Subroutine {
521:
522: public final BitSet instructions = new BitSet();
523:
524: public void addInstruction(final int idx) {
525: instructions.set(idx);
526: }
527:
528: public boolean ownsInstruction(final int idx) {
529: return instructions.get(idx);
530: }
531:
532: public String toString() {
533: return "Subroutine: " + instructions;
534: }
535: }
536:
537: /**
538: * A class that represents an instantiation of a subroutine. Each
539: * instantiation has an associate "stack" --- which is a listing of those
540: * instantiations that were active when this particular instance of this
541: * subroutine was invoked. Each instantiation also has a map from the
542: * original labels of the program to the labels appropriate for this
543: * instantiation, and finally a label to return to.
544: */
545: private class Instantiation extends AbstractMap {
546:
547: /**
548: * Previous instantiations; the stack must be statically predictable to
549: * be inlinable.
550: */
551: final Instantiation previous;
552:
553: /**
554: * The subroutine this is an instantiation of.
555: */
556: public final Subroutine subroutine;
557:
558: /**
559: * This table maps Labels from the original source to Labels pointing at
560: * code specific to this instantiation, for use in remapping try/catch
561: * blocks,as well as gotos.
562: *
563: * Note that in the presence of dual citizens instructions, that is,
564: * instructions which belong to more than one subroutine due to the
565: * merging of control flow without a RET instruction, we will map the
566: * target label of a GOTO to the label used by the instantiation lowest
567: * on the stack. This avoids code duplication during inlining in most
568: * cases.
569: *
570: * @see #findOwner(int)
571: */
572: public final Map rangeTable = new HashMap();
573:
574: /**
575: * All returns for this instantiation will be mapped to this label
576: */
577: public final LabelNode returnLabel;
578:
579: private Instantiation(final Instantiation prev,
580: final Subroutine sub) {
581: previous = prev;
582: subroutine = sub;
583: for (Instantiation p = prev; p != null; p = p.previous) {
584: if (p.subroutine == sub) {
585: throw new RuntimeException(
586: "Recursive invocation of " + sub);
587: }
588: }
589:
590: // Determine the label to return to when this subroutine terminates
591: // via RET: note that the main subroutine never terminates via RET.
592: if (prev != null) {
593: returnLabel = new LabelNode();
594: } else {
595: returnLabel = null;
596: }
597:
598: // Each instantiation will remap the labels from the code above to
599: // refer to its particular copy of its own instructions. Note that
600: // we collapse labels which point at the same instruction into one:
601: // this is fairly common as we are often ignoring large chunks of
602: // instructions, so what were previously distinct labels become
603: // duplicates.
604: LabelNode duplbl = null;
605: for (int i = 0, c = instructions.size(); i < c; i++) {
606: AbstractInsnNode insn = instructions.get(i);
607:
608: if (insn.getType() == AbstractInsnNode.LABEL) {
609: LabelNode ilbl = (LabelNode) insn;
610:
611: if (duplbl == null) {
612: // if we already have a label pointing at this spot,
613: // don't recreate it.
614: duplbl = new LabelNode();
615: }
616:
617: // Add an entry in the rangeTable for every label
618: // in the original code which points at the next
619: // instruction of our own to be emitted.
620: rangeTable.put(ilbl, duplbl);
621: } else if (findOwner(i) == this ) {
622: // We will emit this instruction, so clear the 'duplbl' flag
623: // since the next Label will refer to a distinct
624: // instruction.
625: duplbl = null;
626: }
627: }
628: }
629:
630: /**
631: * Returns the "owner" of a particular instruction relative to this
632: * instantiation: the owner referes to the Instantiation which will emit
633: * the version of this instruction that we will execute.
634: *
635: * Typically, the return value is either <code>this</code> or
636: * <code>null</code>. <code>this</code> indicates that this
637: * instantiation will generate the version of this instruction that we
638: * will execute, and <code>null</code> indicates that this
639: * instantiation never executes the given instruction.
640: *
641: * Sometimes, however, an instruction can belong to multiple
642: * subroutines; this is called a "dual citizen" instruction (though it
643: * may belong to more than 2 subroutines), and occurs when multiple
644: * subroutines branch to common points of control. In this case, the
645: * owner is the subroutine that appears lowest on the stack, and which
646: * also owns the instruction in question.
647: *
648: * @param i the index of the instruction in the original code
649: * @return the "owner" of a particular instruction relative to this
650: * instantiation.
651: */
652: public Instantiation findOwner(final int i) {
653: if (!subroutine.ownsInstruction(i)) {
654: return null;
655: }
656: if (!dualCitizens.get(i)) {
657: return this ;
658: }
659: Instantiation own = this ;
660: for (Instantiation p = previous; p != null; p = p.previous) {
661: if (p.subroutine.ownsInstruction(i)) {
662: own = p;
663: }
664: }
665: return own;
666: }
667:
668: /**
669: * Looks up the label <code>l</code> in the <code>gotoTable</code>,
670: * thus translating it from a Label in the original code, to a Label in
671: * the inlined code that is appropriate for use by an instruction that
672: * branched to the original label.
673: *
674: * @param l The label we will be translating
675: * @return a label for use by a branch instruction in the inlined code
676: * @see #rangeLabel
677: */
678: public LabelNode gotoLabel(final LabelNode l) {
679: // owner should never be null, because owner is only null
680: // if an instruction cannot be reached from this subroutine
681: Instantiation owner = findOwner(instructions.indexOf(l));
682: return (LabelNode) owner.rangeTable.get(l);
683: }
684:
685: /**
686: * Looks up the label <code>l</code> in the <code>rangeTable</code>,
687: * thus translating it from a Label in the original code, to a Label in
688: * the inlined code that is appropriate for use by an try/catch or
689: * variable use annotation.
690: *
691: * @param l The label we will be translating
692: * @return a label for use by a try/catch or variable annotation in the
693: * original code
694: * @see #rangeTable
695: */
696: public LabelNode rangeLabel(final LabelNode l) {
697: return (LabelNode) rangeTable.get(l);
698: }
699:
700: // AbstractMap implementation
701:
702: public Set entrySet() {
703: return null;
704: }
705:
706: public Object get(final Object o) {
707: return gotoLabel((LabelNode) o);
708: }
709: }
710: }
|