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