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