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