(final int opcode, final Label label)
| 1092 | } |
| 1093 | |
| 1094 | @Override |
| 1095 | public void visitJumpInsn(final int opcode, final Label label) { |
| 1096 | lastBytecodeOffset = code.length; |
| 1097 | // Add the instruction to the bytecode of the method. |
| 1098 | // Compute the 'base' opcode, i.e. GOTO or JSR if opcode is GOTO_W or JSR_W, otherwise opcode. |
| 1099 | int baseOpcode = |
| 1100 | opcode >= Constants.GOTO_W ? opcode - Constants.WIDE_JUMP_OPCODE_DELTA : opcode; |
| 1101 | boolean nextInsnIsJumpTarget = false; |
| 1102 | if ((label.flags & Label.FLAG_RESOLVED) != 0 |
| 1103 | && label.bytecodeOffset - code.length < Short.MIN_VALUE) { |
| 1104 | // Case of a backward jump with an offset < -32768. In this case we automatically replace GOTO |
| 1105 | // with GOTO_W, JSR with JSR_W and IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:..., where |
| 1106 | // IFNOTxxx is the "opposite" opcode of IFxxx (e.g. IFNE for IFEQ) and where <L> designates |
| 1107 | // the instruction just after the GOTO_W. |
| 1108 | if (baseOpcode == Opcodes.GOTO) { |
| 1109 | code.putByte(Constants.GOTO_W); |
| 1110 | } else if (baseOpcode == Opcodes.JSR) { |
| 1111 | code.putByte(Constants.JSR_W); |
| 1112 | } else { |
| 1113 | // Put the "opposite" opcode of baseOpcode. This can be done by flipping the least |
| 1114 | // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ (with a |
| 1115 | // pre and post offset by 1). The jump offset is 8 bytes (3 for IFNOTxxx, 5 for GOTO_W). |
| 1116 | code.putByte(baseOpcode >= Opcodes.IFNULL ? baseOpcode ^ 1 : ((baseOpcode + 1) ^ 1) - 1); |
| 1117 | code.putShort(8); |
| 1118 | // Here we could put a GOTO_W in theory, but if ASM specific instructions are used in this |
| 1119 | // method or another one, and if the class has frames, we will need to insert a frame after |
| 1120 | // this GOTO_W during the additional ClassReader -> ClassWriter round trip to remove the ASM |
| 1121 | // specific instructions. To not miss this additional frame, we need to use an ASM_GOTO_W |
| 1122 | // here, which has the unfortunate effect of forcing this additional round trip (which in |
| 1123 | // some case would not have been really necessary, but we can't know this at this point). |
| 1124 | code.putByte(Constants.ASM_GOTO_W); |
| 1125 | hasAsmInstructions = true; |
| 1126 | // The instruction after the GOTO_W becomes the target of the IFNOT instruction. |
| 1127 | nextInsnIsJumpTarget = true; |
| 1128 | } |
| 1129 | label.put(code, code.length - 1, true); |
| 1130 | } else if (baseOpcode != opcode) { |
| 1131 | // Case of a GOTO_W or JSR_W specified by the user (normally ClassReader when used to remove |
| 1132 | // ASM specific instructions). In this case we keep the original instruction. |
| 1133 | code.putByte(opcode); |
| 1134 | label.put(code, code.length - 1, true); |
| 1135 | } else { |
| 1136 | // Case of a jump with an offset >= -32768, or of a jump with an unknown offset. In these |
| 1137 | // cases we store the offset in 2 bytes (which will be increased via a ClassReader -> |
| 1138 | // ClassWriter round trip if it turns out that 2 bytes are not sufficient). |
| 1139 | code.putByte(baseOpcode); |
| 1140 | label.put(code, code.length - 1, false); |
| 1141 | } |
| 1142 | |
| 1143 | // If needed, update the maximum stack size and number of locals, and stack map frames. |
| 1144 | if (currentBasicBlock != null) { |
| 1145 | Label nextBasicBlock = null; |
| 1146 | if (compute == COMPUTE_ALL_FRAMES) { |
| 1147 | currentBasicBlock.frame.execute(baseOpcode, 0, null, null); |
| 1148 | // Record the fact that 'label' is the target of a jump instruction. |
| 1149 | label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; |
| 1150 | // Add 'label' as a successor of the current basic block. |
| 1151 | addSuccessorToCurrentBasicBlock(Edge.JUMP, label); |
no test coverage detected