1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.optimize.evaluation; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.*; 25 import proguard.classfile.attribute.visitor.AttributeVisitor; 26 import proguard.classfile.editor.*; 27 import proguard.classfile.instruction.*; 28 import proguard.classfile.instruction.visitor.InstructionVisitor; 29 import proguard.classfile.util.*; 30 import proguard.classfile.visitor.ClassPrinter; 31 import proguard.evaluation.TracedVariables; 32 import proguard.evaluation.value.*; 33 import proguard.optimize.info.SideEffectInstructionChecker; 34 35 import java.util.Arrays; 36 37 /** 38 * This AttributeVisitor simplifies the code attributes that it visits, based 39 * on partial evaluation. 40 * 41 * @author Eric Lafortune 42 */ 43 public class EvaluationSimplifier 44 extends SimplifiedVisitor 45 implements AttributeVisitor, 46 InstructionVisitor 47 { 48 private static final int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f); 49 private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0); 50 51 //* 52 private static final boolean DEBUG = false; 53 /*/ 54 private static boolean DEBUG = System.getProperty("es") != null; 55 //*/ 56 57 private final InstructionVisitor extraInstructionVisitor; 58 59 private final PartialEvaluator partialEvaluator; 60 private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); 61 private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true); 62 63 64 /** 65 * Creates a new EvaluationSimplifier. 66 */ EvaluationSimplifier()67 public EvaluationSimplifier() 68 { 69 this(new PartialEvaluator(), null); 70 } 71 72 73 /** 74 * Creates a new EvaluationSimplifier. 75 * @param partialEvaluator the partial evaluator that will 76 * execute the code and provide 77 * information about the results. 78 * @param extraInstructionVisitor an optional extra visitor for all 79 * simplified instructions. 80 */ EvaluationSimplifier(PartialEvaluator partialEvaluator, InstructionVisitor extraInstructionVisitor)81 public EvaluationSimplifier(PartialEvaluator partialEvaluator, 82 InstructionVisitor extraInstructionVisitor) 83 { 84 this.partialEvaluator = partialEvaluator; 85 this.extraInstructionVisitor = extraInstructionVisitor; 86 } 87 88 89 // Implementations for AttributeVisitor. 90 visitAnyAttribute(Clazz clazz, Attribute attribute)91 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 92 93 visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)94 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 95 { 96 // DEBUG = 97 // clazz.getName().equals("abc/Def") && 98 // method.getName(clazz).equals("abc"); 99 100 // TODO: Remove this when the evaluation simplifier has stabilized. 101 // Catch any unexpected exceptions from the actual visiting method. 102 try 103 { 104 // Process the code. 105 visitCodeAttribute0(clazz, method, codeAttribute); 106 } 107 catch (RuntimeException ex) 108 { 109 System.err.println("Unexpected error while simplifying instructions after partial evaluation:"); 110 System.err.println(" Class = ["+clazz.getName()+"]"); 111 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 112 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 113 System.err.println("Not optimizing this method"); 114 115 if (DEBUG) 116 { 117 method.accept(clazz, new ClassPrinter()); 118 119 throw ex; 120 } 121 } 122 } 123 124 visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)125 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 126 { 127 if (DEBUG) 128 { 129 System.out.println(); 130 System.out.println("EvaluationSimplifier ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 131 } 132 133 // Evaluate the method. 134 partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 135 136 int codeLength = codeAttribute.u4codeLength; 137 138 // Reset the code changes. 139 codeAttributeEditor.reset(codeLength); 140 141 // Replace any instructions that can be simplified. 142 for (int offset = 0; offset < codeLength; offset++) 143 { 144 if (partialEvaluator.isTraced(offset)) 145 { 146 Instruction instruction = InstructionFactory.create(codeAttribute.code, 147 offset); 148 149 instruction.accept(clazz, method, codeAttribute, offset, this); 150 } 151 } 152 153 // Apply all accumulated changes to the code. 154 codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); 155 } 156 157 158 // Implementations for InstructionVisitor. 159 visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)160 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 161 { 162 switch (simpleInstruction.opcode) 163 { 164 case InstructionConstants.OP_IALOAD: 165 case InstructionConstants.OP_BALOAD: 166 case InstructionConstants.OP_CALOAD: 167 case InstructionConstants.OP_SALOAD: 168 case InstructionConstants.OP_IADD: 169 case InstructionConstants.OP_ISUB: 170 case InstructionConstants.OP_IMUL: 171 case InstructionConstants.OP_IDIV: 172 case InstructionConstants.OP_IREM: 173 case InstructionConstants.OP_INEG: 174 case InstructionConstants.OP_ISHL: 175 case InstructionConstants.OP_ISHR: 176 case InstructionConstants.OP_IUSHR: 177 case InstructionConstants.OP_IAND: 178 case InstructionConstants.OP_IOR: 179 case InstructionConstants.OP_IXOR: 180 case InstructionConstants.OP_L2I: 181 case InstructionConstants.OP_F2I: 182 case InstructionConstants.OP_D2I: 183 case InstructionConstants.OP_I2B: 184 case InstructionConstants.OP_I2C: 185 case InstructionConstants.OP_I2S: 186 case InstructionConstants.OP_ARRAYLENGTH: 187 replaceIntegerPushInstruction(clazz, offset, simpleInstruction); 188 break; 189 190 case InstructionConstants.OP_LALOAD: 191 case InstructionConstants.OP_LADD: 192 case InstructionConstants.OP_LSUB: 193 case InstructionConstants.OP_LMUL: 194 case InstructionConstants.OP_LDIV: 195 case InstructionConstants.OP_LREM: 196 case InstructionConstants.OP_LNEG: 197 case InstructionConstants.OP_LSHL: 198 case InstructionConstants.OP_LSHR: 199 case InstructionConstants.OP_LUSHR: 200 case InstructionConstants.OP_LAND: 201 case InstructionConstants.OP_LOR: 202 case InstructionConstants.OP_LXOR: 203 case InstructionConstants.OP_I2L: 204 case InstructionConstants.OP_F2L: 205 case InstructionConstants.OP_D2L: 206 replaceLongPushInstruction(clazz, offset, simpleInstruction); 207 break; 208 209 case InstructionConstants.OP_FALOAD: 210 case InstructionConstants.OP_FADD: 211 case InstructionConstants.OP_FSUB: 212 case InstructionConstants.OP_FMUL: 213 case InstructionConstants.OP_FDIV: 214 case InstructionConstants.OP_FREM: 215 case InstructionConstants.OP_FNEG: 216 case InstructionConstants.OP_I2F: 217 case InstructionConstants.OP_L2F: 218 case InstructionConstants.OP_D2F: 219 replaceFloatPushInstruction(clazz, offset, simpleInstruction); 220 break; 221 222 case InstructionConstants.OP_DALOAD: 223 case InstructionConstants.OP_DADD: 224 case InstructionConstants.OP_DSUB: 225 case InstructionConstants.OP_DMUL: 226 case InstructionConstants.OP_DDIV: 227 case InstructionConstants.OP_DREM: 228 case InstructionConstants.OP_DNEG: 229 case InstructionConstants.OP_I2D: 230 case InstructionConstants.OP_L2D: 231 case InstructionConstants.OP_F2D: 232 replaceDoublePushInstruction(clazz, offset, simpleInstruction); 233 break; 234 235 case InstructionConstants.OP_AALOAD: 236 replaceReferencePushInstruction(clazz, offset, simpleInstruction); 237 break; 238 } 239 } 240 241 visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)242 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 243 { 244 int variableIndex = variableInstruction.variableIndex; 245 246 switch (variableInstruction.opcode) 247 { 248 case InstructionConstants.OP_ILOAD: 249 case InstructionConstants.OP_ILOAD_0: 250 case InstructionConstants.OP_ILOAD_1: 251 case InstructionConstants.OP_ILOAD_2: 252 case InstructionConstants.OP_ILOAD_3: 253 replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex); 254 break; 255 256 case InstructionConstants.OP_LLOAD: 257 case InstructionConstants.OP_LLOAD_0: 258 case InstructionConstants.OP_LLOAD_1: 259 case InstructionConstants.OP_LLOAD_2: 260 case InstructionConstants.OP_LLOAD_3: 261 replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex); 262 break; 263 264 case InstructionConstants.OP_FLOAD: 265 case InstructionConstants.OP_FLOAD_0: 266 case InstructionConstants.OP_FLOAD_1: 267 case InstructionConstants.OP_FLOAD_2: 268 case InstructionConstants.OP_FLOAD_3: 269 replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex); 270 break; 271 272 case InstructionConstants.OP_DLOAD: 273 case InstructionConstants.OP_DLOAD_0: 274 case InstructionConstants.OP_DLOAD_1: 275 case InstructionConstants.OP_DLOAD_2: 276 case InstructionConstants.OP_DLOAD_3: 277 replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex); 278 break; 279 280 case InstructionConstants.OP_ALOAD: 281 case InstructionConstants.OP_ALOAD_0: 282 case InstructionConstants.OP_ALOAD_1: 283 case InstructionConstants.OP_ALOAD_2: 284 case InstructionConstants.OP_ALOAD_3: 285 replaceReferencePushInstruction(clazz, offset, variableInstruction); 286 break; 287 288 case InstructionConstants.OP_ASTORE: 289 case InstructionConstants.OP_ASTORE_0: 290 case InstructionConstants.OP_ASTORE_1: 291 case InstructionConstants.OP_ASTORE_2: 292 case InstructionConstants.OP_ASTORE_3: 293 deleteReferencePopInstruction(clazz, offset, variableInstruction); 294 break; 295 296 case InstructionConstants.OP_RET: 297 replaceBranchInstruction(clazz, offset, variableInstruction); 298 break; 299 } 300 } 301 302 visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)303 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 304 { 305 switch (constantInstruction.opcode) 306 { 307 case InstructionConstants.OP_GETSTATIC: 308 case InstructionConstants.OP_GETFIELD: 309 replaceAnyPushInstruction(clazz, offset, constantInstruction); 310 break; 311 312 case InstructionConstants.OP_INVOKEVIRTUAL: 313 case InstructionConstants.OP_INVOKESPECIAL: 314 case InstructionConstants.OP_INVOKESTATIC: 315 case InstructionConstants.OP_INVOKEINTERFACE: 316 if (constantInstruction.stackPushCount(clazz) > 0 && 317 !sideEffectInstructionChecker.hasSideEffects(clazz, 318 method, 319 codeAttribute, 320 offset, 321 constantInstruction)) 322 { 323 replaceAnyPushInstruction(clazz, offset, constantInstruction); 324 } 325 326 break; 327 328 case InstructionConstants.OP_CHECKCAST: 329 replaceReferencePushInstruction(clazz, offset, constantInstruction); 330 break; 331 } 332 } 333 334 visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)335 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 336 { 337 switch (branchInstruction.opcode) 338 { 339 case InstructionConstants.OP_GOTO: 340 case InstructionConstants.OP_GOTO_W: 341 // Don't replace unconditional branches. 342 break; 343 344 case InstructionConstants.OP_JSR: 345 case InstructionConstants.OP_JSR_W: 346 replaceJsrInstruction(clazz, offset, branchInstruction); 347 break; 348 349 default: 350 replaceBranchInstruction(clazz, offset, branchInstruction); 351 break; 352 } 353 } 354 355 visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)356 public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) 357 { 358 // First try to simplify it to a simple branch. 359 replaceBranchInstruction(clazz, offset, tableSwitchInstruction); 360 361 // Otherwise try to simplify simple enum switches. 362 if (!codeAttributeEditor.isModified(offset)) 363 { 364 replaceSimpleEnumSwitchInstruction(clazz, 365 codeAttribute, 366 offset, 367 tableSwitchInstruction); 368 369 // Otherwise make sure all branch targets are valid. 370 if (!codeAttributeEditor.isModified(offset)) 371 { 372 cleanUpSwitchInstruction(clazz, offset, tableSwitchInstruction); 373 374 trimSwitchInstruction(clazz, offset, tableSwitchInstruction); 375 } 376 } 377 } 378 379 visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)380 public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) 381 { 382 // First try to simplify it to a simple branch. 383 replaceBranchInstruction(clazz, offset, lookUpSwitchInstruction); 384 385 // Otherwise try to simplify simple enum switches. 386 if (!codeAttributeEditor.isModified(offset)) 387 { 388 replaceSimpleEnumSwitchInstruction(clazz, 389 codeAttribute, 390 offset, 391 lookUpSwitchInstruction); 392 393 // Otherwise make sure all branch targets are valid. 394 if (!codeAttributeEditor.isModified(offset)) 395 { 396 cleanUpSwitchInstruction(clazz, offset, lookUpSwitchInstruction); 397 398 trimSwitchInstruction(clazz, offset, lookUpSwitchInstruction); 399 } 400 } 401 } 402 403 404 // Small utility methods. 405 406 /** 407 * Replaces the push instruction at the given offset by a simpler push 408 * instruction, if possible. 409 */ replaceAnyPushInstruction(Clazz clazz, int offset, Instruction instruction)410 private void replaceAnyPushInstruction(Clazz clazz, 411 int offset, 412 Instruction instruction) 413 { 414 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 415 if (pushedValue.isParticular()) 416 { 417 switch (pushedValue.computationalType()) 418 { 419 case Value.TYPE_INTEGER: 420 replaceIntegerPushInstruction(clazz, offset, instruction); 421 break; 422 case Value.TYPE_LONG: 423 replaceLongPushInstruction(clazz, offset, instruction); 424 break; 425 case Value.TYPE_FLOAT: 426 replaceFloatPushInstruction(clazz, offset, instruction); 427 break; 428 case Value.TYPE_DOUBLE: 429 replaceDoublePushInstruction(clazz, offset, instruction); 430 break; 431 case Value.TYPE_REFERENCE: 432 replaceReferencePushInstruction(clazz, offset, instruction); 433 break; 434 } 435 } 436 } 437 438 439 /** 440 * Replaces the integer pushing instruction at the given offset by a simpler 441 * push instruction, if possible. 442 */ replaceIntegerPushInstruction(Clazz clazz, int offset, Instruction instruction)443 private void replaceIntegerPushInstruction(Clazz clazz, 444 int offset, 445 Instruction instruction) 446 { 447 replaceIntegerPushInstruction(clazz, 448 offset, 449 instruction, 450 partialEvaluator.getVariablesBefore(offset).size()); 451 } 452 453 454 /** 455 * Replaces the integer pushing instruction at the given offset by a simpler 456 * push instruction, if possible. 457 */ replaceIntegerPushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)458 private void replaceIntegerPushInstruction(Clazz clazz, 459 int offset, 460 Instruction instruction, 461 int maxVariableIndex) 462 { 463 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 464 if (pushedValue.isParticular()) 465 { 466 // Push a constant instead. 467 int value = pushedValue.integerValue().value(); 468 if ((short)value == value) 469 { 470 replaceConstantPushInstruction(clazz, 471 offset, 472 instruction, 473 InstructionConstants.OP_SIPUSH, 474 value); 475 } 476 else 477 { 478 ConstantPoolEditor constantPoolEditor = 479 new ConstantPoolEditor((ProgramClass)clazz); 480 481 Instruction replacementInstruction = 482 new ConstantInstruction(InstructionConstants.OP_LDC, 483 constantPoolEditor.addIntegerConstant(value)); 484 485 replaceInstruction(clazz, offset, instruction, replacementInstruction); 486 } 487 } 488 else if (pushedValue.isSpecific()) 489 { 490 // Load an equivalent lower-numbered variable instead, if any. 491 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 492 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 493 { 494 if (pushedValue.equals(variables.load(variableIndex))) 495 { 496 replaceVariablePushInstruction(clazz, 497 offset, 498 instruction, 499 InstructionConstants.OP_ILOAD, 500 variableIndex); 501 break; 502 } 503 } 504 } 505 } 506 507 508 /** 509 * Replaces the long pushing instruction at the given offset by a simpler 510 * push instruction, if possible. 511 */ replaceLongPushInstruction(Clazz clazz, int offset, Instruction instruction)512 private void replaceLongPushInstruction(Clazz clazz, 513 int offset, 514 Instruction instruction) 515 { 516 replaceLongPushInstruction(clazz, 517 offset, 518 instruction, 519 partialEvaluator.getVariablesBefore(offset).size()); 520 } 521 522 523 /** 524 * Replaces the long pushing instruction at the given offset by a simpler 525 * push instruction, if possible. 526 */ replaceLongPushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)527 private void replaceLongPushInstruction(Clazz clazz, 528 int offset, 529 Instruction instruction, 530 int maxVariableIndex) 531 { 532 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 533 if (pushedValue.isParticular()) 534 { 535 // Push a constant instead. 536 long value = pushedValue.longValue().value(); 537 if (value == 0L || 538 value == 1L) 539 { 540 replaceConstantPushInstruction(clazz, 541 offset, 542 instruction, 543 InstructionConstants.OP_LCONST_0, 544 (int)value); 545 } 546 else 547 { 548 ConstantPoolEditor constantPoolEditor = 549 new ConstantPoolEditor((ProgramClass)clazz); 550 551 Instruction replacementInstruction = 552 new ConstantInstruction(InstructionConstants.OP_LDC2_W, 553 constantPoolEditor.addLongConstant(value)); 554 555 replaceInstruction(clazz, offset, instruction, replacementInstruction); 556 } 557 } 558 else if (pushedValue.isSpecific()) 559 { 560 // Load an equivalent lower-numbered variable instead, if any. 561 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 562 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 563 { 564 // Note that we have to check the second part as well. 565 if (pushedValue.equals(variables.load(variableIndex)) && 566 variables.load(variableIndex + 1) != null && 567 variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) 568 { 569 replaceVariablePushInstruction(clazz, 570 offset, 571 instruction, 572 InstructionConstants.OP_LLOAD, 573 variableIndex); 574 } 575 } 576 } 577 } 578 579 580 /** 581 * Replaces the float pushing instruction at the given offset by a simpler 582 * push instruction, if possible. 583 */ replaceFloatPushInstruction(Clazz clazz, int offset, Instruction instruction)584 private void replaceFloatPushInstruction(Clazz clazz, 585 int offset, 586 Instruction instruction) 587 { 588 replaceFloatPushInstruction(clazz, 589 offset, 590 instruction, 591 partialEvaluator.getVariablesBefore(offset).size()); 592 } 593 594 595 /** 596 * Replaces the float pushing instruction at the given offset by a simpler 597 * push instruction, if possible. 598 */ replaceFloatPushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)599 private void replaceFloatPushInstruction(Clazz clazz, 600 int offset, 601 Instruction instruction, 602 int maxVariableIndex) 603 { 604 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 605 if (pushedValue.isParticular()) 606 { 607 // Push a constant instead. 608 // Make sure to distinguish between +0.0 and -0.0. 609 float value = pushedValue.floatValue().value(); 610 if (value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS || 611 value == 1.0f || 612 value == 2.0f) 613 { 614 replaceConstantPushInstruction(clazz, 615 offset, 616 instruction, 617 InstructionConstants.OP_FCONST_0, 618 (int)value); 619 } 620 else 621 { 622 ConstantPoolEditor constantPoolEditor = 623 new ConstantPoolEditor((ProgramClass)clazz); 624 625 Instruction replacementInstruction = 626 new ConstantInstruction(InstructionConstants.OP_LDC, 627 constantPoolEditor.addFloatConstant(value)); 628 629 replaceInstruction(clazz, offset, instruction, replacementInstruction); 630 } 631 } 632 else if (pushedValue.isSpecific()) 633 { 634 // Load an equivalent lower-numbered variable instead, if any. 635 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 636 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 637 { 638 if (pushedValue.equals(variables.load(variableIndex))) 639 { 640 replaceVariablePushInstruction(clazz, 641 offset, 642 instruction, 643 InstructionConstants.OP_FLOAD, 644 variableIndex); 645 } 646 } 647 } 648 } 649 650 651 /** 652 * Replaces the double pushing instruction at the given offset by a simpler 653 * push instruction, if possible. 654 */ replaceDoublePushInstruction(Clazz clazz, int offset, Instruction instruction)655 private void replaceDoublePushInstruction(Clazz clazz, 656 int offset, 657 Instruction instruction) 658 { 659 replaceDoublePushInstruction(clazz, 660 offset, 661 instruction, 662 partialEvaluator.getVariablesBefore(offset).size()); 663 } 664 665 666 /** 667 * Replaces the double pushing instruction at the given offset by a simpler 668 * push instruction, if possible. 669 */ replaceDoublePushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)670 private void replaceDoublePushInstruction(Clazz clazz, 671 int offset, 672 Instruction instruction, 673 int maxVariableIndex) 674 { 675 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 676 if (pushedValue.isParticular()) 677 { 678 // Push a constant instead. 679 // Make sure to distinguish between +0.0 and -0.0. 680 double value = pushedValue.doubleValue().value(); 681 if (value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS || 682 value == 1.0) 683 { 684 replaceConstantPushInstruction(clazz, 685 offset, 686 instruction, 687 InstructionConstants.OP_DCONST_0, 688 (int)value); 689 } 690 else 691 { 692 ConstantPoolEditor constantPoolEditor = 693 new ConstantPoolEditor((ProgramClass)clazz); 694 695 Instruction replacementInstruction = 696 new ConstantInstruction(InstructionConstants.OP_LDC2_W, 697 constantPoolEditor.addDoubleConstant(value)); 698 699 replaceInstruction(clazz, offset, instruction, replacementInstruction); 700 } 701 } 702 else if (pushedValue.isSpecific()) 703 { 704 // Load an equivalent lower-numbered variable instead, if any. 705 TracedVariables variables = partialEvaluator.getVariablesBefore(offset); 706 for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) 707 { 708 // Note that we have to check the second part as well. 709 if (pushedValue.equals(variables.load(variableIndex)) && 710 variables.load(variableIndex + 1) != null && 711 variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) 712 { 713 replaceVariablePushInstruction(clazz, 714 offset, 715 instruction, 716 InstructionConstants.OP_DLOAD, 717 variableIndex); 718 } 719 } 720 } 721 } 722 723 724 /** 725 * Replaces the reference pushing instruction at the given offset by a 726 * simpler push instruction, if possible. 727 */ replaceReferencePushInstruction(Clazz clazz, int offset, Instruction instruction)728 private void replaceReferencePushInstruction(Clazz clazz, 729 int offset, 730 Instruction instruction) 731 { 732 Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); 733 if (pushedValue.isParticular()) 734 { 735 // A reference value can only be specific if it is null. 736 replaceConstantPushInstruction(clazz, 737 offset, 738 instruction, 739 InstructionConstants.OP_ACONST_NULL, 740 0); 741 } 742 } 743 744 745 /** 746 * Replaces the instruction at a given offset by a given push instruction 747 * of a constant. 748 */ replaceConstantPushInstruction(Clazz clazz, int offset, Instruction instruction, byte replacementOpcode, int value)749 private void replaceConstantPushInstruction(Clazz clazz, 750 int offset, 751 Instruction instruction, 752 byte replacementOpcode, 753 int value) 754 { 755 Instruction replacementInstruction = 756 new SimpleInstruction(replacementOpcode, value); 757 758 replaceInstruction(clazz, offset, instruction, replacementInstruction); 759 } 760 761 762 /** 763 * Replaces the instruction at a given offset by a given push instruction 764 * of a variable. 765 */ replaceVariablePushInstruction(Clazz clazz, int offset, Instruction instruction, byte replacementOpcode, int variableIndex)766 private void replaceVariablePushInstruction(Clazz clazz, 767 int offset, 768 Instruction instruction, 769 byte replacementOpcode, 770 int variableIndex) 771 { 772 Instruction replacementInstruction = 773 new VariableInstruction(replacementOpcode, variableIndex); 774 775 replaceInstruction(clazz, offset, instruction, replacementInstruction); 776 } 777 778 779 /** 780 * Replaces the given 'jsr' instruction by a simpler branch instruction, 781 * if it jumps to a subroutine that doesn't return or a subroutine that 782 * is only called from one place. 783 */ replaceJsrInstruction(Clazz clazz, int offset, BranchInstruction branchInstruction)784 private void replaceJsrInstruction(Clazz clazz, 785 int offset, 786 BranchInstruction branchInstruction) 787 { 788 // Is the subroutine ever returning? 789 int subroutineStart = offset + branchInstruction.branchOffset; 790 if (!partialEvaluator.isSubroutineReturning(subroutineStart) || 791 partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1) 792 { 793 // All 'jsr' instructions to this subroutine can be replaced 794 // by unconditional branch instructions. 795 replaceBranchInstruction(clazz, offset, branchInstruction); 796 } 797 else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset))) 798 { 799 // We have to make sure the instruction after this 'jsr' 800 // instruction is valid, even if it is never reached. 801 replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction); 802 } 803 } 804 805 806 /** 807 * Deletes the reference popping instruction at the given offset, if 808 * it is at the start of a subroutine that doesn't return or a subroutine 809 * that is only called from one place. 810 */ deleteReferencePopInstruction(Clazz clazz, int offset, Instruction instruction)811 private void deleteReferencePopInstruction(Clazz clazz, 812 int offset, 813 Instruction instruction) 814 { 815 if (partialEvaluator.isSubroutineStart(offset) && 816 (!partialEvaluator.isSubroutineReturning(offset) || 817 partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1)) 818 { 819 if (DEBUG) System.out.println(" Deleting store of subroutine return address "+instruction.toString(offset)); 820 821 // A reference value can only be specific if it is null. 822 codeAttributeEditor.deleteInstruction(offset); 823 } 824 } 825 826 827 /** 828 * Deletes the given branch instruction, or replaces it by a simpler branch 829 * instruction, if possible. 830 */ replaceBranchInstruction(Clazz clazz, int offset, Instruction instruction)831 private void replaceBranchInstruction(Clazz clazz, 832 int offset, 833 Instruction instruction) 834 { 835 InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); 836 837 // Is there exactly one branch target (not from a goto or jsr)? 838 if (branchTargets != null && 839 branchTargets.instructionOffsetCount() == 1) 840 { 841 // Is it branching to the next instruction? 842 int branchOffset = branchTargets.instructionOffset(0) - offset; 843 if (branchOffset == instruction.length(offset)) 844 { 845 if (DEBUG) System.out.println(" Ignoring zero branch instruction at ["+offset+"]"); 846 } 847 else 848 { 849 // Replace the branch instruction by a simple branch instruction. 850 Instruction replacementInstruction = 851 new BranchInstruction(InstructionConstants.OP_GOTO, 852 branchOffset); 853 854 replaceInstruction(clazz, offset, instruction, replacementInstruction); 855 } 856 } 857 } 858 859 860 /** 861 * Replaces the given table switch instruction, if it is based on the value 862 * of a fixed array. This is typical for switches on simple enums. 863 */ replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)864 private void replaceSimpleEnumSwitchInstruction(Clazz clazz, 865 CodeAttribute codeAttribute, 866 int offset, 867 TableSwitchInstruction tableSwitchInstruction) 868 { 869 // Check if the switch instruction is consuming a single value loaded 870 // from a fully specified array. 871 InstructionOffsetValue producerOffsets = 872 partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue(); 873 874 if (producerOffsets.instructionOffsetCount() == 1) 875 { 876 int producerOffset = producerOffsets.instructionOffset(0); 877 878 if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD && 879 !codeAttributeEditor.isModified(producerOffset)) 880 { 881 ReferenceValue referenceValue = 882 partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue(); 883 884 if (referenceValue.isParticular()) 885 { 886 // Simplify the entire construct. 887 replaceSimpleEnumSwitchInstruction(clazz, 888 codeAttribute, 889 producerOffset, 890 offset, 891 tableSwitchInstruction, 892 referenceValue); 893 } 894 } 895 } 896 } 897 898 899 /** 900 * Replaces the given table switch instruction that is based on a value of 901 * the given fixed array. 902 */ replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int loadOffset, int switchOffset, TableSwitchInstruction tableSwitchInstruction, ReferenceValue mappingValue)903 private void replaceSimpleEnumSwitchInstruction(Clazz clazz, 904 CodeAttribute codeAttribute, 905 int loadOffset, 906 int switchOffset, 907 TableSwitchInstruction tableSwitchInstruction, 908 ReferenceValue mappingValue) 909 { 910 ValueFactory valueFactory = new ParticularValueFactory(); 911 912 // Transform the jump offsets. 913 int[] jumpOffsets = tableSwitchInstruction.jumpOffsets; 914 int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()]; 915 916 for (int index = 0; index < newJumpOffsets.length; index++) 917 { 918 int switchCase = 919 mappingValue.integerArrayLoad(valueFactory.createIntegerValue( 920 index), 921 valueFactory).value(); 922 923 newJumpOffsets[index] = 924 switchCase >= tableSwitchInstruction.lowCase && 925 switchCase <= tableSwitchInstruction.highCase ? 926 jumpOffsets[switchCase - tableSwitchInstruction.lowCase] : 927 tableSwitchInstruction.defaultOffset; 928 } 929 930 // Update the instruction. 931 tableSwitchInstruction.lowCase = 0; 932 tableSwitchInstruction.highCase = newJumpOffsets.length - 1; 933 tableSwitchInstruction.jumpOffsets = newJumpOffsets; 934 935 // Replace the original one with the new version. 936 replaceSimpleEnumSwitchInstruction(clazz, 937 loadOffset, 938 switchOffset, 939 tableSwitchInstruction); 940 941 cleanUpSwitchInstruction(clazz, switchOffset, tableSwitchInstruction); 942 943 trimSwitchInstruction(clazz, switchOffset, tableSwitchInstruction); 944 } 945 946 947 /** 948 * Replaces the given look up switch instruction, if it is based on the 949 * value of a fixed array. This is typical for switches on simple enums. 950 */ replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookupSwitchInstruction)951 private void replaceSimpleEnumSwitchInstruction(Clazz clazz, 952 CodeAttribute codeAttribute, 953 int offset, 954 LookUpSwitchInstruction lookupSwitchInstruction) 955 { 956 // Check if the switch instruction is consuming a single value loaded 957 // from a fully specified array. 958 InstructionOffsetValue producerOffsets = 959 partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue(); 960 961 if (producerOffsets.instructionOffsetCount() == 1) 962 { 963 int producerOffset = producerOffsets.instructionOffset(0); 964 965 if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD && 966 !codeAttributeEditor.isModified(producerOffset)) 967 { 968 ReferenceValue referenceValue = 969 partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue(); 970 971 if (referenceValue.isParticular()) 972 { 973 // Simplify the entire construct. 974 replaceSimpleEnumSwitchInstruction(clazz, 975 codeAttribute, 976 producerOffset, 977 offset, 978 lookupSwitchInstruction, 979 referenceValue); 980 } 981 } 982 } 983 } 984 985 986 /** 987 * Replaces the given look up switch instruction that is based on a value of 988 * the given fixed array. This is typical for switches on simple enums. 989 */ replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int loadOffset, int switchOffset, LookUpSwitchInstruction lookupSwitchInstruction, ReferenceValue mappingValue)990 private void replaceSimpleEnumSwitchInstruction(Clazz clazz, 991 CodeAttribute codeAttribute, 992 int loadOffset, 993 int switchOffset, 994 LookUpSwitchInstruction lookupSwitchInstruction, 995 ReferenceValue mappingValue) 996 { 997 ValueFactory valueFactory = new ParticularValueFactory(); 998 999 // Transform the jump offsets. 1000 int[] cases = lookupSwitchInstruction.cases; 1001 int[] jumpOffsets = lookupSwitchInstruction.jumpOffsets; 1002 int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()]; 1003 1004 for (int index = 0; index < newJumpOffsets.length; index++) 1005 { 1006 int switchCase = 1007 mappingValue.integerArrayLoad(valueFactory.createIntegerValue(index), 1008 valueFactory).value(); 1009 1010 int caseIndex = Arrays.binarySearch(cases, switchCase); 1011 1012 newJumpOffsets[index] = caseIndex >= 0 ? 1013 jumpOffsets[caseIndex] : 1014 lookupSwitchInstruction.defaultOffset; 1015 } 1016 1017 // Replace the original lookup switch with a table switch. 1018 TableSwitchInstruction replacementSwitchInstruction = 1019 new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, 1020 lookupSwitchInstruction.defaultOffset, 1021 0, 1022 newJumpOffsets.length - 1, 1023 newJumpOffsets); 1024 1025 replaceSimpleEnumSwitchInstruction(clazz, 1026 loadOffset, 1027 switchOffset, 1028 replacementSwitchInstruction); 1029 1030 cleanUpSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction); 1031 1032 trimSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction); 1033 } 1034 1035 1036 /** 1037 * Makes sure all branch targets of the given switch instruction are valid. 1038 */ cleanUpSwitchInstruction(Clazz clazz, int offset, SwitchInstruction switchInstruction)1039 private void cleanUpSwitchInstruction(Clazz clazz, 1040 int offset, 1041 SwitchInstruction switchInstruction) 1042 { 1043 // Get the actual branch targets. 1044 InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); 1045 1046 // Get an offset that can serve as a valid default offset. 1047 int defaultOffset = 1048 branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) - 1049 offset; 1050 1051 Instruction replacementInstruction = null; 1052 1053 // Check the jump offsets. 1054 int[] jumpOffsets = switchInstruction.jumpOffsets; 1055 for (int index = 0; index < jumpOffsets.length; index++) 1056 { 1057 if (!branchTargets.contains(offset + jumpOffsets[index])) 1058 { 1059 // Replace the unused offset. 1060 jumpOffsets[index] = defaultOffset; 1061 1062 // Remember to replace the instruction. 1063 replacementInstruction = switchInstruction; 1064 } 1065 } 1066 1067 // Check the default offset. 1068 if (!branchTargets.contains(offset + switchInstruction.defaultOffset)) 1069 { 1070 // Replace the unused offset. 1071 switchInstruction.defaultOffset = defaultOffset; 1072 1073 // Remember to replace the instruction. 1074 replacementInstruction = switchInstruction; 1075 } 1076 1077 if (replacementInstruction != null) 1078 { 1079 replaceInstruction(clazz, offset, switchInstruction, replacementInstruction); 1080 } 1081 } 1082 1083 1084 /** 1085 * Trims redundant offsets from the given switch instruction. 1086 */ trimSwitchInstruction(Clazz clazz, int offset, TableSwitchInstruction tableSwitchInstruction)1087 private void trimSwitchInstruction(Clazz clazz, 1088 int offset, 1089 TableSwitchInstruction tableSwitchInstruction) 1090 { 1091 // Get an offset that can serve as a valid default offset. 1092 int defaultOffset = tableSwitchInstruction.defaultOffset; 1093 int[] jumpOffsets = tableSwitchInstruction.jumpOffsets; 1094 int length = jumpOffsets.length; 1095 1096 // Find the lowest index with a non-default jump offset. 1097 int lowIndex = 0; 1098 while (lowIndex < length && 1099 jumpOffsets[lowIndex] == defaultOffset) 1100 { 1101 lowIndex++; 1102 } 1103 1104 // Find the highest index with a non-default jump offset. 1105 int highIndex = length - 1; 1106 while (highIndex >= 0 && 1107 jumpOffsets[highIndex] == defaultOffset) 1108 { 1109 highIndex--; 1110 } 1111 1112 // Can we use a shorter array? 1113 int newLength = highIndex - lowIndex + 1; 1114 if (newLength < length) 1115 { 1116 if (newLength <= 0) 1117 { 1118 // Replace the switch instruction by a simple branch instruction. 1119 Instruction replacementInstruction = 1120 new BranchInstruction(InstructionConstants.OP_GOTO, 1121 defaultOffset); 1122 1123 replaceInstruction(clazz, offset, tableSwitchInstruction, 1124 replacementInstruction); 1125 } 1126 else 1127 { 1128 // Trim the array. 1129 int[] newJumpOffsets = new int[newLength]; 1130 1131 System.arraycopy(jumpOffsets, lowIndex, newJumpOffsets, 0, newLength); 1132 1133 tableSwitchInstruction.jumpOffsets = newJumpOffsets; 1134 tableSwitchInstruction.lowCase += lowIndex; 1135 tableSwitchInstruction.highCase -= length - newLength - lowIndex; 1136 1137 replaceInstruction(clazz, offset, tableSwitchInstruction, 1138 tableSwitchInstruction); 1139 } 1140 } 1141 } 1142 1143 1144 /** 1145 * Trims redundant offsets from the given switch instruction. 1146 */ trimSwitchInstruction(Clazz clazz, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)1147 private void trimSwitchInstruction(Clazz clazz, 1148 int offset, 1149 LookUpSwitchInstruction lookUpSwitchInstruction) 1150 { 1151 // Get an offset that can serve as a valid default offset. 1152 int defaultOffset = lookUpSwitchInstruction.defaultOffset; 1153 int[] jumpOffsets = lookUpSwitchInstruction.jumpOffsets; 1154 int length = jumpOffsets.length; 1155 int newLength = length; 1156 1157 // Count the default jump offsets. 1158 for (int index = 0; index < length; index++) 1159 { 1160 if (jumpOffsets[index] == defaultOffset) 1161 { 1162 newLength--; 1163 } 1164 } 1165 1166 // Can we use shorter arrays? 1167 if (newLength < length) 1168 { 1169 if (newLength <= 0) 1170 { 1171 // Replace the switch instruction by a simple branch instruction. 1172 Instruction replacementInstruction = 1173 new BranchInstruction(InstructionConstants.OP_GOTO, 1174 defaultOffset); 1175 1176 replaceInstruction(clazz, offset, lookUpSwitchInstruction, 1177 replacementInstruction); 1178 } 1179 else 1180 { 1181 // Remove redundant entries from the arrays. 1182 int[] cases = lookUpSwitchInstruction.cases; 1183 int[] newJumpOffsets = new int[newLength]; 1184 int[] newCases = new int[newLength]; 1185 1186 int newIndex = 0; 1187 1188 for (int index = 0; index < length; index++) 1189 { 1190 if (jumpOffsets[index] != defaultOffset) 1191 { 1192 newJumpOffsets[newIndex] = jumpOffsets[index]; 1193 newCases[newIndex++] = cases[index]; 1194 } 1195 } 1196 1197 lookUpSwitchInstruction.jumpOffsets = newJumpOffsets; 1198 lookUpSwitchInstruction.cases = newCases; 1199 1200 replaceInstruction(clazz, offset, lookUpSwitchInstruction, 1201 lookUpSwitchInstruction); 1202 } 1203 } 1204 } 1205 1206 1207 /** 1208 * Replaces the given instruction by an infinite loop. 1209 */ replaceByInfiniteLoop(Clazz clazz, int offset, Instruction instruction)1210 private void replaceByInfiniteLoop(Clazz clazz, 1211 int offset, 1212 Instruction instruction) 1213 { 1214 // Replace the instruction by an infinite loop. 1215 Instruction replacementInstruction = 1216 new BranchInstruction(InstructionConstants.OP_GOTO, 0); 1217 1218 if (DEBUG) System.out.println(" Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset)); 1219 1220 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 1221 1222 // Visit the instruction, if required. 1223 if (extraInstructionVisitor != null) 1224 { 1225 // Note: we're not passing the right arguments for now, knowing that 1226 // they aren't used anyway. 1227 instruction.accept(clazz, 1228 null, 1229 null, 1230 offset, 1231 extraInstructionVisitor); 1232 } 1233 } 1234 1235 1236 /** 1237 * Replaces the instruction at a given offset by a given push instruction. 1238 */ replaceInstruction(Clazz clazz, int offset, Instruction instruction, Instruction replacementInstruction)1239 private void replaceInstruction(Clazz clazz, 1240 int offset, 1241 Instruction instruction, 1242 Instruction replacementInstruction) 1243 { 1244 // Pop unneeded stack entries if necessary. 1245 int popCount = 1246 instruction.stackPopCount(clazz) - 1247 replacementInstruction.stackPopCount(clazz); 1248 1249 insertPopInstructions(offset, popCount); 1250 1251 if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)")); 1252 1253 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 1254 1255 // Visit the instruction, if required. 1256 if (extraInstructionVisitor != null) 1257 { 1258 // Note: we're not passing the right arguments for now, knowing that 1259 // they aren't used anyway. 1260 instruction.accept(clazz, null, null, offset, extraInstructionVisitor); 1261 } 1262 } 1263 1264 1265 /** 1266 * Pops the given number of stack entries before the instruction at the 1267 * given offset. 1268 */ insertPopInstructions(int offset, int popCount)1269 private void insertPopInstructions(int offset, int popCount) 1270 { 1271 switch (popCount) 1272 { 1273 case 0: 1274 { 1275 break; 1276 } 1277 case 1: 1278 { 1279 // Insert a single pop instruction. 1280 Instruction popInstruction = 1281 new SimpleInstruction(InstructionConstants.OP_POP); 1282 1283 codeAttributeEditor.insertBeforeInstruction(offset, 1284 popInstruction); 1285 break; 1286 } 1287 case 2: 1288 { 1289 // Insert a single pop2 instruction. 1290 Instruction popInstruction = 1291 new SimpleInstruction(InstructionConstants.OP_POP2); 1292 1293 codeAttributeEditor.insertBeforeInstruction(offset, 1294 popInstruction); 1295 break; 1296 } 1297 default: 1298 { 1299 // Insert the specified number of pop instructions. 1300 Instruction[] popInstructions = 1301 new Instruction[popCount / 2 + popCount % 2]; 1302 1303 Instruction popInstruction = 1304 new SimpleInstruction(InstructionConstants.OP_POP2); 1305 1306 for (int index = 0; index < popCount / 2; index++) 1307 { 1308 popInstructions[index] = popInstruction; 1309 } 1310 1311 if (popCount % 2 == 1) 1312 { 1313 popInstruction = 1314 new SimpleInstruction(InstructionConstants.OP_POP); 1315 1316 popInstructions[popCount / 2] = popInstruction; 1317 } 1318 1319 codeAttributeEditor.insertBeforeInstruction(offset, 1320 popInstructions); 1321 break; 1322 } 1323 } 1324 } 1325 1326 1327 /** 1328 * Replaces the simple enum switch instructions at a given offsets by a 1329 * given replacement instruction. 1330 */ replaceSimpleEnumSwitchInstruction(Clazz clazz, int loadOffset, int switchOffset, SwitchInstruction replacementSwitchInstruction)1331 private void replaceSimpleEnumSwitchInstruction(Clazz clazz, 1332 int loadOffset, 1333 int switchOffset, 1334 SwitchInstruction replacementSwitchInstruction) 1335 { 1336 if (DEBUG) System.out.println(" Replacing switch instruction at ["+switchOffset+"] -> ["+loadOffset+"] swap + pop, "+replacementSwitchInstruction.toString(switchOffset)+")"); 1337 1338 // Remove the array load instruction. 1339 codeAttributeEditor.replaceInstruction(loadOffset, new Instruction[] 1340 { 1341 new SimpleInstruction(InstructionConstants.OP_SWAP), 1342 new SimpleInstruction(InstructionConstants.OP_POP), 1343 }); 1344 1345 // Replace the switch instruction. 1346 codeAttributeEditor.replaceInstruction(switchOffset, replacementSwitchInstruction); 1347 1348 // Visit the instruction, if required. 1349 if (extraInstructionVisitor != null) 1350 { 1351 // Note: we're not passing the right arguments for now, knowing that 1352 // they aren't used anyway. 1353 replacementSwitchInstruction.accept(clazz, 1354 null, 1355 null, 1356 switchOffset, 1357 extraInstructionVisitor); 1358 } 1359 } 1360 } 1361