1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * ASM: a very small and fast Java bytecode manipulation framework 32 * Copyright (c) 2000-2011 INRIA, France Telecom 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. Neither the name of the copyright holders nor the names of its 44 * contributors may be used to endorse or promote products derived from 45 * this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 57 * THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 package jdk.internal.org.objectweb.asm.commons; 60 61 import java.util.AbstractMap; 62 import java.util.ArrayList; 63 import java.util.BitSet; 64 import java.util.HashMap; 65 import java.util.Iterator; 66 import java.util.LinkedList; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.Set; 70 71 import jdk.internal.org.objectweb.asm.Label; 72 import jdk.internal.org.objectweb.asm.MethodVisitor; 73 import jdk.internal.org.objectweb.asm.Opcodes; 74 import jdk.internal.org.objectweb.asm.Type; 75 import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; 76 import jdk.internal.org.objectweb.asm.tree.InsnList; 77 import jdk.internal.org.objectweb.asm.tree.InsnNode; 78 import jdk.internal.org.objectweb.asm.tree.JumpInsnNode; 79 import jdk.internal.org.objectweb.asm.tree.LabelNode; 80 import jdk.internal.org.objectweb.asm.tree.LocalVariableNode; 81 import jdk.internal.org.objectweb.asm.tree.LookupSwitchInsnNode; 82 import jdk.internal.org.objectweb.asm.tree.MethodNode; 83 import jdk.internal.org.objectweb.asm.tree.TableSwitchInsnNode; 84 import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; 85 86 /** 87 * A {@link jdk.internal.org.objectweb.asm.MethodVisitor} that removes JSR instructions and 88 * inlines the referenced subroutines. 89 * 90 * <b>Explanation of how it works</b> TODO 91 * 92 * @author Niko Matsakis 93 */ 94 public class JSRInlinerAdapter extends MethodNode implements Opcodes { 95 96 private static final boolean LOGGING = false; 97 98 /** 99 * For each label that is jumped to by a JSR, we create a BitSet instance. 100 */ 101 private final Map<LabelNode, BitSet> subroutineHeads = new HashMap<LabelNode, BitSet>(); 102 103 /** 104 * This subroutine instance denotes the line of execution that is not 105 * contained within any subroutine; i.e., the "subroutine" that is executing 106 * when a method first begins. 107 */ 108 private final BitSet mainSubroutine = new BitSet(); 109 110 /** 111 * This BitSet contains the index of every instruction that belongs to more 112 * than one subroutine. This should not happen often. 113 */ 114 final BitSet dualCitizens = new BitSet(); 115 116 /** 117 * Creates a new JSRInliner. <i>Subclasses must not use this 118 * constructor</i>. Instead, they must use the 119 * {@link #JSRInlinerAdapter(int, MethodVisitor, int, String, String, String, String[])} 120 * version. 121 * 122 * @param mv 123 * the <code>MethodVisitor</code> to send the resulting inlined 124 * method code to (use <code>null</code> for none). 125 * @param access 126 * the method's access flags (see {@link Opcodes}). This 127 * parameter also indicates if the method is synthetic and/or 128 * deprecated. 129 * @param name 130 * the method's name. 131 * @param desc 132 * the method's descriptor (see {@link Type}). 133 * @param signature 134 * the method's signature. May be <tt>null</tt>. 135 * @param exceptions 136 * the internal names of the method's exception classes (see 137 * {@link Type#getInternalName() getInternalName}). May be 138 * <tt>null</tt>. 139 */ 140 public JSRInlinerAdapter(final MethodVisitor mv, final int access, 141 final String name, final String desc, final String signature, 142 final String[] exceptions) { 143 this(Opcodes.ASM5, mv, access, name, desc, signature, exceptions); 144 } 145 146 /** 147 * Creates a new JSRInliner. 148 * 149 * @param api 150 * the ASM API version implemented by this visitor. Must be one 151 * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. 152 * @param mv 153 * the <code>MethodVisitor</code> to send the resulting inlined 154 * method code to (use <code>null</code> for none). 155 * @param access 156 * the method's access flags (see {@link Opcodes}). This 157 * parameter also indicates if the method is synthetic and/or 158 * deprecated. 159 * @param name 160 * the method's name. 161 * @param desc 162 * the method's descriptor (see {@link Type}). 163 * @param signature 164 * the method's signature. May be <tt>null</tt>. 165 * @param exceptions 166 * the internal names of the method's exception classes (see 167 * {@link Type#getInternalName() getInternalName}). May be 168 * <tt>null</tt>. 169 */ 170 protected JSRInlinerAdapter(final int api, final MethodVisitor mv, 171 final int access, final String name, final String desc, 172 final String signature, final String[] exceptions) { 173 super(api, access, name, desc, signature, exceptions); 174 this.mv = mv; 175 } 176 177 /** 178 * Detects a JSR instruction and sets a flag to indicate we will need to do 179 * inlining. 180 */ 181 @Override 182 public void visitJumpInsn(final int opcode, final Label lbl) { 183 super.visitJumpInsn(opcode, lbl); 184 LabelNode ln = ((JumpInsnNode) instructions.getLast()).label; 185 if (opcode == JSR && !subroutineHeads.containsKey(ln)) { 186 subroutineHeads.put(ln, new BitSet()); 187 } 188 } 189 190 /** 191 * If any JSRs were seen, triggers the inlining process. Otherwise, forwards 192 * the byte codes untouched. 193 */ 194 @Override 195 public void visitEnd() { 196 if (!subroutineHeads.isEmpty()) { 197 markSubroutines(); 198 if (LOGGING) { 199 log(mainSubroutine.toString()); 200 Iterator<BitSet> it = subroutineHeads.values().iterator(); 201 while (it.hasNext()) { 202 BitSet sub = it.next(); 203 log(sub.toString()); 204 } 205 } 206 emitCode(); 207 } 208 209 // Forward the translate opcodes on if appropriate: 210 if (mv != null) { 211 accept(mv); 212 } 213 } 214 215 /** 216 * Walks the method and determines which internal subroutine(s), if any, 217 * each instruction is a method of. 218 */ 219 private void markSubroutines() { 220 BitSet anyvisited = new BitSet(); 221 222 // First walk the main subroutine and find all those instructions which 223 // can be reached without invoking any JSR at all 224 markSubroutineWalk(mainSubroutine, 0, anyvisited); 225 226 // Go through the head of each subroutine and find any nodes reachable 227 // to that subroutine without following any JSR links. 228 for (Iterator<Map.Entry<LabelNode, BitSet>> it = subroutineHeads 229 .entrySet().iterator(); it.hasNext();) { 230 Map.Entry<LabelNode, BitSet> entry = it.next(); 231 LabelNode lab = entry.getKey(); 232 BitSet sub = entry.getValue(); 233 int index = instructions.indexOf(lab); 234 markSubroutineWalk(sub, index, anyvisited); 235 } 236 } 237 238 /** 239 * Performs a depth first search walking the normal byte code path starting 240 * at <code>index</code>, and adding each instruction encountered into the 241 * subroutine <code>sub</code>. After this walk is complete, iterates over 242 * the exception handlers to ensure that we also include those byte codes 243 * which are reachable through an exception that may be thrown during the 244 * execution of the subroutine. Invoked from <code>markSubroutines()</code>. 245 * 246 * @param sub 247 * the subroutine whose instructions must be computed. 248 * @param index 249 * an instruction of this subroutine. 250 * @param anyvisited 251 * indexes of the already visited instructions, i.e. marked as 252 * part of this subroutine or any previously computed subroutine. 253 */ 254 private void markSubroutineWalk(final BitSet sub, final int index, 255 final BitSet anyvisited) { 256 if (LOGGING) { 257 log("markSubroutineWalk: sub=" + sub + " index=" + index); 258 } 259 260 // First find those instructions reachable via normal execution 261 markSubroutineWalkDFS(sub, index, anyvisited); 262 263 // Now, make sure we also include any applicable exception handlers 264 boolean loop = true; 265 while (loop) { 266 loop = false; 267 for (Iterator<TryCatchBlockNode> it = tryCatchBlocks.iterator(); it 268 .hasNext();) { 269 TryCatchBlockNode trycatch = it.next(); 270 271 if (LOGGING) { 272 // TODO use of default toString(). 273 log("Scanning try/catch " + trycatch); 274 } 275 276 // If the handler has already been processed, skip it. 277 int handlerindex = instructions.indexOf(trycatch.handler); 278 if (sub.get(handlerindex)) { 279 continue; 280 } 281 282 int startindex = instructions.indexOf(trycatch.start); 283 int endindex = instructions.indexOf(trycatch.end); 284 int nextbit = sub.nextSetBit(startindex); 285 if (nextbit != -1 && nextbit < endindex) { 286 if (LOGGING) { 287 log("Adding exception handler: " + startindex + '-' 288 + endindex + " due to " + nextbit + " handler " 289 + handlerindex); 290 } 291 markSubroutineWalkDFS(sub, handlerindex, anyvisited); 292 loop = true; 293 } 294 } 295 } 296 } 297 298 /** 299 * Performs a simple DFS of the instructions, assigning each to the 300 * subroutine <code>sub</code>. Starts from <code>index</code>. Invoked only 301 * by <code>markSubroutineWalk()</code>. 302 * 303 * @param sub 304 * the subroutine whose instructions must be computed. 305 * @param index 306 * an instruction of this subroutine. 307 * @param anyvisited 308 * indexes of the already visited instructions, i.e. marked as 309 * part of this subroutine or any previously computed subroutine. 310 */ 311 private void markSubroutineWalkDFS(final BitSet sub, int index, 312 final BitSet anyvisited) { 313 while (true) { 314 AbstractInsnNode node = instructions.get(index); 315 316 // don't visit a node twice 317 if (sub.get(index)) { 318 return; 319 } 320 sub.set(index); 321 322 // check for those nodes already visited by another subroutine 323 if (anyvisited.get(index)) { 324 dualCitizens.set(index); 325 if (LOGGING) { 326 log("Instruction #" + index + " is dual citizen."); 327 } 328 } 329 anyvisited.set(index); 330 331 if (node.getType() == AbstractInsnNode.JUMP_INSN 332 && node.getOpcode() != JSR) { 333 // we do not follow recursively called subroutines here; but any 334 // other sort of branch we do follow 335 JumpInsnNode jnode = (JumpInsnNode) node; 336 int destidx = instructions.indexOf(jnode.label); 337 markSubroutineWalkDFS(sub, destidx, anyvisited); 338 } 339 if (node.getType() == AbstractInsnNode.TABLESWITCH_INSN) { 340 TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node; 341 int destidx = instructions.indexOf(tsnode.dflt); 342 markSubroutineWalkDFS(sub, destidx, anyvisited); 343 for (int i = tsnode.labels.size() - 1; i >= 0; --i) { 344 LabelNode l = tsnode.labels.get(i); 345 destidx = instructions.indexOf(l); 346 markSubroutineWalkDFS(sub, destidx, anyvisited); 347 } 348 } 349 if (node.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) { 350 LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node; 351 int destidx = instructions.indexOf(lsnode.dflt); 352 markSubroutineWalkDFS(sub, destidx, anyvisited); 353 for (int i = lsnode.labels.size() - 1; i >= 0; --i) { 354 LabelNode l = lsnode.labels.get(i); 355 destidx = instructions.indexOf(l); 356 markSubroutineWalkDFS(sub, destidx, anyvisited); 357 } 358 } 359 360 // check to see if this opcode falls through to the next instruction 361 // or not; if not, return. 362 switch (instructions.get(index).getOpcode()) { 363 case GOTO: 364 case RET: 365 case TABLESWITCH: 366 case LOOKUPSWITCH: 367 case IRETURN: 368 case LRETURN: 369 case FRETURN: 370 case DRETURN: 371 case ARETURN: 372 case RETURN: 373 case ATHROW: 374 /* 375 * note: this either returns from this subroutine, or a parent 376 * subroutine which invoked it 377 */ 378 return; 379 } 380 381 // Use tail recursion here in the form of an outer while loop to 382 // avoid our stack growing needlessly: 383 index++; 384 } 385 } 386 387 /** 388 * Creates the new instructions, inlining each instantiation of each 389 * subroutine until the code is fully elaborated. 390 */ 391 private void emitCode() { 392 LinkedList<Instantiation> worklist = new LinkedList<Instantiation>(); 393 // Create an instantiation of the "root" subroutine, which is just the 394 // main routine 395 worklist.add(new Instantiation(null, mainSubroutine)); 396 397 // Emit instantiations of each subroutine we encounter, including the 398 // main subroutine 399 InsnList newInstructions = new InsnList(); 400 List<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<TryCatchBlockNode>(); 401 List<LocalVariableNode> newLocalVariables = new ArrayList<LocalVariableNode>(); 402 while (!worklist.isEmpty()) { 403 Instantiation inst = worklist.removeFirst(); 404 emitSubroutine(inst, worklist, newInstructions, newTryCatchBlocks, 405 newLocalVariables); 406 } 407 instructions = newInstructions; 408 tryCatchBlocks = newTryCatchBlocks; 409 localVariables = newLocalVariables; 410 } 411 412 /** 413 * Emits one instantiation of one subroutine, specified by 414 * <code>instant</code>. May add new instantiations that are invoked by this 415 * one to the <code>worklist</code> parameter, and new try/catch blocks to 416 * <code>newTryCatchBlocks</code>. 417 * 418 * @param instant 419 * the instantiation that must be performed. 420 * @param worklist 421 * list of the instantiations that remain to be done. 422 * @param newInstructions 423 * the instruction list to which the instantiated code must be 424 * appended. 425 * @param newTryCatchBlocks 426 * the exception handler list to which the instantiated handlers 427 * must be appended. 428 */ 429 private void emitSubroutine(final Instantiation instant, 430 final List<Instantiation> worklist, final InsnList newInstructions, 431 final List<TryCatchBlockNode> newTryCatchBlocks, 432 final List<LocalVariableNode> newLocalVariables) { 433 LabelNode duplbl = null; 434 435 if (LOGGING) { 436 log("--------------------------------------------------------"); 437 log("Emitting instantiation of subroutine " + instant.subroutine); 438 } 439 440 // Emit the relevant instructions for this instantiation, translating 441 // labels and jump targets as we go: 442 for (int i = 0, c = instructions.size(); i < c; i++) { 443 AbstractInsnNode insn = instructions.get(i); 444 Instantiation owner = instant.findOwner(i); 445 446 // Always remap labels: 447 if (insn.getType() == AbstractInsnNode.LABEL) { 448 // Translate labels into their renamed equivalents. 449 // Avoid adding the same label more than once. Note 450 // that because we own this instruction the gotoTable 451 // and the rangeTable will always agree. 452 LabelNode ilbl = (LabelNode) insn; 453 LabelNode remap = instant.rangeLabel(ilbl); 454 if (LOGGING) { 455 // TODO use of default toString(). 456 log("Translating lbl #" + i + ':' + ilbl + " to " + remap); 457 } 458 if (remap != duplbl) { 459 newInstructions.add(remap); 460 duplbl = remap; 461 } 462 continue; 463 } 464 465 // We don't want to emit instructions that were already 466 // emitted by a subroutine higher on the stack. Note that 467 // it is still possible for a given instruction to be 468 // emitted twice because it may belong to two subroutines 469 // that do not invoke each other. 470 if (owner != instant) { 471 continue; 472 } 473 474 if (LOGGING) { 475 log("Emitting inst #" + i); 476 } 477 478 if (insn.getOpcode() == RET) { 479 // Translate RET instruction(s) to a jump to the return label 480 // for the appropriate instantiation. The problem is that the 481 // subroutine may "fall through" to the ret of a parent 482 // subroutine; therefore, to find the appropriate ret label we 483 // find the lowest subroutine on the stack that claims to own 484 // this instruction. See the class javadoc comment for an 485 // explanation on why this technique is safe (note: it is only 486 // safe if the input is verifiable). 487 LabelNode retlabel = null; 488 for (Instantiation p = instant; p != null; p = p.previous) { 489 if (p.subroutine.get(i)) { 490 retlabel = p.returnLabel; 491 } 492 } 493 if (retlabel == null) { 494 // This is only possible if the mainSubroutine owns a RET 495 // instruction, which should never happen for verifiable 496 // code. 497 throw new RuntimeException("Instruction #" + i 498 + " is a RET not owned by any subroutine"); 499 } 500 newInstructions.add(new JumpInsnNode(GOTO, retlabel)); 501 } else if (insn.getOpcode() == JSR) { 502 LabelNode lbl = ((JumpInsnNode) insn).label; 503 BitSet sub = subroutineHeads.get(lbl); 504 Instantiation newinst = new Instantiation(instant, sub); 505 LabelNode startlbl = newinst.gotoLabel(lbl); 506 507 if (LOGGING) { 508 log(" Creating instantiation of subr " + sub); 509 } 510 511 // Rather than JSRing, we will jump to the inline version and 512 // push NULL for what was once the return value. This hack 513 // allows us to avoid doing any sort of data flow analysis to 514 // figure out which instructions manipulate the old return value 515 // pointer which is now known to be unneeded. 516 newInstructions.add(new InsnNode(ACONST_NULL)); 517 newInstructions.add(new JumpInsnNode(GOTO, startlbl)); 518 newInstructions.add(newinst.returnLabel); 519 520 // Insert this new instantiation into the queue to be emitted 521 // later. 522 worklist.add(newinst); 523 } else { 524 newInstructions.add(insn.clone(instant)); 525 } 526 } 527 528 // Emit try/catch blocks that are relevant to this method. 529 for (Iterator<TryCatchBlockNode> it = tryCatchBlocks.iterator(); it 530 .hasNext();) { 531 TryCatchBlockNode trycatch = it.next(); 532 533 if (LOGGING) { 534 // TODO use of default toString(). 535 log("try catch block original labels=" + trycatch.start + '-' 536 + trycatch.end + "->" + trycatch.handler); 537 } 538 539 final LabelNode start = instant.rangeLabel(trycatch.start); 540 final LabelNode end = instant.rangeLabel(trycatch.end); 541 542 // Ignore empty try/catch regions 543 if (start == end) { 544 if (LOGGING) { 545 log(" try catch block empty in this subroutine"); 546 } 547 continue; 548 } 549 550 final LabelNode handler = instant.gotoLabel(trycatch.handler); 551 552 if (LOGGING) { 553 // TODO use of default toString(). 554 log(" try catch block new labels=" + start + '-' + end + "->" 555 + handler); 556 } 557 558 if (start == null || end == null || handler == null) { 559 throw new RuntimeException("Internal error!"); 560 } 561 562 newTryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, 563 trycatch.type)); 564 } 565 566 for (Iterator<LocalVariableNode> it = localVariables.iterator(); it 567 .hasNext();) { 568 LocalVariableNode lvnode = it.next(); 569 if (LOGGING) { 570 log("local var " + lvnode.name); 571 } 572 final LabelNode start = instant.rangeLabel(lvnode.start); 573 final LabelNode end = instant.rangeLabel(lvnode.end); 574 if (start == end) { 575 if (LOGGING) { 576 log(" local variable empty in this sub"); 577 } 578 continue; 579 } 580 newLocalVariables.add(new LocalVariableNode(lvnode.name, 581 lvnode.desc, lvnode.signature, start, end, lvnode.index)); 582 } 583 } 584 585 private static void log(final String str) { 586 System.err.println(str); 587 } 588 589 /** 590 * A class that represents an instantiation of a subroutine. Each 591 * instantiation has an associate "stack" --- which is a listing of those 592 * instantiations that were active when this particular instance of this 593 * subroutine was invoked. Each instantiation also has a map from the 594 * original labels of the program to the labels appropriate for this 595 * instantiation, and finally a label to return to. 596 */ 597 private class Instantiation extends AbstractMap<LabelNode, LabelNode> { 598 599 /** 600 * Previous instantiations; the stack must be statically predictable to 601 * be inlinable. 602 */ 603 final Instantiation previous; 604 605 /** 606 * The subroutine this is an instantiation of. 607 */ 608 public final BitSet subroutine; 609 610 /** 611 * This table maps Labels from the original source to Labels pointing at 612 * code specific to this instantiation, for use in remapping try/catch 613 * blocks,as well as gotos. 614 * 615 * Note that in the presence of dual citizens instructions, that is, 616 * instructions which belong to more than one subroutine due to the 617 * merging of control flow without a RET instruction, we will map the 618 * target label of a GOTO to the label used by the instantiation lowest 619 * on the stack. This avoids code duplication during inlining in most 620 * cases. 621 * 622 * @see #findOwner(int) 623 */ 624 public final Map<LabelNode, LabelNode> rangeTable = new HashMap<LabelNode, LabelNode>(); 625 626 /** 627 * All returns for this instantiation will be mapped to this label 628 */ 629 public final LabelNode returnLabel; 630 631 Instantiation(final Instantiation prev, final BitSet sub) { 632 previous = prev; 633 subroutine = sub; 634 for (Instantiation p = prev; p != null; p = p.previous) { 635 if (p.subroutine == sub) { 636 throw new RuntimeException("Recursive invocation of " + sub); 637 } 638 } 639 640 // Determine the label to return to when this subroutine terminates 641 // via RET: note that the main subroutine never terminates via RET. 642 if (prev != null) { 643 returnLabel = new LabelNode(); 644 } else { 645 returnLabel = null; 646 } 647 648 // Each instantiation will remap the labels from the code above to 649 // refer to its particular copy of its own instructions. Note that 650 // we collapse labels which point at the same instruction into one: 651 // this is fairly common as we are often ignoring large chunks of 652 // instructions, so what were previously distinct labels become 653 // duplicates. 654 LabelNode duplbl = null; 655 for (int i = 0, c = instructions.size(); i < c; i++) { 656 AbstractInsnNode insn = instructions.get(i); 657 658 if (insn.getType() == AbstractInsnNode.LABEL) { 659 LabelNode ilbl = (LabelNode) insn; 660 661 if (duplbl == null) { 662 // if we already have a label pointing at this spot, 663 // don't recreate it. 664 duplbl = new LabelNode(); 665 } 666 667 // Add an entry in the rangeTable for every label 668 // in the original code which points at the next 669 // instruction of our own to be emitted. 670 rangeTable.put(ilbl, duplbl); 671 } else if (findOwner(i) == this) { 672 // We will emit this instruction, so clear the 'duplbl' flag 673 // since the next Label will refer to a distinct 674 // instruction. 675 duplbl = null; 676 } 677 } 678 } 679 680 /** 681 * Returns the "owner" of a particular instruction relative to this 682 * instantiation: the owner referes to the Instantiation which will emit 683 * the version of this instruction that we will execute. 684 * 685 * Typically, the return value is either <code>this</code> or 686 * <code>null</code>. <code>this</code> indicates that this 687 * instantiation will generate the version of this instruction that we 688 * will execute, and <code>null</code> indicates that this instantiation 689 * never executes the given instruction. 690 * 691 * Sometimes, however, an instruction can belong to multiple 692 * subroutines; this is called a "dual citizen" instruction (though it 693 * may belong to more than 2 subroutines), and occurs when multiple 694 * subroutines branch to common points of control. In this case, the 695 * owner is the subroutine that appears lowest on the stack, and which 696 * also owns the instruction in question. 697 * 698 * @param i 699 * the index of the instruction in the original code 700 * @return the "owner" of a particular instruction relative to this 701 * instantiation. 702 */ 703 public Instantiation findOwner(final int i) { 704 if (!subroutine.get(i)) { 705 return null; 706 } 707 if (!dualCitizens.get(i)) { 708 return this; 709 } 710 Instantiation own = this; 711 for (Instantiation p = previous; p != null; p = p.previous) { 712 if (p.subroutine.get(i)) { 713 own = p; 714 } 715 } 716 return own; 717 } 718 719 /** 720 * Looks up the label <code>l</code> in the <code>gotoTable</code>, thus 721 * translating it from a Label in the original code, to a Label in the 722 * inlined code that is appropriate for use by an instruction that 723 * branched to the original label. 724 * 725 * @param l 726 * The label we will be translating 727 * @return a label for use by a branch instruction in the inlined code 728 * @see #rangeLabel 729 */ 730 public LabelNode gotoLabel(final LabelNode l) { 731 // owner should never be null, because owner is only null 732 // if an instruction cannot be reached from this subroutine 733 Instantiation owner = findOwner(instructions.indexOf(l)); 734 return owner.rangeTable.get(l); 735 } 736 737 /** 738 * Looks up the label <code>l</code> in the <code>rangeTable</code>, 739 * thus translating it from a Label in the original code, to a Label in 740 * the inlined code that is appropriate for use by an try/catch or 741 * variable use annotation. 742 * 743 * @param l 744 * The label we will be translating 745 * @return a label for use by a try/catch or variable annotation in the 746 * original code 747 * @see #rangeTable 748 */ 749 public LabelNode rangeLabel(final LabelNode l) { 750 return rangeTable.get(l); 751 } 752 753 // AbstractMap implementation 754 755 @Override 756 public Set<Map.Entry<LabelNode, LabelNode>> entrySet() { 757 return null; 758 } 759 760 @Override 761 public LabelNode get(final Object o) { 762 return gotoLabel((LabelNode) o); 763 } 764 } 765 }