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.CstString; 29import com.android.dx.rop.cst.CstType; 30import com.android.dx.rop.type.Type; 31 32import com.android.dex.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