1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.dex.code;
18 
19 import com.android.dx.dex.DexOptions;
20 import com.android.dx.io.Opcodes;
21 import com.android.dx.rop.code.LocalItem;
22 import com.android.dx.rop.code.RegisterSpec;
23 import com.android.dx.rop.code.RegisterSpecList;
24 import com.android.dx.rop.code.RegisterSpecSet;
25 import com.android.dx.rop.code.SourcePosition;
26 import com.android.dx.rop.cst.Constant;
27 import com.android.dx.rop.cst.CstMemberRef;
28 import com.android.dx.rop.cst.CstType;
29 import com.android.dx.rop.cst.CstString;
30 import com.android.dx.rop.type.Type;
31 
32 import com.android.dx.util.DexException;
33 import java.util.ArrayList;
34 import java.util.BitSet;
35 import java.util.HashSet;
36 
37 /**
38  * Processor for instruction lists, which takes a "first cut" of
39  * instruction selection as a basis and produces a "final cut" in the
40  * form of a {@link DalvInsnList} instance.
41  */
42 public final class OutputFinisher {
43     /** {@code non-null;} options for dex output */
44     private final DexOptions dexOptions;
45 
46     /**
47      * {@code >= 0;} register count for the method, not including any extra
48      * "reserved" registers needed to translate "difficult" instructions
49      */
50     private final int unreservedRegCount;
51 
52     /** {@code non-null;} the list of instructions, per se */
53     private ArrayList<DalvInsn> insns;
54 
55     /** whether any instruction has position info */
56     private boolean hasAnyPositionInfo;
57 
58     /** whether any instruction has local variable info */
59     private boolean hasAnyLocalInfo;
60 
61     /**
62      * {@code >= 0;} the count of reserved registers (low-numbered
63      * registers used when expanding instructions that can't be
64      * represented simply); becomes valid after a call to {@link
65      * #massageInstructions}
66      */
67     private int reservedCount;
68 
69     /**
70      * Constructs an instance. It initially contains no instructions.
71      *
72      * @param dexOptions {@code non-null;} options for dex output
73      * @param regCount {@code >= 0;} register count for the method
74      * @param initialCapacity {@code >= 0;} initial capacity of the
75      * instructions list
76      */
OutputFinisher(DexOptions dexOptions, int initialCapacity, int regCount)77     public OutputFinisher(DexOptions dexOptions, int initialCapacity, int regCount) {
78         this.dexOptions = dexOptions;
79         this.unreservedRegCount = regCount;
80         this.insns = new ArrayList<DalvInsn>(initialCapacity);
81         this.reservedCount = -1;
82         this.hasAnyPositionInfo = false;
83         this.hasAnyLocalInfo = false;
84     }
85 
86     /**
87      * Returns whether any of the instructions added to this instance
88      * come with position info.
89      *
90      * @return whether any of the instructions added to this instance
91      * come with position info
92      */
hasAnyPositionInfo()93     public boolean hasAnyPositionInfo() {
94         return hasAnyPositionInfo;
95     }
96 
97     /**
98      * Returns whether this instance has any local variable information.
99      *
100      * @return whether this instance has any local variable information
101      */
hasAnyLocalInfo()102     public boolean hasAnyLocalInfo() {
103         return hasAnyLocalInfo;
104     }
105 
106     /**
107      * Helper for {@link #add} which scrutinizes a single
108      * instruction for local variable information.
109      *
110      * @param insn {@code non-null;} instruction to scrutinize
111      * @return {@code true} iff the instruction refers to any
112      * named locals
113      */
hasLocalInfo(DalvInsn insn)114     private static boolean hasLocalInfo(DalvInsn insn) {
115         if (insn instanceof LocalSnapshot) {
116             RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
117             int size = specs.size();
118             for (int i = 0; i < size; i++) {
119                 if (hasLocalInfo(specs.get(i))) {
120                     return true;
121                 }
122             }
123         } else if (insn instanceof LocalStart) {
124             RegisterSpec spec = ((LocalStart) insn).getLocal();
125             if (hasLocalInfo(spec)) {
126                 return true;
127             }
128         }
129 
130         return false;
131     }
132 
133     /**
134      * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
135      * register spec.
136      *
137      * @param spec {@code non-null;} spec to scrutinize
138      * @return {@code true} iff the spec refers to any
139      * named locals
140      */
hasLocalInfo(RegisterSpec spec)141     private static boolean hasLocalInfo(RegisterSpec spec) {
142         return (spec != null)
143             && (spec.getLocalItem().getName() != null);
144     }
145 
146     /**
147      * Returns the set of all constants referred to by instructions added
148      * to this instance.
149      *
150      * @return {@code non-null;} the set of constants
151      */
getAllConstants()152     public HashSet<Constant> getAllConstants() {
153         HashSet<Constant> result = new HashSet<Constant>(20);
154 
155         for (DalvInsn insn : insns) {
156             addConstants(result, insn);
157         }
158 
159         return result;
160     }
161 
162     /**
163      * Helper for {@link #getAllConstants} which adds all the info for
164      * a single instruction.
165      *
166      * @param result {@code non-null;} result set to add to
167      * @param insn {@code non-null;} instruction to scrutinize
168      */
addConstants(HashSet<Constant> result, DalvInsn insn)169     private static void addConstants(HashSet<Constant> result,
170             DalvInsn insn) {
171         if (insn instanceof CstInsn) {
172             Constant cst = ((CstInsn) insn).getConstant();
173             result.add(cst);
174         } else if (insn instanceof LocalSnapshot) {
175             RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
176             int size = specs.size();
177             for (int i = 0; i < size; i++) {
178                 addConstants(result, specs.get(i));
179             }
180         } else if (insn instanceof LocalStart) {
181             RegisterSpec spec = ((LocalStart) insn).getLocal();
182             addConstants(result, spec);
183         }
184     }
185 
186     /**
187      * Helper for {@link #getAllConstants} which adds all the info for
188      * a single {@code RegisterSpec}.
189      *
190      * @param result {@code non-null;} result set to add to
191      * @param spec {@code null-ok;} register spec to add
192      */
addConstants(HashSet<Constant> result, RegisterSpec spec)193     private static void addConstants(HashSet<Constant> result,
194             RegisterSpec spec) {
195         if (spec == null) {
196             return;
197         }
198 
199         LocalItem local = spec.getLocalItem();
200         CstString name = local.getName();
201         CstString signature = local.getSignature();
202         Type type = spec.getType();
203 
204         if (type != Type.KNOWN_NULL) {
205             result.add(CstType.intern(type));
206         }
207 
208         if (name != null) {
209             result.add(name);
210         }
211 
212         if (signature != null) {
213             result.add(signature);
214         }
215     }
216 
217     /**
218      * Adds an instruction to the output.
219      *
220      * @param insn {@code non-null;} the instruction to add
221      */
add(DalvInsn insn)222     public void add(DalvInsn insn) {
223         insns.add(insn);
224         updateInfo(insn);
225     }
226 
227     /**
228      * Inserts an instruction in the output at the given offset.
229      *
230      * @param at {@code >= 0;} what index to insert at
231      * @param insn {@code non-null;} the instruction to insert
232      */
insert(int at, DalvInsn insn)233     public void insert(int at, DalvInsn insn) {
234         insns.add(at, insn);
235         updateInfo(insn);
236     }
237 
238     /**
239      * Helper for {@link #add} and {@link #insert},
240      * which updates the position and local info flags.
241      *
242      * @param insn {@code non-null;} an instruction that was just introduced
243      */
updateInfo(DalvInsn insn)244     private void updateInfo(DalvInsn insn) {
245         if (! hasAnyPositionInfo) {
246             SourcePosition pos = insn.getPosition();
247             if (pos.getLine() >= 0) {
248                 hasAnyPositionInfo = true;
249             }
250         }
251 
252         if (! hasAnyLocalInfo) {
253             if (hasLocalInfo(insn)) {
254                 hasAnyLocalInfo = true;
255             }
256         }
257     }
258 
259     /**
260      * Reverses a branch which is buried a given number of instructions
261      * backward in the output. It is illegal to call this unless the
262      * indicated instruction really is a reversible branch.
263      *
264      * @param which how many instructions back to find the branch;
265      * {@code 0} is the most recently added instruction,
266      * {@code 1} is the instruction before that, etc.
267      * @param newTarget {@code non-null;} the new target for the
268      * reversed branch
269      */
reverseBranch(int which, CodeAddress newTarget)270     public void reverseBranch(int which, CodeAddress newTarget) {
271         int size = insns.size();
272         int index = size - which - 1;
273         TargetInsn targetInsn;
274 
275         try {
276             targetInsn = (TargetInsn) insns.get(index);
277         } catch (IndexOutOfBoundsException ex) {
278             // Translate the exception.
279             throw new IllegalArgumentException("too few instructions");
280         } catch (ClassCastException ex) {
281             // Translate the exception.
282             throw new IllegalArgumentException("non-reversible instruction");
283         }
284 
285         /*
286          * No need to call this.set(), since the format and other info
287          * are the same.
288          */
289         insns.set(index, targetInsn.withNewTargetAndReversed(newTarget));
290     }
291 
292     /**
293      * Assigns indices in all instructions that need them, using the
294      * given callback to perform lookups. This should be called before
295      * calling {@link #finishProcessingAndGetList}.
296      *
297      * @param callback {@code non-null;} callback object
298      */
assignIndices(DalvCode.AssignIndicesCallback callback)299     public void assignIndices(DalvCode.AssignIndicesCallback callback) {
300         for (DalvInsn insn : insns) {
301             if (insn instanceof CstInsn) {
302                 assignIndices((CstInsn) insn, callback);
303             }
304         }
305     }
306 
307     /**
308      * Helper for {@link #assignIndices} which does assignment for one
309      * instruction.
310      *
311      * @param insn {@code non-null;} the instruction
312      * @param callback {@code non-null;} the callback
313      */
assignIndices(CstInsn insn, DalvCode.AssignIndicesCallback callback)314     private static void assignIndices(CstInsn insn,
315             DalvCode.AssignIndicesCallback callback) {
316         Constant cst = insn.getConstant();
317         int index = callback.getIndex(cst);
318 
319         if (index >= 0) {
320             insn.setIndex(index);
321         }
322 
323         if (cst instanceof CstMemberRef) {
324             CstMemberRef member = (CstMemberRef) cst;
325             CstType definer = member.getDefiningClass();
326             index = callback.getIndex(definer);
327             if (index >= 0) {
328                 insn.setClassIndex(index);
329             }
330         }
331     }
332 
333     /**
334      * Does final processing on this instance and gets the output as
335      * a {@link DalvInsnList}. Final processing consists of:
336      *
337      * <ul>
338      *   <li>optionally renumbering registers (to make room as needed for
339      *   expanded instructions)</li>
340      *   <li>picking a final opcode for each instruction</li>
341      *   <li>rewriting instructions, because of register number,
342      *   constant pool index, or branch target size issues</li>
343      *   <li>assigning final addresses</li>
344      * </ul>
345      *
346      * <p><b>Note:</b> This method may only be called once per instance
347      * of this class.</p>
348      *
349      * @return {@code non-null;} the output list
350      * @throws UnsupportedOperationException if this method has
351      * already been called
352      */
finishProcessingAndGetList()353     public DalvInsnList finishProcessingAndGetList() {
354         if (reservedCount >= 0) {
355             throw new UnsupportedOperationException("already processed");
356         }
357 
358         Dop[] opcodes = makeOpcodesArray();
359         reserveRegisters(opcodes);
360         massageInstructions(opcodes);
361         assignAddressesAndFixBranches();
362 
363         return DalvInsnList.makeImmutable(insns,
364                 reservedCount + unreservedRegCount);
365     }
366 
367     /**
368      * Helper for {@link #finishProcessingAndGetList}, which extracts
369      * the opcode out of each instruction into a separate array, to be
370      * further manipulated as things progress.
371      *
372      * @return {@code non-null;} the array of opcodes
373      */
makeOpcodesArray()374     private Dop[] makeOpcodesArray() {
375         int size = insns.size();
376         Dop[] result = new Dop[size];
377 
378         for (int i = 0; i < size; i++) {
379             result[i] = insns.get(i).getOpcode();
380         }
381 
382         return result;
383     }
384 
385     /**
386      * Helper for {@link #finishProcessingAndGetList}, which figures
387      * out how many reserved registers are required and then reserving
388      * them. It also updates the given {@code opcodes} array so
389      * as to avoid extra work when constructing the massaged
390      * instruction list.
391      *
392      * @param opcodes {@code non-null;} array of per-instruction
393      * opcode selections
394      */
reserveRegisters(Dop[] opcodes)395     private void reserveRegisters(Dop[] opcodes) {
396         int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
397 
398         /*
399          * Call calculateReservedCount() and then perform register
400          * reservation, repeatedly until no new reservations happen.
401          */
402         for (;;) {
403             int newReservedCount = calculateReservedCount(opcodes);
404             if (oldReservedCount >= newReservedCount) {
405                 break;
406             }
407 
408             int reservedDifference = newReservedCount - oldReservedCount;
409             int size = insns.size();
410 
411             for (int i = 0; i < size; i++) {
412                 /*
413                  * CodeAddress instance identity is used to link
414                  * TargetInsns to their targets, so it is
415                  * inappropriate to make replacements, and they don't
416                  * have registers in any case. Hence, the instanceof
417                  * test below.
418                  */
419                 DalvInsn insn = insns.get(i);
420                 if (!(insn instanceof CodeAddress)) {
421                     /*
422                      * No need to call this.set() since the format and
423                      * other info are the same.
424                      */
425                     insns.set(i, insn.withRegisterOffset(reservedDifference));
426                 }
427             }
428 
429             oldReservedCount = newReservedCount;
430         }
431 
432         reservedCount = oldReservedCount;
433     }
434 
435     /**
436      * Helper for {@link #reserveRegisters}, which does one
437      * pass over the instructions, calculating the number of
438      * registers that need to be reserved. It also updates the
439      * {@code opcodes} list to help avoid extra work in future
440      * register reservation passes.
441      *
442      * @param opcodes {@code non-null;} array of per-instruction
443      * opcode selections
444      * @return {@code >= 0;} the count of reserved registers
445      */
calculateReservedCount(Dop[] opcodes)446     private int calculateReservedCount(Dop[] opcodes) {
447         int size = insns.size();
448 
449         /*
450          * Potential new value of reservedCount, which gets updated in the
451          * following loop. It starts out with the existing reservedCount
452          * and gets increased if it turns out that additional registers
453          * need to be reserved.
454          */
455         int newReservedCount = reservedCount;
456 
457         for (int i = 0; i < size; i++) {
458             DalvInsn insn = insns.get(i);
459             Dop originalOpcode = opcodes[i];
460             Dop newOpcode = findOpcodeForInsn(insn, originalOpcode);
461 
462             if (newOpcode == null) {
463                 /*
464                  * The instruction will need to be expanded, so find the
465                  * expanded opcode and reserve registers for it.
466                  */
467                 Dop expandedOp = findExpandedOpcodeForInsn(insn);
468                 BitSet compatRegs = expandedOp.getFormat().compatibleRegs(insn);
469                 int reserve = insn.getMinimumRegisterRequirement(compatRegs);
470                 if (reserve > newReservedCount) {
471                     newReservedCount = reserve;
472                 }
473             } else if (originalOpcode == newOpcode) {
474                 continue;
475             }
476 
477             opcodes[i] = newOpcode;
478         }
479 
480         return newReservedCount;
481     }
482 
483     /**
484      * Attempts to fit the given instruction into a specific opcode,
485      * returning the opcode whose format that the instruction fits
486      * into or {@code null} to indicate that the instruction will need
487      * to be expanded. This fitting process starts with the given
488      * opcode as a first "best guess" and then pessimizes from there
489      * if necessary.
490      *
491      * @param insn {@code non-null;} the instruction in question
492      * @param guess {@code null-ok;} the current guess as to the best
493      * opcode; {@code null} means that no simple opcode fits
494      * @return {@code null-ok;} a possibly-different opcode; either a
495      * {@code non-null} good fit or {@code null} to indicate that no
496      * simple opcode fits
497      */
findOpcodeForInsn(DalvInsn insn, Dop guess)498     private Dop findOpcodeForInsn(DalvInsn insn, Dop guess) {
499         /*
500          * Note: The initial guess might be null, meaning that an
501          * earlier call to this method already determined that there
502          * was no possible simple opcode fit.
503          */
504 
505         while (guess != null) {
506             if (guess.getFormat().isCompatible(insn)) {
507                 break;
508             }
509 
510             guess = Dops.getNextOrNull(guess, dexOptions);
511         }
512 
513         return guess;
514     }
515 
516     /**
517      * Finds the proper opcode for the given instruction, ignoring
518      * register constraints.
519      *
520      * @param insn {@code non-null;} the instruction in question
521      * @return {@code non-null;} the opcode that fits
522      */
findExpandedOpcodeForInsn(DalvInsn insn)523     private Dop findExpandedOpcodeForInsn(DalvInsn insn) {
524         Dop result = findOpcodeForInsn(insn.getLowRegVersion(), insn.getOpcode());
525         if (result == null) {
526             throw new DexException("No expanded opcode for " + insn);
527         }
528         return result;
529     }
530 
531     /**
532      * Helper for {@link #finishProcessingAndGetList}, which goes
533      * through each instruction in the output, making sure its opcode
534      * can accomodate its arguments. In cases where the opcode is
535      * unable to do so, this replaces the instruction with a larger
536      * instruction with identical semantics that <i>will</i> work.
537      *
538      * <p>This method may also reserve a number of low-numbered
539      * registers, renumbering the instructions' original registers, in
540      * order to have register space available in which to move
541      * very-high registers when expanding instructions into
542      * multi-instruction sequences. This expansion is done when no
543      * simple instruction format can be found for a given instruction that
544      * is able to accomodate that instruction's registers.</p>
545      *
546      * <p>This method ignores issues of branch target size, since
547      * final addresses aren't known at the point that this method is
548      * called.</p>
549      *
550      * @param opcodes {@code non-null;} array of per-instruction
551      * opcode selections
552      */
massageInstructions(Dop[] opcodes)553     private void massageInstructions(Dop[] opcodes) {
554         if (reservedCount == 0) {
555             /*
556              * The easy common case: No registers were reserved, so we
557              * merely need to replace any instructions whose format
558              * (and hence whose opcode) changed during the reservation
559              * pass, but all instructions will stay at their original
560              * indices, and the instruction list doesn't grow.
561              */
562             int size = insns.size();
563 
564             for (int i = 0; i < size; i++) {
565                 DalvInsn insn = insns.get(i);
566                 Dop originalOpcode = insn.getOpcode();
567                 Dop currentOpcode = opcodes[i];
568 
569                 if (originalOpcode != currentOpcode) {
570                     insns.set(i, insn.withOpcode(currentOpcode));
571                 }
572             }
573         } else {
574             /*
575              * The difficult uncommon case: Some instructions have to be
576              * expanded to deal with high registers.
577              */
578             insns = performExpansion(opcodes);
579         }
580     }
581 
582     /**
583      * Helper for {@link #massageInstructions}, which constructs a
584      * replacement list, where each {link DalvInsn} instance that
585      * couldn't be represented simply (due to register representation
586      * problems) is expanded into a series of instances that together
587      * perform the proper function.
588      *
589      * @param opcodes {@code non-null;} array of per-instruction
590      * opcode selections
591      * @return {@code non-null;} the replacement list
592      */
performExpansion(Dop[] opcodes)593     private ArrayList<DalvInsn> performExpansion(Dop[] opcodes) {
594         int size = insns.size();
595         ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
596 
597         for (int i = 0; i < size; i++) {
598             DalvInsn insn = insns.get(i);
599             Dop originalOpcode = insn.getOpcode();
600             Dop currentOpcode = opcodes[i];
601             DalvInsn prefix;
602             DalvInsn suffix;
603 
604             if (currentOpcode != null) {
605                 // No expansion is necessary.
606                 prefix = null;
607                 suffix = null;
608             } else {
609                 // Expansion is required.
610                 currentOpcode = findExpandedOpcodeForInsn(insn);
611                 BitSet compatRegs =
612                     currentOpcode.getFormat().compatibleRegs(insn);
613                 prefix = insn.expandedPrefix(compatRegs);
614                 suffix = insn.expandedSuffix(compatRegs);
615 
616                 // Expand necessary registers to fit the new format
617                 insn = insn.expandedVersion(compatRegs);
618             }
619 
620             if (prefix != null) {
621                 result.add(prefix);
622             }
623 
624             if (currentOpcode != originalOpcode) {
625                 insn = insn.withOpcode(currentOpcode);
626             }
627             result.add(insn);
628 
629             if (suffix != null) {
630                 result.add(suffix);
631             }
632         }
633 
634         return result;
635     }
636 
637     /**
638      * Helper for {@link #finishProcessingAndGetList}, which assigns
639      * addresses to each instruction, possibly rewriting branches to
640      * fix ones that wouldn't otherwise be able to reach their
641      * targets.
642      */
assignAddressesAndFixBranches()643     private void assignAddressesAndFixBranches() {
644         for (;;) {
645             assignAddresses();
646             if (!fixBranches()) {
647                 break;
648             }
649         }
650     }
651 
652     /**
653      * Helper for {@link #assignAddressesAndFixBranches}, which
654      * assigns an address to each instruction, in order.
655      */
assignAddresses()656     private void assignAddresses() {
657         int address = 0;
658         int size = insns.size();
659 
660         for (int i = 0; i < size; i++) {
661             DalvInsn insn = insns.get(i);
662             insn.setAddress(address);
663             address += insn.codeSize();
664         }
665     }
666 
667     /**
668      * Helper for {@link #assignAddressesAndFixBranches}, which checks
669      * the branch target size requirement of each branch instruction
670      * to make sure it fits. For instructions that don't fit, this
671      * rewrites them to use a {@code goto} of some sort. In the
672      * case of a conditional branch that doesn't fit, the sense of the
673      * test is reversed in order to branch around a {@code goto}
674      * to the original target.
675      *
676      * @return whether any branches had to be fixed
677      */
fixBranches()678     private boolean fixBranches() {
679         int size = insns.size();
680         boolean anyFixed = false;
681 
682         for (int i = 0; i < size; i++) {
683             DalvInsn insn = insns.get(i);
684             if (!(insn instanceof TargetInsn)) {
685                 // This loop only needs to inspect TargetInsns.
686                 continue;
687             }
688 
689             Dop opcode = insn.getOpcode();
690             TargetInsn target = (TargetInsn) insn;
691 
692             if (opcode.getFormat().branchFits(target)) {
693                 continue;
694             }
695 
696             if (opcode.getFamily() == Opcodes.GOTO) {
697                 // It is a goto; widen it if possible.
698                 opcode = findOpcodeForInsn(insn, opcode);
699                 if (opcode == null) {
700                     /*
701                      * The branch is already maximally large. This should
702                      * only be possible if a method somehow manages to have
703                      * more than 2^31 code units.
704                      */
705                     throw new UnsupportedOperationException("method too long");
706                 }
707                 insns.set(i, insn.withOpcode(opcode));
708             } else {
709                 /*
710                  * It is a conditional: Reverse its sense, and arrange for
711                  * it to branch around an absolute goto to the original
712                  * branch target.
713                  *
714                  * Note: An invariant of the list being processed is
715                  * that every TargetInsn is followed by a CodeAddress.
716                  * Hence, it is always safe to get the next element
717                  * after a TargetInsn and cast it to CodeAddress, as
718                  * is happening a few lines down.
719                  *
720                  * Also note: Size gets incremented by one here, as we
721                  * have -- in the net -- added one additional element
722                  * to the list, so we increment i to match. The added
723                  * and changed elements will be inspected by a repeat
724                  * call to this method after this invocation returns.
725                  */
726                 CodeAddress newTarget;
727                 try {
728                     newTarget = (CodeAddress) insns.get(i + 1);
729                 } catch (IndexOutOfBoundsException ex) {
730                     // The TargetInsn / CodeAddress invariant was violated.
731                     throw new IllegalStateException(
732                             "unpaired TargetInsn (dangling)");
733                 } catch (ClassCastException ex) {
734                     // The TargetInsn / CodeAddress invariant was violated.
735                     throw new IllegalStateException("unpaired TargetInsn");
736                 }
737                 TargetInsn gotoInsn =
738                     new TargetInsn(Dops.GOTO, target.getPosition(),
739                             RegisterSpecList.EMPTY, target.getTarget());
740                 insns.set(i, gotoInsn);
741                 insns.add(i, target.withNewTargetAndReversed(newTarget));
742                 size++;
743                 i++;
744             }
745 
746             anyFixed = true;
747         }
748 
749         return anyFixed;
750     }
751 }
752