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.LinkedList; 66 import java.util.List; 67 import java.util.Map; 68 import java.util.Set; 69 import jdk.internal.org.objectweb.asm.Label; 70 import jdk.internal.org.objectweb.asm.MethodVisitor; 71 import jdk.internal.org.objectweb.asm.Opcodes; 72 import jdk.internal.org.objectweb.asm.tree.AbstractInsnNode; 73 import jdk.internal.org.objectweb.asm.tree.InsnList; 74 import jdk.internal.org.objectweb.asm.tree.InsnNode; 75 import jdk.internal.org.objectweb.asm.tree.JumpInsnNode; 76 import jdk.internal.org.objectweb.asm.tree.LabelNode; 77 import jdk.internal.org.objectweb.asm.tree.LocalVariableNode; 78 import jdk.internal.org.objectweb.asm.tree.LookupSwitchInsnNode; 79 import jdk.internal.org.objectweb.asm.tree.MethodNode; 80 import jdk.internal.org.objectweb.asm.tree.TableSwitchInsnNode; 81 import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; 82 83 /** 84 * A {@link jdk.internal.org.objectweb.asm.MethodVisitor} that removes JSR instructions and inlines the 85 * referenced subroutines. 86 * 87 * @author Niko Matsakis 88 */ 89 // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility). 90 public class JSRInlinerAdapter extends MethodNode implements Opcodes { 91 92 /** 93 * The instructions that belong to the main "subroutine". Bit i is set iff instruction at index i 94 * belongs to this main "subroutine". 95 */ 96 private final BitSet mainSubroutineInsns = new BitSet(); 97 98 /** 99 * The instructions that belong to each subroutine. For each label which is the target of a JSR 100 * instruction, bit i of the corresponding BitSet in this map is set iff instruction at index i 101 * belongs to this subroutine. 102 */ 103 private final Map<LabelNode, BitSet> subroutinesInsns = new HashMap<LabelNode, BitSet>(); 104 105 /** 106 * The instructions that belong to more that one subroutine. Bit i is set iff instruction at index 107 * i belongs to more than one subroutine. 108 */ 109 final BitSet sharedSubroutineInsns = new BitSet(); 110 111 /** 112 * Constructs a new {@link JSRInlinerAdapter}. <i>Subclasses must not use this constructor</i>. 113 * Instead, they must use the {@link #JSRInlinerAdapter(int, MethodVisitor, int, String, String, 114 * String, String[])} version. 115 * 116 * @param methodVisitor the method visitor to send the resulting inlined method code to, or <code> 117 * null</code>. 118 * @param access the method's access flags. 119 * @param name the method's name. 120 * @param descriptor the method's descriptor. 121 * @param signature the method's signature. May be {@literal null}. 122 * @param exceptions the internal names of the method's exception classes. May be {@literal null}. 123 * @throws IllegalStateException if a subclass calls this constructor. 124 */ JSRInlinerAdapter( final MethodVisitor methodVisitor, final int access, final String name, final String descriptor, final String signature, final String[] exceptions)125 public JSRInlinerAdapter( 126 final MethodVisitor methodVisitor, 127 final int access, 128 final String name, 129 final String descriptor, 130 final String signature, 131 final String[] exceptions) { 132 this(Opcodes.ASM7, methodVisitor, access, name, descriptor, signature, exceptions); 133 if (getClass() != JSRInlinerAdapter.class) { 134 throw new IllegalStateException(); 135 } 136 } 137 138 /** 139 * Constructs a new {@link JSRInlinerAdapter}. 140 * 141 * @param api the ASM API version implemented by this visitor. Must be one of {@link 142 * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. 143 * @param methodVisitor the method visitor to send the resulting inlined method code to, or <code> 144 * null</code>. 145 * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if 146 * the method is synthetic and/or deprecated. 147 * @param name the method's name. 148 * @param descriptor the method's descriptor. 149 * @param signature the method's signature. May be {@literal null}. 150 * @param exceptions the internal names of the method's exception classes. May be {@literal null}. 151 */ JSRInlinerAdapter( final int api, final MethodVisitor methodVisitor, final int access, final String name, final String descriptor, final String signature, final String[] exceptions)152 protected JSRInlinerAdapter( 153 final int api, 154 final MethodVisitor methodVisitor, 155 final int access, 156 final String name, 157 final String descriptor, 158 final String signature, 159 final String[] exceptions) { 160 super(api, access, name, descriptor, signature, exceptions); 161 this.mv = methodVisitor; 162 } 163 164 @Override visitJumpInsn(final int opcode, final Label label)165 public void visitJumpInsn(final int opcode, final Label label) { 166 super.visitJumpInsn(opcode, label); 167 LabelNode labelNode = ((JumpInsnNode) instructions.getLast()).label; 168 if (opcode == JSR && !subroutinesInsns.containsKey(labelNode)) { 169 subroutinesInsns.put(labelNode, new BitSet()); 170 } 171 } 172 173 @Override visitEnd()174 public void visitEnd() { 175 if (!subroutinesInsns.isEmpty()) { 176 // If the code contains at least one JSR instruction, inline the subroutines. 177 findSubroutinesInsns(); 178 emitCode(); 179 } 180 if (mv != null) { 181 accept(mv); 182 } 183 } 184 185 /** Determines, for each instruction, to which subroutine(s) it belongs. */ findSubroutinesInsns()186 private void findSubroutinesInsns() { 187 // Find the instructions that belong to main subroutine. 188 BitSet visitedInsns = new BitSet(); 189 findSubroutineInsns(0, mainSubroutineInsns, visitedInsns); 190 // For each subroutine, find the instructions that belong to this subroutine. 191 for (Map.Entry<LabelNode, BitSet> entry : subroutinesInsns.entrySet()) { 192 LabelNode jsrLabelNode = entry.getKey(); 193 BitSet subroutineInsns = entry.getValue(); 194 findSubroutineInsns(instructions.indexOf(jsrLabelNode), subroutineInsns, visitedInsns); 195 } 196 } 197 198 /** 199 * Finds the instructions that belong to the subroutine starting at the given instruction index. 200 * For this the control flow graph is visited with a depth first search (this includes the normal 201 * control flow and the exception handlers). 202 * 203 * @param startInsnIndex the index of the first instruction of the subroutine. 204 * @param subroutineInsns where the indices of the instructions of the subroutine must be stored. 205 * @param visitedInsns the indices of the instructions that have been visited so far (including in 206 * previous calls to this method). This bitset is updated by this method each time a new 207 * instruction is visited. It is used to make sure each instruction is visited at most once. 208 */ findSubroutineInsns( final int startInsnIndex, final BitSet subroutineInsns, final BitSet visitedInsns)209 private void findSubroutineInsns( 210 final int startInsnIndex, final BitSet subroutineInsns, final BitSet visitedInsns) { 211 // First find the instructions reachable via normal execution. 212 findReachableInsns(startInsnIndex, subroutineInsns, visitedInsns); 213 214 // Then find the instructions reachable via the applicable exception handlers. 215 while (true) { 216 boolean applicableHandlerFound = false; 217 for (TryCatchBlockNode tryCatchBlockNode : tryCatchBlocks) { 218 // If the handler has already been processed, skip it. 219 int handlerIndex = instructions.indexOf(tryCatchBlockNode.handler); 220 if (subroutineInsns.get(handlerIndex)) { 221 continue; 222 } 223 224 // If an instruction in the exception handler range belongs to the subroutine, the handler 225 // can be reached from the routine, and its instructions must be added to the subroutine. 226 int startIndex = instructions.indexOf(tryCatchBlockNode.start); 227 int endIndex = instructions.indexOf(tryCatchBlockNode.end); 228 int firstSubroutineInsnAfterTryCatchStart = subroutineInsns.nextSetBit(startIndex); 229 if (firstSubroutineInsnAfterTryCatchStart >= startIndex 230 && firstSubroutineInsnAfterTryCatchStart < endIndex) { 231 findReachableInsns(handlerIndex, subroutineInsns, visitedInsns); 232 applicableHandlerFound = true; 233 } 234 } 235 // If an applicable exception handler has been found, other handlers may become applicable, so 236 // we must examine them again. 237 if (!applicableHandlerFound) { 238 return; 239 } 240 } 241 } 242 243 /** 244 * Finds the instructions that are reachable from the given instruction, without following any JSR 245 * instruction nor any exception handler. For this the control flow graph is visited with a depth 246 * first search. 247 * 248 * @param insnIndex the index of an instruction of the subroutine. 249 * @param subroutineInsns where the indices of the instructions of the subroutine must be stored. 250 * @param visitedInsns the indices of the instructions that have been visited so far (including in 251 * previous calls to this method). This bitset is updated by this method each time a new 252 * instruction is visited. It is used to make sure each instruction is visited at most once. 253 */ findReachableInsns( final int insnIndex, final BitSet subroutineInsns, final BitSet visitedInsns)254 private void findReachableInsns( 255 final int insnIndex, final BitSet subroutineInsns, final BitSet visitedInsns) { 256 int currentInsnIndex = insnIndex; 257 // We implicitly assume below that execution can always fall through to the next instruction 258 // after a JSR. But a subroutine may never return, in which case the code after the JSR is 259 // unreachable and can be anything. In particular, it can seem to fall off the end of the 260 // method, so we must handle this case here (we could instead detect whether execution can 261 // return or not from a JSR, but this is more complicated). 262 while (currentInsnIndex < instructions.size()) { 263 // Visit each instruction at most once. 264 if (subroutineInsns.get(currentInsnIndex)) { 265 return; 266 } 267 subroutineInsns.set(currentInsnIndex); 268 269 // Check if this instruction has already been visited by another subroutine. 270 if (visitedInsns.get(currentInsnIndex)) { 271 sharedSubroutineInsns.set(currentInsnIndex); 272 } 273 visitedInsns.set(currentInsnIndex); 274 275 AbstractInsnNode currentInsnNode = instructions.get(currentInsnIndex); 276 if (currentInsnNode.getType() == AbstractInsnNode.JUMP_INSN 277 && currentInsnNode.getOpcode() != JSR) { 278 // Don't follow JSR instructions in the control flow graph. 279 JumpInsnNode jumpInsnNode = (JumpInsnNode) currentInsnNode; 280 findReachableInsns(instructions.indexOf(jumpInsnNode.label), subroutineInsns, visitedInsns); 281 } else if (currentInsnNode.getType() == AbstractInsnNode.TABLESWITCH_INSN) { 282 TableSwitchInsnNode tableSwitchInsnNode = (TableSwitchInsnNode) currentInsnNode; 283 findReachableInsns( 284 instructions.indexOf(tableSwitchInsnNode.dflt), subroutineInsns, visitedInsns); 285 for (LabelNode labelNode : tableSwitchInsnNode.labels) { 286 findReachableInsns(instructions.indexOf(labelNode), subroutineInsns, visitedInsns); 287 } 288 } else if (currentInsnNode.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN) { 289 LookupSwitchInsnNode lookupSwitchInsnNode = (LookupSwitchInsnNode) currentInsnNode; 290 findReachableInsns( 291 instructions.indexOf(lookupSwitchInsnNode.dflt), subroutineInsns, visitedInsns); 292 for (LabelNode labelNode : lookupSwitchInsnNode.labels) { 293 findReachableInsns(instructions.indexOf(labelNode), subroutineInsns, visitedInsns); 294 } 295 } 296 297 // Check if this instruction falls through to the next instruction; if not, return. 298 switch (instructions.get(currentInsnIndex).getOpcode()) { 299 case GOTO: 300 case RET: 301 case TABLESWITCH: 302 case LOOKUPSWITCH: 303 case IRETURN: 304 case LRETURN: 305 case FRETURN: 306 case DRETURN: 307 case ARETURN: 308 case RETURN: 309 case ATHROW: 310 // Note: this either returns from this subroutine, or from a parent subroutine. 311 return; 312 default: 313 // Go to the next instruction. 314 currentInsnIndex++; 315 break; 316 } 317 } 318 } 319 320 /** 321 * Creates the new instructions, inlining each instantiation of each subroutine until the code is 322 * fully elaborated. 323 */ emitCode()324 private void emitCode() { 325 LinkedList<Instantiation> worklist = new LinkedList<Instantiation>(); 326 // Create an instantiation of the main "subroutine", which is just the main routine. 327 worklist.add(new Instantiation(null, mainSubroutineInsns)); 328 329 // Emit instantiations of each subroutine we encounter, including the main subroutine. 330 InsnList newInstructions = new InsnList(); 331 List<TryCatchBlockNode> newTryCatchBlocks = new ArrayList<TryCatchBlockNode>(); 332 List<LocalVariableNode> newLocalVariables = new ArrayList<LocalVariableNode>(); 333 while (!worklist.isEmpty()) { 334 Instantiation instantiation = worklist.removeFirst(); 335 emitInstantiation( 336 instantiation, worklist, newInstructions, newTryCatchBlocks, newLocalVariables); 337 } 338 instructions = newInstructions; 339 tryCatchBlocks = newTryCatchBlocks; 340 localVariables = newLocalVariables; 341 } 342 343 /** 344 * Emits an instantiation of a subroutine, specified by <code>instantiation</code>. May add new 345 * instantiations that are invoked by this one to the <code>worklist</code>, and new try/catch 346 * blocks to <code>newTryCatchBlocks</code>. 347 * 348 * @param instantiation the instantiation that must be performed. 349 * @param worklist list of the instantiations that remain to be done. 350 * @param newInstructions the instruction list to which the instantiated code must be appended. 351 * @param newTryCatchBlocks the exception handler list to which the instantiated handlers must be 352 * appended. 353 * @param newLocalVariables the local variables list to which the instantiated local variables 354 * must be appended. 355 */ emitInstantiation( final Instantiation instantiation, final List<Instantiation> worklist, final InsnList newInstructions, final List<TryCatchBlockNode> newTryCatchBlocks, final List<LocalVariableNode> newLocalVariables)356 private void emitInstantiation( 357 final Instantiation instantiation, 358 final List<Instantiation> worklist, 359 final InsnList newInstructions, 360 final List<TryCatchBlockNode> newTryCatchBlocks, 361 final List<LocalVariableNode> newLocalVariables) { 362 LabelNode previousLabelNode = null; 363 for (int i = 0; i < instructions.size(); ++i) { 364 AbstractInsnNode insnNode = instructions.get(i); 365 if (insnNode.getType() == AbstractInsnNode.LABEL) { 366 // Always clone all labels, while avoiding to add the same label more than once. 367 LabelNode labelNode = (LabelNode) insnNode; 368 LabelNode clonedLabelNode = instantiation.getClonedLabel(labelNode); 369 if (clonedLabelNode != previousLabelNode) { 370 newInstructions.add(clonedLabelNode); 371 previousLabelNode = clonedLabelNode; 372 } 373 } else if (instantiation.findOwner(i) == instantiation) { 374 // Don't emit instructions that were already emitted by an ancestor subroutine. Note that it 375 // is still possible for a given instruction to be emitted twice because it may belong to 376 // two subroutines that do not invoke each other. 377 378 if (insnNode.getOpcode() == RET) { 379 // Translate RET instruction(s) to a jump to the return label for the appropriate 380 // instantiation. The problem is that the subroutine may "fall through" to the ret of a 381 // parent subroutine; therefore, to find the appropriate ret label we find the oldest 382 // instantiation that claims to own this instruction. 383 LabelNode retLabel = null; 384 for (Instantiation retLabelOwner = instantiation; 385 retLabelOwner != null; 386 retLabelOwner = retLabelOwner.parent) { 387 if (retLabelOwner.subroutineInsns.get(i)) { 388 retLabel = retLabelOwner.returnLabel; 389 } 390 } 391 if (retLabel == null) { 392 // This is only possible if the mainSubroutine owns a RET instruction, which should 393 // never happen for verifiable code. 394 throw new IllegalArgumentException( 395 "Instruction #" + i + " is a RET not owned by any subroutine"); 396 } 397 newInstructions.add(new JumpInsnNode(GOTO, retLabel)); 398 } else if (insnNode.getOpcode() == JSR) { 399 LabelNode jsrLabelNode = ((JumpInsnNode) insnNode).label; 400 BitSet subroutineInsns = subroutinesInsns.get(jsrLabelNode); 401 Instantiation newInstantiation = new Instantiation(instantiation, subroutineInsns); 402 LabelNode clonedJsrLabelNode = newInstantiation.getClonedLabelForJumpInsn(jsrLabelNode); 403 // Replace the JSR instruction with a GOTO to the instantiated subroutine, and push NULL 404 // for what was once the return address value. This hack allows us to avoid doing any sort 405 // of data flow analysis to figure out which instructions manipulate the old return 406 // address value pointer which is now known to be unneeded. 407 newInstructions.add(new InsnNode(ACONST_NULL)); 408 newInstructions.add(new JumpInsnNode(GOTO, clonedJsrLabelNode)); 409 newInstructions.add(newInstantiation.returnLabel); 410 // Insert this new instantiation into the queue to be emitted later. 411 worklist.add(newInstantiation); 412 } else { 413 newInstructions.add(insnNode.clone(instantiation)); 414 } 415 } 416 } 417 418 // Emit the try/catch blocks that are relevant for this instantiation. 419 for (TryCatchBlockNode tryCatchBlockNode : tryCatchBlocks) { 420 final LabelNode start = instantiation.getClonedLabel(tryCatchBlockNode.start); 421 final LabelNode end = instantiation.getClonedLabel(tryCatchBlockNode.end); 422 if (start != end) { 423 final LabelNode handler = 424 instantiation.getClonedLabelForJumpInsn(tryCatchBlockNode.handler); 425 if (start == null || end == null || handler == null) { 426 throw new AssertionError("Internal error!"); 427 } 428 newTryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, tryCatchBlockNode.type)); 429 } 430 } 431 432 // Emit the local variable nodes that are relevant for this instantiation. 433 for (LocalVariableNode localVariableNode : localVariables) { 434 final LabelNode start = instantiation.getClonedLabel(localVariableNode.start); 435 final LabelNode end = instantiation.getClonedLabel(localVariableNode.end); 436 if (start != end) { 437 newLocalVariables.add( 438 new LocalVariableNode( 439 localVariableNode.name, 440 localVariableNode.desc, 441 localVariableNode.signature, 442 start, 443 end, 444 localVariableNode.index)); 445 } 446 } 447 } 448 449 /** An instantiation of a subroutine. */ 450 private class Instantiation extends AbstractMap<LabelNode, LabelNode> { 451 452 /** 453 * The instantiation from which this one was created (or {@literal null} for the instantiation 454 * of the main "subroutine"). 455 */ 456 final Instantiation parent; 457 458 /** 459 * The original instructions that belong to the subroutine which is instantiated. Bit i is set 460 * iff instruction at index i belongs to this subroutine. 461 */ 462 final BitSet subroutineInsns; 463 464 /** 465 * A map from labels from the original code to labels pointing at code specific to this 466 * instantiation, for use in remapping try/catch blocks, as well as jumps. 467 * 468 * <p>Note that in the presence of instructions belonging to several subroutines, we map the 469 * target label of a GOTO to the label used by the oldest instantiation (parent instantiations 470 * are older than their children). This avoids code duplication during inlining in most cases. 471 */ 472 final Map<LabelNode, LabelNode> clonedLabels; 473 474 /** The return label for this instantiation, to which all original returns will be mapped. */ 475 final LabelNode returnLabel; 476 Instantiation(final Instantiation parent, final BitSet subroutineInsns)477 Instantiation(final Instantiation parent, final BitSet subroutineInsns) { 478 for (Instantiation instantiation = parent; 479 instantiation != null; 480 instantiation = instantiation.parent) { 481 if (instantiation.subroutineInsns == subroutineInsns) { 482 throw new IllegalArgumentException("Recursive invocation of " + subroutineInsns); 483 } 484 } 485 486 this.parent = parent; 487 this.subroutineInsns = subroutineInsns; 488 this.returnLabel = parent == null ? null : new LabelNode(); 489 this.clonedLabels = new HashMap<LabelNode, LabelNode>(); 490 491 // Create a clone of each label in the original code of the subroutine. Note that we collapse 492 // labels which point at the same instruction into one. 493 LabelNode clonedLabelNode = null; 494 for (int insnIndex = 0; insnIndex < instructions.size(); insnIndex++) { 495 AbstractInsnNode insnNode = instructions.get(insnIndex); 496 if (insnNode.getType() == AbstractInsnNode.LABEL) { 497 LabelNode labelNode = (LabelNode) insnNode; 498 // If we already have a label pointing at this spot, don't recreate it. 499 if (clonedLabelNode == null) { 500 clonedLabelNode = new LabelNode(); 501 } 502 clonedLabels.put(labelNode, clonedLabelNode); 503 } else if (findOwner(insnIndex) == this) { 504 // We will emit this instruction, so clear the duplicateLabelNode flag since the next 505 // Label will refer to a distinct instruction. 506 clonedLabelNode = null; 507 } 508 } 509 } 510 511 /** 512 * Returns the "owner" of a particular instruction relative to this instantiation: the owner 513 * refers to the Instantiation which will emit the version of this instruction that we will 514 * execute. 515 * 516 * <p>Typically, the return value is either <code>this</code> or <code>null</code>. <code>this 517 * </code> indicates that this instantiation will generate the version of this instruction that 518 * we will execute, and <code>null</code> indicates that this instantiation never executes the 519 * given instruction. 520 * 521 * <p>Sometimes, however, an instruction can belong to multiple subroutines; this is called a 522 * shared instruction, and occurs when multiple subroutines branch to common points of control. 523 * In this case, the owner is the oldest instantiation which owns the instruction in question 524 * (parent instantiations are older than their children). 525 * 526 * @param insnIndex the index of an instruction in the original code. 527 * @return the "owner" of a particular instruction relative to this instantiation. 528 */ findOwner(final int insnIndex)529 Instantiation findOwner(final int insnIndex) { 530 if (!subroutineInsns.get(insnIndex)) { 531 return null; 532 } 533 if (!sharedSubroutineInsns.get(insnIndex)) { 534 return this; 535 } 536 Instantiation owner = this; 537 for (Instantiation instantiation = parent; 538 instantiation != null; 539 instantiation = instantiation.parent) { 540 if (instantiation.subroutineInsns.get(insnIndex)) { 541 owner = instantiation; 542 } 543 } 544 return owner; 545 } 546 547 /** 548 * Returns the clone of the given original label that is appropriate for use in a jump 549 * instruction. 550 * 551 * @param labelNode a label of the original code. 552 * @return a clone of the given label for use in a jump instruction in the inlined code. 553 */ getClonedLabelForJumpInsn(final LabelNode labelNode)554 LabelNode getClonedLabelForJumpInsn(final LabelNode labelNode) { 555 // findOwner should never return null, because owner is null only if an instruction cannot be 556 // reached from this subroutine. 557 return findOwner(instructions.indexOf(labelNode)).clonedLabels.get(labelNode); 558 } 559 560 /** 561 * Returns the clone of the given original label that is appropriate for use by a try/catch 562 * block or a variable annotation. 563 * 564 * @param labelNode a label of the original code. 565 * @return a clone of the given label for use by a try/catch block or a variable annotation in 566 * the inlined code. 567 */ getClonedLabel(final LabelNode labelNode)568 LabelNode getClonedLabel(final LabelNode labelNode) { 569 return clonedLabels.get(labelNode); 570 } 571 572 // AbstractMap implementation 573 574 @Override entrySet()575 public Set<Map.Entry<LabelNode, LabelNode>> entrySet() { 576 throw new UnsupportedOperationException(); 577 } 578 579 @Override get(final Object key)580 public LabelNode get(final Object key) { 581 return getClonedLabelForJumpInsn((LabelNode) key); 582 } 583 584 @Override equals(final Object other)585 public boolean equals(final Object other) { 586 throw new UnsupportedOperationException(); 587 } 588 589 @Override hashCode()590 public int hashCode() { 591 throw new UnsupportedOperationException(); 592 } 593 } 594 } 595