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