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.constant.*; 27 import proguard.classfile.constant.visitor.ConstantVisitor; 28 import proguard.classfile.editor.*; 29 import proguard.classfile.instruction.*; 30 import proguard.classfile.instruction.visitor.InstructionVisitor; 31 import proguard.classfile.util.*; 32 import proguard.classfile.visitor.*; 33 import proguard.evaluation.value.*; 34 import 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 */ 44 public class SimpleEnumUseSimplifier 45 extends SimplifiedVisitor 46 implements 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 */ SimpleEnumUseSimplifier()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 */ SimpleEnumUseSimplifier(PartialEvaluator partialEvaluator, InstructionVisitor extraInstructionVisitor)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 visitAnyAttribute(Clazz clazz, Attribute attribute)98 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 99 100 visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)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 visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)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 visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)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 visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)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 visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)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 visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)409 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 410 { 411 } 412 413 414 // Implementations for ConstantVisitor. 415 visitAnyConstant(Clazz clazz, Constant constant)416 public void visitAnyConstant(Clazz clazz, Constant constant) {} 417 418 visitStringConstant(Clazz clazz, StringConstant stringConstant)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 visitClassConstant(Clazz clazz, ClassConstant classConstant)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 visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass)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 */ isReferencingSimpleEnum(Clazz clazz, int constantIndex)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 */ isReturningSimpleEnum(Clazz clazz, Method method)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 */ isPushingSimpleEnum(int offset)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 */ isPoppingSimpleEnum(int offset)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 */ isPoppingSimpleEnum(int offset, int stackEntryIndex)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 */ isPoppingSimpleEnumType(int offset, int stackEntryIndex)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 */ isPoppingSimpleEnumArray(int offset, int stackEntryIndex)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 */ isSimpleEnum(Clazz clazz)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 */ replaceSupportedMethod(Clazz clazz, int offset, Instruction instruction, String name, String type)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 */ replaceInstructions(Clazz clazz, int offset, Instruction instruction, Instruction[] replacementInstructions)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 */ replaceInstruction(Clazz clazz, int offset, Instruction instruction, Instruction replacementInstruction)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 */ deleteInstruction(Clazz clazz, int offset, Instruction instruction)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 */ insertPopInstructions(int offset, int popCount)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 */ replaceNullStackEntryProducers(Clazz clazz, Method method, CodeAttribute codeAttribute, int consumerOffset)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 */ replaceNullStackEntryProducers(Clazz clazz, Method method, CodeAttribute codeAttribute, int consumerOffset, int stackEntryIndex)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 */ replaceNullVariableProducers(Clazz clazz, Method method, CodeAttribute codeAttribute, int consumerOffset, int variableIndex)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