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 */ 21package proguard.optimize.evaluation; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.visitor.AttributeVisitor; 26import proguard.classfile.constant.*; 27import proguard.classfile.constant.visitor.ConstantVisitor; 28import proguard.classfile.editor.*; 29import proguard.classfile.instruction.*; 30import proguard.classfile.instruction.visitor.InstructionVisitor; 31import proguard.classfile.util.*; 32import proguard.classfile.visitor.*; 33import proguard.evaluation.value.*; 34import proguard.optimize.info.SimpleEnumMarker; 35 36/** 37 * This AttributeVisitor simplifies the use of enums in the code attributes that 38 * it visits. 39 * 40 * @see SimpleEnumMarker 41 * @see MemberReferenceFixer 42 * @author Eric Lafortune 43 */ 44public class SimpleEnumUseSimplifier 45extends SimplifiedVisitor 46implements AttributeVisitor, 47 InstructionVisitor, 48 ConstantVisitor, 49 ParameterVisitor 50{ 51 //* 52 private static final boolean DEBUG = false; 53 /*/ 54 private static boolean DEBUG = System.getProperty("enum") != null; 55 //*/ 56 57 private final InstructionVisitor extraInstructionVisitor; 58 59 private final PartialEvaluator partialEvaluator; 60 private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); 61 private final ConstantVisitor nullParameterFixer = new ReferencedMemberVisitor(new AllParameterVisitor(this)); 62 63 // Fields acting as parameters and return values for the visitor methods. 64 private Clazz invocationClazz; 65 private Method invocationMethod; 66 private CodeAttribute invocationCodeAttribute; 67 private int invocationOffset; 68 private boolean isSimpleEnum; 69 70 71 /** 72 * Creates a new SimpleEnumUseSimplifier. 73 */ 74 public SimpleEnumUseSimplifier() 75 { 76 this(new PartialEvaluator(), null); 77 } 78 79 80 /** 81 * Creates a new SimpleEnumDescriptorSimplifier. 82 * @param partialEvaluator the partial evaluator that will 83 * execute the code and provide 84 * information about the results. 85 * @param extraInstructionVisitor an optional extra visitor for all 86 * simplified instructions. 87 */ 88 public SimpleEnumUseSimplifier(PartialEvaluator partialEvaluator, 89 InstructionVisitor extraInstructionVisitor) 90 { 91 this.partialEvaluator = partialEvaluator; 92 this.extraInstructionVisitor = extraInstructionVisitor; 93 } 94 95 96 // Implementations for AttributeVisitor. 97 98 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 99 100 101 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 102 { 103 if (DEBUG) 104 { 105 System.out.println("SimpleEnumUseSimplifier: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); 106 } 107 108 // Skip the non-static methods of simple enum classes. 109 if (SimpleEnumMarker.isSimpleEnum(clazz) && 110 (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0) 111 { 112 return; 113 } 114 115 // Evaluate the method. 116 partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 117 118 int codeLength = codeAttribute.u4codeLength; 119 120 // Reset the code changes. 121 codeAttributeEditor.reset(codeLength); 122 123 // Replace any instructions that can be simplified. 124 for (int offset = 0; offset < codeLength; offset++) 125 { 126 if (partialEvaluator.isTraced(offset)) 127 { 128 Instruction instruction = InstructionFactory.create(codeAttribute.code, 129 offset); 130 131 instruction.accept(clazz, method, codeAttribute, offset, this); 132 } 133 } 134 135 // Apply all accumulated changes to the code. 136 codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); 137 } 138 139 140 // Implementations for InstructionVisitor. 141 142 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 143 { 144 switch (simpleInstruction.opcode) 145 { 146 case InstructionConstants.OP_AALOAD: 147 { 148 if (isPushingSimpleEnum(offset)) 149 { 150 // Load a simple enum integer from an integer array. 151 replaceInstruction(clazz, 152 offset, 153 simpleInstruction, 154 new SimpleInstruction( 155 InstructionConstants.OP_IALOAD)); 156 } 157 break; 158 } 159 case InstructionConstants.OP_AASTORE: 160 { 161 if (isPoppingSimpleEnumArray(offset, 2)) 162 { 163 // Store a simple enum integer in an integer array. 164 replaceInstruction(clazz, 165 offset, 166 simpleInstruction, 167 new SimpleInstruction(InstructionConstants.OP_IASTORE)); 168 169 // Replace any producers of null constants. 170 replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); 171 } 172 break; 173 } 174 case InstructionConstants.OP_ARETURN: 175 { 176 if (isReturningSimpleEnum(clazz, method)) 177 { 178 // Return a simple enum integer instead of an enum. 179 replaceInstruction(clazz, 180 offset, 181 simpleInstruction, 182 new SimpleInstruction(InstructionConstants.OP_IRETURN)); 183 184 // Replace any producers of null constants. 185 replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); 186 } 187 break; 188 } 189 } 190 } 191 192 193 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 194 { 195 int variableIndex = variableInstruction.variableIndex; 196 197 switch (variableInstruction.opcode) 198 { 199 case InstructionConstants.OP_ALOAD: 200 case InstructionConstants.OP_ALOAD_0: 201 case InstructionConstants.OP_ALOAD_1: 202 case InstructionConstants.OP_ALOAD_2: 203 case InstructionConstants.OP_ALOAD_3: 204 { 205 if (isPushingSimpleEnum(offset)) 206 { 207 // Load a simple enum integer instead of an enum. 208 replaceInstruction(clazz, 209 offset, 210 variableInstruction, 211 new VariableInstruction(InstructionConstants.OP_ILOAD, 212 variableIndex)); 213 214 // Replace any producers of null constants. 215 replaceNullVariableProducers(clazz, 216 method, 217 codeAttribute, 218 offset, 219 variableIndex); 220 } 221 break; 222 } 223 case InstructionConstants.OP_ASTORE: 224 case InstructionConstants.OP_ASTORE_0: 225 case InstructionConstants.OP_ASTORE_1: 226 case InstructionConstants.OP_ASTORE_2: 227 case InstructionConstants.OP_ASTORE_3: 228 { 229 if (!partialEvaluator.isSubroutineStart(offset) && 230 isPoppingSimpleEnum(offset)) 231 { 232 // Store a simple enum integer instead of an enum. 233 replaceInstruction(clazz, 234 offset, 235 variableInstruction, 236 new VariableInstruction(InstructionConstants.OP_ISTORE, 237 variableIndex)); 238 239 // Replace any producers of null constants. 240 replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); 241 } 242 break; 243 } 244 } 245 } 246 247 248 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 249 { 250 switch (constantInstruction.opcode) 251 { 252 case InstructionConstants.OP_PUTSTATIC: 253 case InstructionConstants.OP_PUTFIELD: 254 { 255 // Replace any producers of null constants. 256 invocationClazz = clazz; 257 invocationMethod = method; 258 invocationCodeAttribute = codeAttribute; 259 invocationOffset = offset; 260 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, 261 nullParameterFixer); 262 break; 263 } 264 case InstructionConstants.OP_INVOKEVIRTUAL: 265 { 266 // Check if the instruction is calling a simple enum. 267 String invokedMethodName = 268 clazz.getRefName(constantInstruction.constantIndex); 269 String invokedMethodType = 270 clazz.getRefType(constantInstruction.constantIndex); 271 int stackEntryIndex = 272 ClassUtil.internalMethodParameterSize(invokedMethodType); 273 if (isPoppingSimpleEnum(offset, stackEntryIndex)) 274 { 275 replaceSupportedMethod(clazz, 276 offset, 277 constantInstruction, 278 invokedMethodName, 279 invokedMethodType); 280 } 281 282 // Fall through to check the parameters. 283 } 284 case InstructionConstants.OP_INVOKESPECIAL: 285 case InstructionConstants.OP_INVOKESTATIC: 286 case InstructionConstants.OP_INVOKEINTERFACE: 287 { 288 // Replace any producers of null constants. 289 invocationClazz = clazz; 290 invocationMethod = method; 291 invocationCodeAttribute = codeAttribute; 292 invocationOffset = offset; 293 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, 294 nullParameterFixer); 295 break; 296 } 297 case InstructionConstants.OP_ANEWARRAY: 298 { 299 int constantIndex = constantInstruction.constantIndex; 300 301 if (isReferencingSimpleEnum(clazz, constantIndex) && 302 !ClassUtil.isInternalArrayType(clazz.getClassName(constantIndex))) 303 { 304 // Create an integer array instead of an enum array. 305 replaceInstruction(clazz, 306 offset, 307 constantInstruction, 308 new SimpleInstruction(InstructionConstants.OP_NEWARRAY, 309 InstructionConstants.ARRAY_T_INT)); 310 } 311 break; 312 } 313 case InstructionConstants.OP_CHECKCAST: 314 { 315 if (isPoppingSimpleEnum(offset)) 316 { 317 // Enum classes can only be simple if the checkcast 318 // succeeds, so we can delete it. 319 deleteInstruction(clazz, 320 offset, 321 constantInstruction); 322 323 // Replace any producers of null constants. 324 replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); 325 } 326 break; 327 } 328 case InstructionConstants.OP_INSTANCEOF: 329 { 330 if (isPoppingSimpleEnum(offset)) 331 { 332 // Enum classes can only be simple if the instanceof 333 // succeeds, so we can push a constant result. 334 replaceInstruction(clazz, 335 offset, 336 constantInstruction, 337 new SimpleInstruction(InstructionConstants.OP_ICONST_1)); 338 339 // Replace any producers of null constants. 340 replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); 341 } 342 break; 343 } 344 } 345 } 346 347 348 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 349 { 350 switch (branchInstruction.opcode) 351 { 352 case InstructionConstants.OP_IFACMPEQ: 353 { 354 if (isPoppingSimpleEnum(offset)) 355 { 356 // Compare simple enum integers instead of enums. 357 replaceInstruction(clazz, 358 offset, 359 branchInstruction, 360 new BranchInstruction(InstructionConstants.OP_IFICMPEQ, 361 branchInstruction.branchOffset)); 362 } 363 break; 364 } 365 case InstructionConstants.OP_IFACMPNE: 366 { 367 if (isPoppingSimpleEnum(offset)) 368 { 369 // Compare simple enum integers instead of enums. 370 replaceInstruction(clazz, 371 offset, 372 branchInstruction, 373 new BranchInstruction(InstructionConstants.OP_IFICMPNE, 374 branchInstruction.branchOffset)); 375 } 376 break; 377 } 378 case InstructionConstants.OP_IFNULL: 379 { 380 if (isPoppingSimpleEnum(offset)) 381 { 382 // Compare with 0 instead of null. 383 replaceInstruction(clazz, 384 offset, 385 branchInstruction, 386 new BranchInstruction( 387 InstructionConstants.OP_IFEQ, 388 branchInstruction.branchOffset)); 389 } 390 break; 391 } 392 case InstructionConstants.OP_IFNONNULL: 393 { 394 if (isPoppingSimpleEnum(offset)) 395 { 396 // Compare with 0 instead of null. 397 replaceInstruction(clazz, 398 offset, 399 branchInstruction, 400 new BranchInstruction(InstructionConstants.OP_IFNE, 401 branchInstruction.branchOffset)); 402 } 403 break; 404 } 405 } 406 } 407 408 409 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 410 { 411 } 412 413 414 // Implementations for ConstantVisitor. 415 416 public void visitAnyConstant(Clazz clazz, Constant constant) {} 417 418 419 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 420 { 421 // Does the constant refer to a simple enum type? 422 isSimpleEnum = isSimpleEnum(stringConstant.referencedClass); 423 } 424 425 426 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 427 { 428 // Does the constant refer to a simple enum type? 429 isSimpleEnum = isSimpleEnum(classConstant.referencedClass); 430 } 431 432 433 // Implementations for ParameterVisitor. 434 435 public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) 436 { 437 // Check if the parameter is passing a simple enum as a more general 438 // type. 439 if (!ClassUtil.isInternalPrimitiveType(parameterType.charAt(0)) && 440 isSimpleEnum(referencedClass)) 441 { 442 // Replace any producers of null constants for this parameter. 443 int stackEntryIndex = parameterSize - parameterOffset - 1; 444 445 replaceNullStackEntryProducers(invocationClazz, 446 invocationMethod, 447 invocationCodeAttribute, 448 invocationOffset, 449 stackEntryIndex); 450 } 451 } 452 453 454 // Small utility methods. 455 456 /** 457 * Returns whether the constant at the given offset is referencing a 458 * simple enum class. 459 */ 460 private boolean isReferencingSimpleEnum(Clazz clazz, int constantIndex) 461 { 462 isSimpleEnum = false; 463 464 clazz.constantPoolEntryAccept(constantIndex, this); 465 466 return isSimpleEnum; 467 } 468 469 470 /** 471 * Returns whether the given method is returning a simple enum class. 472 */ 473 private boolean isReturningSimpleEnum(Clazz clazz, Method method) 474 { 475 String descriptor = method.getDescriptor(clazz); 476 String returnType = ClassUtil.internalMethodReturnType(descriptor); 477 478 if (ClassUtil.isInternalClassType(returnType) && 479 !ClassUtil.isInternalArrayType(returnType)) 480 { 481 Clazz[] referencedClasses = 482 ((ProgramMethod)method).referencedClasses; 483 484 if (referencedClasses != null) 485 { 486 int returnedClassIndex = 487 new DescriptorClassEnumeration(descriptor).classCount() - 1; 488 489 Clazz returnedClass = referencedClasses[returnedClassIndex]; 490 491 return isSimpleEnum(returnedClass); 492 } 493 } 494 495 return false; 496 } 497 498 499 /** 500 * Returns whether the instruction at the given offset is pushing a simple 501 * enum class. 502 */ 503 private boolean isPushingSimpleEnum(int offset) 504 { 505 ReferenceValue referenceValue = 506 partialEvaluator.getStackAfter(offset).getTop(0).referenceValue(); 507 508 Clazz referencedClass = referenceValue.getReferencedClass(); 509 510 return isSimpleEnum(referencedClass) && 511 !ClassUtil.isInternalArrayType(referenceValue.getType()); 512 } 513 514 515 /** 516 * Returns whether the instruction at the given offset is popping a simple 517 * enum class. 518 */ 519 private boolean isPoppingSimpleEnum(int offset) 520 { 521 return isPoppingSimpleEnum(offset, 0); 522 } 523 524 525 /** 526 * Returns whether the instruction at the given offset is popping a simple 527 * enum class. 528 */ 529 private boolean isPoppingSimpleEnum(int offset, int stackEntryIndex) 530 { 531 ReferenceValue referenceValue = 532 partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); 533 534 return isSimpleEnum(referenceValue.getReferencedClass()) && 535 !ClassUtil.isInternalArrayType(referenceValue.getType()); 536 } 537 538 539 /** 540 * Returns whether the instruction at the given offset is popping a simple 541 * enum type. This includes simple enum arrays. 542 */ 543 private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex) 544 { 545 ReferenceValue referenceValue = 546 partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); 547 548 return isSimpleEnum(referenceValue.getReferencedClass()); 549 } 550 551 552 /** 553 * Returns whether the instruction at the given offset is popping a 554 * one-dimensional simple enum array. 555 */ 556 private boolean isPoppingSimpleEnumArray(int offset, int stackEntryIndex) 557 { 558 ReferenceValue referenceValue = 559 partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); 560 561 return isSimpleEnum(referenceValue.getReferencedClass()) && 562 ClassUtil.internalArrayTypeDimensionCount(referenceValue.getType()) == 1; 563 } 564 565 566 /** 567 * Returns whether the given class is not null and a simple enum class. 568 */ 569 private boolean isSimpleEnum(Clazz clazz) 570 { 571 return clazz != null && 572 SimpleEnumMarker.isSimpleEnum(clazz); 573 } 574 575 576 /** 577 * Returns whether the specified enum method is supported for simple enums. 578 */ 579 private void replaceSupportedMethod(Clazz clazz, 580 int offset, 581 Instruction instruction, 582 String name, 583 String type) 584 { 585 if (name.equals(ClassConstants.METHOD_NAME_ORDINAL) && 586 type.equals(ClassConstants.METHOD_TYPE_ORDINAL)) 587 { 588 Instruction[] replacementInstructions = new Instruction[] 589 { 590 new SimpleInstruction(InstructionConstants.OP_ICONST_1), 591 new SimpleInstruction(InstructionConstants.OP_ISUB), 592 }; 593 594 replaceInstructions(clazz, 595 offset, 596 instruction, 597 replacementInstructions); 598 } 599 } 600 601 602 /** 603 * Replaces the instruction at the given offset by the given instructions. 604 */ 605 private void replaceInstructions(Clazz clazz, 606 int offset, 607 Instruction instruction, 608 Instruction[] replacementInstructions) 609 { 610 if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstructions.length+" instructions"); 611 612 codeAttributeEditor.replaceInstruction(offset, replacementInstructions); 613 614 // Visit the instruction, if required. 615 if (extraInstructionVisitor != null) 616 { 617 // Note: we're not passing the right arguments for now, knowing that 618 // they aren't used anyway. 619 instruction.accept(clazz, null, null, offset, extraInstructionVisitor); 620 } 621 } 622 623 624 /** 625 * Replaces the instruction at the given offset by the given instruction, 626 * popping any now unused stack entries. 627 */ 628 private void replaceInstruction(Clazz clazz, 629 int offset, 630 Instruction instruction, 631 Instruction replacementInstruction) 632 { 633 // Pop unneeded stack entries if necessary. 634 int popCount = 635 instruction.stackPopCount(clazz) - 636 replacementInstruction.stackPopCount(clazz); 637 638 insertPopInstructions(offset, popCount); 639 640 if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)")); 641 642 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 643 644 // Visit the instruction, if required. 645 if (extraInstructionVisitor != null) 646 { 647 // Note: we're not passing the right arguments for now, knowing that 648 // they aren't used anyway. 649 instruction.accept(clazz, null, null, offset, extraInstructionVisitor); 650 } 651 } 652 653 654 /** 655 * Deletes the instruction at the given offset, popping any now unused 656 * stack entries. 657 */ 658 private void deleteInstruction(Clazz clazz, 659 int offset, 660 Instruction instruction) 661 { 662 // Pop unneeded stack entries if necessary. 663 //int popCount = instruction.stackPopCount(clazz); 664 // 665 //insertPopInstructions(offset, popCount); 666 // 667 //if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset)+(popCount == 0 ? "" : " ("+popCount+" pops)")); 668 669 if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset)); 670 671 codeAttributeEditor.deleteInstruction(offset); 672 673 // Visit the instruction, if required. 674 if (extraInstructionVisitor != null) 675 { 676 // Note: we're not passing the right arguments for now, knowing that 677 // they aren't used anyway. 678 instruction.accept(clazz, null, null, offset, extraInstructionVisitor); 679 } 680 } 681 682 683 /** 684 * Pops the given number of stack entries before the instruction at the 685 * given offset. 686 */ 687 private void insertPopInstructions(int offset, int popCount) 688 { 689 switch (popCount) 690 { 691 case 0: 692 { 693 break; 694 } 695 case 1: 696 { 697 // Insert a single pop instruction. 698 Instruction popInstruction = 699 new SimpleInstruction(InstructionConstants.OP_POP); 700 701 codeAttributeEditor.insertBeforeInstruction(offset, 702 popInstruction); 703 break; 704 } 705 case 2: 706 { 707 // Insert a single pop2 instruction. 708 Instruction popInstruction = 709 new SimpleInstruction(InstructionConstants.OP_POP2); 710 711 codeAttributeEditor.insertBeforeInstruction(offset, 712 popInstruction); 713 break; 714 } 715 default: 716 { 717 // Insert the specified number of pop instructions. 718 Instruction[] popInstructions = 719 new Instruction[popCount / 2 + popCount % 2]; 720 721 Instruction popInstruction = 722 new SimpleInstruction(InstructionConstants.OP_POP2); 723 724 for (int index = 0; index < popCount / 2; index++) 725 { 726 popInstructions[index] = popInstruction; 727 } 728 729 if (popCount % 2 == 1) 730 { 731 popInstruction = 732 new SimpleInstruction(InstructionConstants.OP_POP); 733 734 popInstructions[popCount / 2] = popInstruction; 735 } 736 737 codeAttributeEditor.insertBeforeInstruction(offset, 738 popInstructions); 739 break; 740 } 741 } 742 } 743 744 745 /** 746 * Replaces aconst_null producers of the consumer of the top stack entry 747 * at the given offset by iconst_0. 748 */ 749 private void replaceNullStackEntryProducers(Clazz clazz, 750 Method method, 751 CodeAttribute codeAttribute, 752 int consumerOffset) 753 { 754 replaceNullStackEntryProducers(clazz, method, codeAttribute, consumerOffset, 0); 755 } 756 757 758 /** 759 * Replaces aconst_null producers of the specified stack entry by 760 * iconst_0. 761 */ 762 private void replaceNullStackEntryProducers(Clazz clazz, 763 Method method, 764 CodeAttribute codeAttribute, 765 int consumerOffset, 766 int stackEntryIndex) 767 { 768 InstructionOffsetValue producerOffsets = 769 partialEvaluator.getStackBefore(consumerOffset).getTopActualProducerValue(stackEntryIndex).instructionOffsetValue(); 770 771 for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++) 772 { 773 int producerOffset = producerOffsets.instructionOffset(index); 774 775 // TODO: A method might be pushing the null constant. 776 if (producerOffset >= 0 && 777 codeAttribute.code[producerOffset] == InstructionConstants.OP_ACONST_NULL) 778 { 779 // Replace pushing null by pushing 0. 780 replaceInstruction(clazz, 781 producerOffset, 782 new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), 783 new SimpleInstruction(InstructionConstants.OP_ICONST_0)); 784 } 785 } 786 } 787 788 789 /** 790 * Replaces aconst_null/astore producers of the specified reference variable by 791 * iconst_0/istore. 792 */ 793 private void replaceNullVariableProducers(Clazz clazz, 794 Method method, 795 CodeAttribute codeAttribute, 796 int consumerOffset, 797 int variableIndex) 798 { 799 InstructionOffsetValue producerOffsets = 800 partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); 801 802 for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++) 803 { 804 int producerOffset = producerOffsets.instructionOffset(index); 805 806 if (producerOffset >= 0 && 807 partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue().isNull() == Value.ALWAYS) 808 { 809 // Replace loading null by loading 0. 810 replaceInstruction(clazz, 811 producerOffset, 812 new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex), 813 new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex)); 814 815 // Replace pushing null by pushing 0. 816 replaceNullStackEntryProducers(clazz, method, codeAttribute, producerOffset); 817 } 818 } 819 } 820} 821