1 7 8 package org.gjt.jclasslib.bytecode; 9 10 import org.gjt.jclasslib.structures.InvalidByteCodeException; 11 import org.gjt.jclasslib.structures.attributes.*; 12 13 import java.util.*; 14 15 23 public class CodeInsertion { 24 25 26 37 public static CodeInsertion merge(int position, 38 boolean shiftTarget, 39 CodeInsertion inner, 40 CodeInsertion outer) 41 { 42 43 if (outer == null) { 44 return inner; 45 } 46 if (inner == null) { 47 return outer; 48 } 49 50 AbstractInstruction[] preInstructions = mergeInstructions(outer.preInstructions, inner.preInstructions); 51 AbstractInstruction[] postInstructions = mergeInstructions(inner.postInstructions, outer.postInstructions); 52 53 CodeInsertion codeInsertion = 54 new CodeInsertion(position, 55 preInstructions, 56 postInstructions, 57 shiftTarget); 58 59 return codeInsertion; 60 } 61 62 68 public static AbstractInstruction[] mergeInstructions(AbstractInstruction[] firstInstructions, 69 AbstractInstruction[] lastInstructions) 70 { 71 if (firstInstructions== null) { 72 return lastInstructions; 73 } 74 if (lastInstructions == null) { 75 return firstInstructions; 76 } 77 78 AbstractInstruction[] mergedInstructions = new AbstractInstruction[firstInstructions.length + lastInstructions.length]; 79 System.arraycopy(firstInstructions, 0, mergedInstructions, 0, firstInstructions.length); 80 System.arraycopy(lastInstructions, 0, mergedInstructions, firstInstructions.length, lastInstructions.length); 81 82 return mergedInstructions; 83 } 84 85 105 public static List apply(List instructions, 106 List codeInsertions, 107 CodeAttribute codeAttribute) 108 109 throws InvalidByteCodeException 110 { 111 int instructionCount = instructions.size(); 112 int[] transformedIndices = new int[instructionCount]; 113 for (int i = 0; i < instructionCount; i++) { 114 transformedIndices[i] = i; 115 } 116 117 List newInstructions = insertCode(instructions, 118 codeInsertions, 119 transformedIndices); 120 121 int[] oldOffsets = new int[instructionCount]; 122 for (int i = 0; i < instructionCount; i++) { 123 oldOffsets[i] = ((AbstractInstruction)instructions.get(i)).getOffset(); 124 } 125 int[] newOffsets = new int[newInstructions.size()]; 126 calculateOffsets(newInstructions, newOffsets); 127 128 129 adjustOffsets(instructions, 130 newInstructions, 131 oldOffsets, 132 newOffsets, 133 transformedIndices); 134 135 if (codeAttribute != null) { 136 adjustExceptionTable(oldOffsets, 137 newOffsets, 138 transformedIndices, 139 codeAttribute); 140 141 adjustLineNumberTable(oldOffsets, 142 newOffsets, 143 transformedIndices, 144 codeAttribute); 145 } 146 147 applyOffsets(newInstructions, newOffsets); 148 return newInstructions; 149 } 150 151 private static List insertCode(List instructions, 152 List codeInsertions, 153 int[] transformedIndices) 154 { 155 int instructionCount = instructions.size(); 156 int insertionCount = codeInsertions.size(); 157 158 int newSize = calculateNewSize(instructions, codeInsertions); 159 List newInstructions = new ArrayList(newSize); 160 161 int currentInsertionIndex = 0; 162 CodeInsertion currentInsertion = (CodeInsertion)codeInsertions.get(0); 163 for (int i = 0; i < instructionCount; i++) { 164 if (currentInsertion.getPosition() < i && currentInsertionIndex < insertionCount - 1) { 165 ++currentInsertionIndex; 166 currentInsertion = (CodeInsertion)codeInsertions.get(currentInsertionIndex); 167 } 168 int addedBefore = 0; 169 int addedAfter = 0; 170 if (currentInsertion.getPosition() == i) { 171 addedBefore = addInstructions(newInstructions, currentInsertion.getPreInstructions()); 172 } 173 newInstructions.add(instructions.get(i)); 174 if (currentInsertion.getPosition() == i) { 175 addedAfter = addInstructions(newInstructions, currentInsertion.getPostInstructions()); 176 } 177 if (addedBefore > 0 || addedAfter > 0) { 178 shiftIndices(i, addedBefore, addedAfter, transformedIndices, currentInsertion.isShiftTarget()); 179 } 180 } 181 182 return newInstructions; 183 } 184 185 private static int calculateNewSize(List instructions, List codeInsertions) { 186 187 int insertionCount = codeInsertions.size(); 188 int newSize = instructions.size(); 189 for (int i = 0; i < insertionCount; i++) { 190 CodeInsertion insertion = (CodeInsertion)codeInsertions.get(i); 191 192 AbstractInstruction[] preInstructions = insertion.getPreInstructions(); 193 if (preInstructions != null) { 194 newSize += preInstructions.length; 195 } 196 AbstractInstruction[] postInstructions = insertion.getPostInstructions(); 197 if (postInstructions != null) { 198 newSize += postInstructions.length; 199 } 200 } 201 return newSize; 202 } 203 204 private static void shiftIndices(int currentIndex, 205 int addedBefore, 206 int addedAfter, 207 int[] transformedIndices, 208 boolean shiftTarget) 209 { 210 if (!shiftTarget) { 211 transformedIndices[currentIndex] += addedBefore; 212 } 213 for (int i = currentIndex + 1; i < transformedIndices.length; i++) { 214 transformedIndices[i] += addedBefore + addedAfter; 215 } 216 } 217 218 private static int addInstructions(List newInstructions, 219 AbstractInstruction[] insertedInstructions) 220 { 221 if (insertedInstructions != null) { 222 for (int i = 0; i < insertedInstructions.length; i++) { 223 newInstructions.add(insertedInstructions[i]); 224 } 225 return insertedInstructions.length; 226 } else { 227 return 0; 228 } 229 } 230 231 private static void calculateOffsets(List instructions, 232 int[] offsets) 233 { 234 int instructionCount = instructions.size(); 235 int currentOffset = 0; 236 for (int i = 0; i < instructionCount; i++) { 237 offsets[i] = currentOffset; 238 239 AbstractInstruction instr = (AbstractInstruction)instructions.get(i); 240 241 int currentSize; 242 if (instr instanceof PaddedInstruction) { 243 currentSize = ((PaddedInstruction)instr).getPaddedSize(currentOffset); 244 } else { 245 currentSize = instr.getSize(); 246 } 247 currentOffset += currentSize; 248 } 249 } 250 251 private static void applyOffsets(List instructions, int[] offsets) { 252 int instructionCount = instructions.size(); 253 for (int i = 0; i < instructionCount; i++) { 254 AbstractInstruction instr = (AbstractInstruction)instructions.get(i); 255 instr.setOffset(offsets[i]); 256 } 257 } 258 259 private static void adjustOffsets(List instructions, 260 List newInstructions, 261 int[] oldOffsets, 262 int[] newOffsets, 263 int[] transformedIndices) 264 throws InvalidByteCodeException 265 { 266 int instructionCount = instructions.size(); 267 for (int sourceIndex = 0; sourceIndex < instructionCount; sourceIndex++) { 268 AbstractInstruction currentInstruction = (AbstractInstruction)instructions.get(sourceIndex); 269 int branchOffset = getBranchOffset(currentInstruction); 270 if (branchOffset == 0) { 271 continue; 272 } 273 if (currentInstruction instanceof TableSwitchInstruction) { 274 int[] jumpOffsets = ((TableSwitchInstruction)currentInstruction).getJumpOffsets(); 275 for (int i = 0; i < jumpOffsets.length; i++) { 276 int targetIndex = getBranchTargetIndex(instructions, sourceIndex, jumpOffsets[i]); 277 jumpOffsets[i] = 278 calculateNewBranchOffset(newInstructions, sourceIndex, targetIndex, transformedIndices, newOffsets); 279 } 280 } else if (currentInstruction instanceof LookupSwitchInstruction) { 281 List matchOffsetPairs = ((LookupSwitchInstruction)currentInstruction).getMatchOffsetPairs(); 282 for (int i = 0; i < matchOffsetPairs.size(); i++) { 283 MatchOffsetPair matchOffsetPair = 284 (MatchOffsetPair)matchOffsetPairs.get(i); 285 int targetIndex = getBranchTargetIndex(instructions, sourceIndex, matchOffsetPair.getOffset()); 286 matchOffsetPair.setOffset( 287 calculateNewBranchOffset(newInstructions, sourceIndex, targetIndex, transformedIndices, newOffsets) 288 ); 289 } 290 } 291 int targetIndex = getBranchTargetIndex(instructions, sourceIndex, branchOffset); 292 293 setBranchOffset( 294 currentInstruction, 295 calculateNewBranchOffset(newInstructions, sourceIndex, targetIndex, transformedIndices, newOffsets) 296 ); 297 298 } 299 } 300 301 private static int calculateNewBranchOffset(List newInstructions, 302 int sourceIndex, 303 int targetIndex, 304 int[] transformedIndices, 305 int[] newOffsets) 306 { 307 int transformedSourceIndex = transformedIndices[sourceIndex]; 308 int transformedTargetIndex = transformedIndices[targetIndex]; 309 310 int newBranchOffset = newOffsets[transformedTargetIndex] - newOffsets[transformedSourceIndex]; 311 312 return newBranchOffset; 313 } 314 315 316 private static int getBranchOffset(AbstractInstruction instruction) { 317 318 int branchOffset = 0; 319 if (instruction.getOpcode() == Opcodes.OPCODE_GOTO_W) { 320 branchOffset = ((ImmediateIntInstruction)instruction).getImmediateInt(); 321 } else if (instruction instanceof TableSwitchInstruction) { 322 branchOffset = ((TableSwitchInstruction)instruction).getDefaultOffset(); 323 } else if (instruction instanceof LookupSwitchInstruction) { 324 branchOffset = ((LookupSwitchInstruction)instruction).getDefaultOffset(); 325 } else if (instruction instanceof BranchInstruction) { 326 branchOffset = ((BranchInstruction)instruction).getBranchOffset(); 327 } 328 return branchOffset; 329 } 330 331 private static void setBranchOffset(AbstractInstruction instruction, 332 int branchOffset) 333 { 334 if (instruction.getOpcode() == Opcodes.OPCODE_GOTO_W) { 335 ((ImmediateIntInstruction)instruction).setImmediateInt(branchOffset); 336 } else if (instruction instanceof TableSwitchInstruction) { 337 ((TableSwitchInstruction)instruction).setDefaultOffset(branchOffset); 338 } else if (instruction instanceof LookupSwitchInstruction) { 339 ((LookupSwitchInstruction)instruction).setDefaultOffset(branchOffset); 340 } else if (instruction instanceof BranchInstruction) { 341 ((BranchInstruction)instruction).setBranchOffset(branchOffset); 342 } 343 } 344 345 private static int getBranchTargetIndex(List instructions, 346 int sourceIndex, 347 int branchOffset) 348 throws InvalidByteCodeException 349 { 350 int instructionsCount = instructions.size(); 351 int startOffset = ((AbstractInstruction)instructions.get(sourceIndex)).getOffset(); 352 int step = branchOffset > 0 ? 1 : -1; 353 for (int i = sourceIndex + step; i >= 0 && i < instructionsCount; i += step) { 354 int targetOffset = ((AbstractInstruction)instructions.get(i)).getOffset(); 355 if (targetOffset - startOffset == branchOffset) { 356 return i; 357 } 358 } 359 throw new InvalidByteCodeException("Invalid branch target"); 360 } 361 362 private static void adjustExceptionTable(int[] oldOffsets, 363 int[] newOffsets, 364 int[] transformedIndices, 365 CodeAttribute codeAttribute) 366 throws InvalidByteCodeException 367 { 368 369 ExceptionTableEntry[] exceptionTable = codeAttribute.getExceptionTable(); 370 if (exceptionTable == null) { 371 return; 372 } 373 374 for (int i = 0; i < exceptionTable.length; i++) { 375 ExceptionTableEntry currentEntry = exceptionTable[i]; 376 int startPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getStartPc()); 377 int endPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getEndPc()); 378 int handlerPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getHandlerPc()); 379 if (startPcIndex < 0 || endPcIndex < 0 || handlerPcIndex < 0 || 380 startPcIndex == oldOffsets.length || 381 endPcIndex == oldOffsets.length || 382 handlerPcIndex == oldOffsets.length) 383 { 384 throw new InvalidByteCodeException("Invalid exception table"); 385 } 386 currentEntry.setStartPc(newOffsets[transformedIndices[startPcIndex]]); 387 currentEntry.setEndPc(newOffsets[transformedIndices[endPcIndex]]); 388 currentEntry.setHandlerPc(newOffsets[transformedIndices[handlerPcIndex]]); 389 } 390 391 } 392 393 private static void adjustLineNumberTable(int[] oldOffsets, 394 int[] newOffsets, 395 int[] transformedIndices, 396 CodeAttribute codeAttribute) 397 throws InvalidByteCodeException 398 { 399 400 LineNumberTableAttribute lineNumberTableAttribute = 401 (LineNumberTableAttribute)codeAttribute.findAttribute(LineNumberTableAttribute.class); 402 if (lineNumberTableAttribute == null) { 403 return; 404 } 405 LineNumberTableEntry[] lineNumberTable = lineNumberTableAttribute.getLineNumberTable(); 406 407 for (int i = 0; i < lineNumberTable.length; i++) { 408 LineNumberTableEntry currentEntry = lineNumberTable[i]; 409 int startPcIndex = Arrays.binarySearch(oldOffsets, currentEntry.getStartPc()); 410 if (startPcIndex < 0 || startPcIndex == oldOffsets.length) { 411 throw new InvalidByteCodeException("Invalid line number table " + currentEntry.getStartPc()); 412 } 413 currentEntry.setStartPc(newOffsets[transformedIndices[startPcIndex]]); 414 } 415 416 } 417 418 private int position; 419 private AbstractInstruction[] preInstructions; 420 private AbstractInstruction[] postInstructions; 421 private boolean shiftTarget; 422 423 439 public CodeInsertion(int position, 440 AbstractInstruction[] preInstructions, 441 AbstractInstruction[] postInstructions, 442 boolean shiftTarget) 443 { 444 this.position = position; 445 this.preInstructions = preInstructions; 446 this.postInstructions = postInstructions; 447 this.shiftTarget = shiftTarget; 448 } 449 450 457 public int getPosition() { 458 return position; 459 } 460 461 468 public void setPosition(int position) { 469 this.position = position; 470 } 471 472 476 public AbstractInstruction[] getPreInstructions() { 477 return preInstructions; 478 } 479 480 484 public void setPreInstructions(AbstractInstruction[] preInstructions) { 485 this.preInstructions = preInstructions; 486 } 487 488 492 public AbstractInstruction[] getPostInstructions() { 493 return postInstructions; 494 } 495 496 500 public void setPostInstructions(AbstractInstruction[] postInstructions) { 501 this.postInstructions = postInstructions; 502 } 503 504 510 public boolean isShiftTarget() { 511 return shiftTarget; 512 } 513 514 520 public void setShiftTarget(boolean shiftTarget) { 521 this.shiftTarget = shiftTarget; 522 } 523 } 524 | Popular Tags |