OutputFinisher.java revision f870f2dce9300c8dec620613371f08e5c234245b
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
17package com.android.dx.dex.code;
18
19import com.android.dx.dex.DexOptions;
20import com.android.dx.io.Opcodes;
21import com.android.dx.rop.code.LocalItem;
22import com.android.dx.rop.code.RegisterSpec;
23import com.android.dx.rop.code.RegisterSpecList;
24import com.android.dx.rop.code.RegisterSpecSet;
25import com.android.dx.rop.code.SourcePosition;
26import com.android.dx.rop.cst.Constant;
27import com.android.dx.rop.cst.CstMemberRef;
28import com.android.dx.rop.cst.CstType;
29import com.android.dx.rop.cst.CstString;
30import com.android.dx.rop.type.Type;
31
32import com.android.dx.util.DexException;
33import java.util.ArrayList;
34import java.util.BitSet;
35import 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 */
42public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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                /*
508                 * Don't break out for const_string to generate jumbo version
509                 * when option is enabled.
510                 */
511                if (!dexOptions.forceJumbo ||
512                    guess.getOpcode() != Opcodes.CONST_STRING) {
513                    break;
514                }
515            }
516
517            guess = Dops.getNextOrNull(guess, dexOptions);
518        }
519
520        return guess;
521    }
522
523    /**
524     * Finds the proper opcode for the given instruction, ignoring
525     * register constraints.
526     *
527     * @param insn {@code non-null;} the instruction in question
528     * @return {@code non-null;} the opcode that fits
529     */
530    private Dop findExpandedOpcodeForInsn(DalvInsn insn) {
531        Dop result = findOpcodeForInsn(insn.getLowRegVersion(), insn.getOpcode());
532        if (result == null) {
533            throw new DexException("No expanded opcode for " + insn);
534        }
535        return result;
536    }
537
538    /**
539     * Helper for {@link #finishProcessingAndGetList}, which goes
540     * through each instruction in the output, making sure its opcode
541     * can accomodate its arguments. In cases where the opcode is
542     * unable to do so, this replaces the instruction with a larger
543     * instruction with identical semantics that <i>will</i> work.
544     *
545     * <p>This method may also reserve a number of low-numbered
546     * registers, renumbering the instructions' original registers, in
547     * order to have register space available in which to move
548     * very-high registers when expanding instructions into
549     * multi-instruction sequences. This expansion is done when no
550     * simple instruction format can be found for a given instruction that
551     * is able to accomodate that instruction's registers.</p>
552     *
553     * <p>This method ignores issues of branch target size, since
554     * final addresses aren't known at the point that this method is
555     * called.</p>
556     *
557     * @param opcodes {@code non-null;} array of per-instruction
558     * opcode selections
559     */
560    private void massageInstructions(Dop[] opcodes) {
561        if (reservedCount == 0) {
562            /*
563             * The easy common case: No registers were reserved, so we
564             * merely need to replace any instructions whose format
565             * (and hence whose opcode) changed during the reservation
566             * pass, but all instructions will stay at their original
567             * indices, and the instruction list doesn't grow.
568             */
569            int size = insns.size();
570
571            for (int i = 0; i < size; i++) {
572                DalvInsn insn = insns.get(i);
573                Dop originalOpcode = insn.getOpcode();
574                Dop currentOpcode = opcodes[i];
575
576                if (originalOpcode != currentOpcode) {
577                    insns.set(i, insn.withOpcode(currentOpcode));
578                }
579            }
580        } else {
581            /*
582             * The difficult uncommon case: Some instructions have to be
583             * expanded to deal with high registers.
584             */
585            insns = performExpansion(opcodes);
586        }
587    }
588
589    /**
590     * Helper for {@link #massageInstructions}, which constructs a
591     * replacement list, where each {link DalvInsn} instance that
592     * couldn't be represented simply (due to register representation
593     * problems) is expanded into a series of instances that together
594     * perform the proper function.
595     *
596     * @param opcodes {@code non-null;} array of per-instruction
597     * opcode selections
598     * @return {@code non-null;} the replacement list
599     */
600    private ArrayList<DalvInsn> performExpansion(Dop[] opcodes) {
601        int size = insns.size();
602        ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
603
604        ArrayList<CodeAddress> closelyBoundAddresses = new ArrayList<CodeAddress>();
605
606        for (int i = 0; i < size; i++) {
607            DalvInsn insn = insns.get(i);
608            Dop originalOpcode = insn.getOpcode();
609            Dop currentOpcode = opcodes[i];
610            DalvInsn prefix;
611            DalvInsn suffix;
612
613            if (currentOpcode != null) {
614                // No expansion is necessary.
615                prefix = null;
616                suffix = null;
617            } else {
618                // Expansion is required.
619                currentOpcode = findExpandedOpcodeForInsn(insn);
620                BitSet compatRegs =
621                    currentOpcode.getFormat().compatibleRegs(insn);
622                prefix = insn.expandedPrefix(compatRegs);
623                suffix = insn.expandedSuffix(compatRegs);
624
625                // Expand necessary registers to fit the new format
626                insn = insn.expandedVersion(compatRegs);
627            }
628
629            if (insn instanceof CodeAddress) {
630                // If we have a closely bound address, don't add it yet,
631                // because we need to add it after the prefix for the
632                // instruction it is bound to.
633                if (((CodeAddress) insn).getBindsClosely()) {
634                    closelyBoundAddresses.add((CodeAddress)insn);
635                    continue;
636                }
637            }
638
639            if (prefix != null) {
640                result.add(prefix);
641            }
642
643            // Add any pending closely bound addresses
644            if (!(insn instanceof ZeroSizeInsn) && closelyBoundAddresses.size() > 0) {
645                for (CodeAddress codeAddress: closelyBoundAddresses) {
646                    result.add(codeAddress);
647                }
648                closelyBoundAddresses.clear();
649            }
650
651            if (currentOpcode != originalOpcode) {
652                insn = insn.withOpcode(currentOpcode);
653            }
654            result.add(insn);
655
656            if (suffix != null) {
657                result.add(suffix);
658            }
659        }
660
661        return result;
662    }
663
664    /**
665     * Helper for {@link #finishProcessingAndGetList}, which assigns
666     * addresses to each instruction, possibly rewriting branches to
667     * fix ones that wouldn't otherwise be able to reach their
668     * targets.
669     */
670    private void assignAddressesAndFixBranches() {
671        for (;;) {
672            assignAddresses();
673            if (!fixBranches()) {
674                break;
675            }
676        }
677    }
678
679    /**
680     * Helper for {@link #assignAddressesAndFixBranches}, which
681     * assigns an address to each instruction, in order.
682     */
683    private void assignAddresses() {
684        int address = 0;
685        int size = insns.size();
686
687        for (int i = 0; i < size; i++) {
688            DalvInsn insn = insns.get(i);
689            insn.setAddress(address);
690            address += insn.codeSize();
691        }
692    }
693
694    /**
695     * Helper for {@link #assignAddressesAndFixBranches}, which checks
696     * the branch target size requirement of each branch instruction
697     * to make sure it fits. For instructions that don't fit, this
698     * rewrites them to use a {@code goto} of some sort. In the
699     * case of a conditional branch that doesn't fit, the sense of the
700     * test is reversed in order to branch around a {@code goto}
701     * to the original target.
702     *
703     * @return whether any branches had to be fixed
704     */
705    private boolean fixBranches() {
706        int size = insns.size();
707        boolean anyFixed = false;
708
709        for (int i = 0; i < size; i++) {
710            DalvInsn insn = insns.get(i);
711            if (!(insn instanceof TargetInsn)) {
712                // This loop only needs to inspect TargetInsns.
713                continue;
714            }
715
716            Dop opcode = insn.getOpcode();
717            TargetInsn target = (TargetInsn) insn;
718
719            if (opcode.getFormat().branchFits(target)) {
720                continue;
721            }
722
723            if (opcode.getFamily() == Opcodes.GOTO) {
724                // It is a goto; widen it if possible.
725                opcode = findOpcodeForInsn(insn, opcode);
726                if (opcode == null) {
727                    /*
728                     * The branch is already maximally large. This should
729                     * only be possible if a method somehow manages to have
730                     * more than 2^31 code units.
731                     */
732                    throw new UnsupportedOperationException("method too long");
733                }
734                insns.set(i, insn.withOpcode(opcode));
735            } else {
736                /*
737                 * It is a conditional: Reverse its sense, and arrange for
738                 * it to branch around an absolute goto to the original
739                 * branch target.
740                 *
741                 * Note: An invariant of the list being processed is
742                 * that every TargetInsn is followed by a CodeAddress.
743                 * Hence, it is always safe to get the next element
744                 * after a TargetInsn and cast it to CodeAddress, as
745                 * is happening a few lines down.
746                 *
747                 * Also note: Size gets incremented by one here, as we
748                 * have -- in the net -- added one additional element
749                 * to the list, so we increment i to match. The added
750                 * and changed elements will be inspected by a repeat
751                 * call to this method after this invocation returns.
752                 */
753                CodeAddress newTarget;
754                try {
755                    newTarget = (CodeAddress) insns.get(i + 1);
756                } catch (IndexOutOfBoundsException ex) {
757                    // The TargetInsn / CodeAddress invariant was violated.
758                    throw new IllegalStateException(
759                            "unpaired TargetInsn (dangling)");
760                } catch (ClassCastException ex) {
761                    // The TargetInsn / CodeAddress invariant was violated.
762                    throw new IllegalStateException("unpaired TargetInsn");
763                }
764                TargetInsn gotoInsn =
765                    new TargetInsn(Dops.GOTO, target.getPosition(),
766                            RegisterSpecList.EMPTY, target.getTarget());
767                insns.set(i, gotoInsn);
768                insns.add(i, target.withNewTargetAndReversed(newTarget));
769                size++;
770                i++;
771            }
772
773            anyFixed = true;
774        }
775
776        return anyFixed;
777    }
778}
779