EvaluationShrinker.java revision cfead78069f3dc32998dc118ee08cab3867acea2
1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2011 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.RefConstant; 27import proguard.classfile.constant.visitor.ConstantVisitor; 28import proguard.classfile.editor.CodeAttributeEditor; 29import proguard.classfile.instruction.*; 30import proguard.classfile.instruction.visitor.InstructionVisitor; 31import proguard.classfile.util.*; 32import proguard.classfile.visitor.*; 33import proguard.evaluation.*; 34import proguard.evaluation.value.*; 35import proguard.optimize.info.*; 36 37import java.util.Arrays; 38 39/** 40 * This AttributeVisitor simplifies the code attributes that it visits, based 41 * on partial evaluation. 42 * 43 * @author Eric Lafortune 44 */ 45public class EvaluationShrinker 46extends SimplifiedVisitor 47implements AttributeVisitor 48{ 49 //* 50 private static final boolean DEBUG_RESULTS = false; 51 private static final boolean DEBUG = false; 52 /*/ 53 private static boolean DEBUG_RESULTS = true; 54 private static boolean DEBUG = true; 55 //*/ 56 57 private final InstructionVisitor extraDeletedInstructionVisitor; 58 private final InstructionVisitor extraAddedInstructionVisitor; 59 60 private final PartialEvaluator partialEvaluator; 61 private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator(); 62 private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true); 63 private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier(); 64 private final MyProducerMarker producerMarker = new MyProducerMarker(); 65 private final MyVariableInitializationMarker variableInitializationMarker = new MyVariableInitializationMarker(); 66 private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer(); 67 private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false); 68 69 private boolean[][] variablesNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_VARIABLES_SIZE]; 70 private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; 71 private boolean[][] stacksSimplifiedBefore = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; 72 private boolean[] instructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; 73 74 private int maxMarkedOffset; 75 76 77 /** 78 * Creates a new EvaluationShrinker. 79 */ 80 public EvaluationShrinker() 81 { 82 this(new PartialEvaluator(), null, null); 83 } 84 85 86 /** 87 * Creates a new EvaluationShrinker. 88 * @param partialEvaluator the partial evaluator that will 89 * execute the code and provide 90 * information about the results. 91 * @param extraDeletedInstructionVisitor an optional extra visitor for all 92 * deleted instructions. 93 * @param extraAddedInstructionVisitor an optional extra visitor for all 94 * added instructions. 95 */ 96 public EvaluationShrinker(PartialEvaluator partialEvaluator, 97 InstructionVisitor extraDeletedInstructionVisitor, 98 InstructionVisitor extraAddedInstructionVisitor) 99 { 100 this.partialEvaluator = partialEvaluator; 101 this.extraDeletedInstructionVisitor = extraDeletedInstructionVisitor; 102 this.extraAddedInstructionVisitor = extraAddedInstructionVisitor; 103 } 104 105 106 // Implementations for AttributeVisitor. 107 108 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 109 110 111 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 112 { 113// DEBUG = DEBUG_RESULTS = 114// clazz.getName().equals("abc/Def") && 115// method.getName(clazz).equals("abc"); 116 117 // TODO: Remove this when the evaluation shrinker has stabilized. 118 // Catch any unexpected exceptions from the actual visiting method. 119 try 120 { 121 // Process the code. 122 visitCodeAttribute0(clazz, method, codeAttribute); 123 } 124 catch (RuntimeException ex) 125 { 126 System.err.println("Unexpected error while shrinking instructions after partial evaluation:"); 127 System.err.println(" Class = ["+clazz.getName()+"]"); 128 System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); 129 System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); 130 System.err.println("Not optimizing this method"); 131 132 if (DEBUG) 133 { 134 method.accept(clazz, new ClassPrinter()); 135 136 throw ex; 137 } 138 } 139 } 140 141 142 public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) 143 { 144 if (DEBUG_RESULTS) 145 { 146 System.out.println(); 147 System.out.println("Class "+ClassUtil.externalClassName(clazz.getName())); 148 System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), 149 0, 150 method.getName(clazz), 151 method.getDescriptor(clazz))); 152 } 153 154 // Initialize the necessary array. 155 initializeNecessary(codeAttribute); 156 157 // Evaluate the method. 158 partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 159 160 int codeLength = codeAttribute.u4codeLength; 161 162 // Reset the code changes. 163 codeAttributeEditor.reset(codeLength); 164 165 // Mark any unused method parameters on the stack. 166 if (DEBUG) System.out.println("Invocation simplification:"); 167 168 for (int offset = 0; offset < codeLength; offset++) 169 { 170 if (partialEvaluator.isTraced(offset)) 171 { 172 Instruction instruction = InstructionFactory.create(codeAttribute.code, 173 offset); 174 175 instruction.accept(clazz, method, codeAttribute, offset, unusedParameterSimplifier); 176 } 177 } 178 179 // Mark all essential instructions that have been encountered as used. 180 if (DEBUG) System.out.println("Usage initialization: "); 181 182 maxMarkedOffset = -1; 183 184 // The invocation of the "super" or "this" <init> method inside a 185 // constructor is always necessary. 186 int superInitializationOffset = partialEvaluator.superInitializationOffset(); 187 if (superInitializationOffset != PartialEvaluator.NONE) 188 { 189 if (DEBUG) System.out.print("(super.<init>)"); 190 191 markInstruction(superInitializationOffset); 192 } 193 194 // Also mark infinite loops and instructions that cause side effects. 195 for (int offset = 0; offset < codeLength; offset++) 196 { 197 if (partialEvaluator.isTraced(offset)) 198 { 199 Instruction instruction = InstructionFactory.create(codeAttribute.code, 200 offset); 201 202 // Mark that the instruction is necessary if it is an infinite loop. 203 if (instruction.opcode == InstructionConstants.OP_GOTO && 204 ((BranchInstruction)instruction).branchOffset == 0) 205 { 206 if (DEBUG) System.out.print("(infinite loop)"); 207 markInstruction(offset); 208 } 209 210 // Mark that the instruction is necessary if it has side effects. 211 else if (sideEffectInstructionChecker.hasSideEffects(clazz, 212 method, 213 codeAttribute, 214 offset, 215 instruction)) 216 { 217 markInstruction(offset); 218 } 219 } 220 } 221 if (DEBUG) System.out.println(); 222 223 224 // Globally mark instructions and their produced variables and stack 225 // entries on which necessary instructions depend. 226 // Instead of doing this recursively, we loop across all instructions, 227 // starting at the highest previously unmarked instruction that has 228 // been been marked. 229 if (DEBUG) System.out.println("Usage marking:"); 230 231 while (maxMarkedOffset >= 0) 232 { 233 int offset = maxMarkedOffset; 234 235 maxMarkedOffset = offset - 1; 236 237 if (partialEvaluator.isTraced(offset)) 238 { 239 if (isInstructionNecessary(offset)) 240 { 241 Instruction instruction = InstructionFactory.create(codeAttribute.code, 242 offset); 243 244 instruction.accept(clazz, method, codeAttribute, offset, producerMarker); 245 } 246 247 // Check if this instruction is a branch origin from a branch 248 // that straddles some marked code. 249 markStraddlingBranches(offset, 250 partialEvaluator.branchTargets(offset), 251 true); 252 253 // Check if this instruction is a branch target from a branch 254 // that straddles some marked code. 255 markStraddlingBranches(offset, 256 partialEvaluator.branchOrigins(offset), 257 false); 258 } 259 260 if (DEBUG) 261 { 262 if (maxMarkedOffset > offset) 263 { 264 System.out.println(" -> "+maxMarkedOffset); 265 } 266 } 267 } 268 if (DEBUG) System.out.println(); 269 270 271 // Mark variable initializations, even if they aren't strictly necessary. 272 // The virtual machine's verification step is not smart enough to see 273 // this, and may complain otherwise. 274 if (DEBUG) System.out.println("Initialization marking: "); 275 276 for (int offset = 0; offset < codeLength; offset++) 277 { 278 // Is it a variable initialization that hasn't been marked yet? 279 if (partialEvaluator.isTraced(offset) && 280 !isInstructionNecessary(offset)) 281 { 282 Instruction instruction = InstructionFactory.create(codeAttribute.code, 283 offset); 284 285 instruction.accept(clazz, method, codeAttribute, offset, variableInitializationMarker); 286 } 287 } 288 if (DEBUG) System.out.println(); 289 290 291 // Locally fix instructions, in order to keep the stack consistent. 292 if (DEBUG) System.out.println("Stack consistency fixing:"); 293 294 maxMarkedOffset = codeLength - 1; 295 296 while (maxMarkedOffset >= 0) 297 { 298 int offset = maxMarkedOffset; 299 300 maxMarkedOffset = offset - 1; 301 302 if (partialEvaluator.isTraced(offset)) 303 { 304 Instruction instruction = InstructionFactory.create(codeAttribute.code, 305 offset); 306 307 instruction.accept(clazz, method, codeAttribute, offset, stackConsistencyFixer); 308 309 // Check if this instruction is a branch origin from a branch 310 // that straddles some marked code. 311 markStraddlingBranches(offset, 312 partialEvaluator.branchTargets(offset), 313 true); 314 315 // Check if this instruction is a branch target from a branch 316 // that straddles some marked code. 317 markStraddlingBranches(offset, 318 partialEvaluator.branchOrigins(offset), 319 false); 320 } 321 } 322 if (DEBUG) System.out.println(); 323 324 325 // Replace traced but unmarked backward branches by infinite loops. 326 // The virtual machine's verification step is not smart enough to see 327 // the code isn't reachable, and may complain otherwise. 328 // Any clearly unreachable code will still be removed elsewhere. 329 if (DEBUG) System.out.println("Infinite loop fixing:"); 330 331 for (int offset = 0; offset < codeLength; offset++) 332 { 333 // Is it a traced but unmarked backward branch, without an unmarked 334 // straddling forward branch? Note that this is still a heuristic. 335 if (partialEvaluator.isTraced(offset) && 336 !isInstructionNecessary(offset) && 337 isAllSmallerThanOrEqual(partialEvaluator.branchTargets(offset), 338 offset) && 339 !isAnyUnnecessaryInstructionBranchingOver(lastNecessaryInstructionOffset(offset), 340 offset)) 341 { 342 replaceByInfiniteLoop(clazz, offset); 343 } 344 } 345 if (DEBUG) System.out.println(); 346 347 348 // Insert infinite loops after jumps to subroutines that don't return. 349 // The virtual machine's verification step is not smart enough to see 350 // the code isn't reachable, and may complain otherwise. 351 if (DEBUG) System.out.println("Non-returning subroutine fixing:"); 352 353 for (int offset = 0; offset < codeLength; offset++) 354 { 355 // Is it a traced but unmarked backward branch, without an unmarked 356 // straddling forward branch? Note that this is still a heuristic. 357 if (isInstructionNecessary(offset) && 358 partialEvaluator.isSubroutineInvocation(offset)) 359 { 360 Instruction instruction = InstructionFactory.create(codeAttribute.code, 361 offset); 362 363 int nextOffset = offset + instruction.length(offset); 364 if (!isInstructionNecessary(nextOffset)) 365 { 366 replaceByInfiniteLoop(clazz, nextOffset); 367 } 368 } 369 } 370 if (DEBUG) System.out.println(); 371 372 373 // Delete all instructions that are not used. 374 int offset = 0; 375 do 376 { 377 Instruction instruction = InstructionFactory.create(codeAttribute.code, 378 offset); 379 if (!isInstructionNecessary(offset)) 380 { 381 codeAttributeEditor.deleteInstruction(offset); 382 383 codeAttributeEditor.insertBeforeInstruction(offset, (Instruction)null); 384 codeAttributeEditor.replaceInstruction(offset, (Instruction)null); 385 codeAttributeEditor.insertAfterInstruction(offset, (Instruction)null); 386 387 // Visit the instruction, if required. 388 if (extraDeletedInstructionVisitor != null) 389 { 390 instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor); 391 } 392 } 393 394 offset += instruction.length(offset); 395 } 396 while (offset < codeLength); 397 398 399 if (DEBUG_RESULTS) 400 { 401 System.out.println("Simplification results:"); 402 403 offset = 0; 404 do 405 { 406 Instruction instruction = InstructionFactory.create(codeAttribute.code, 407 offset); 408 System.out.println((isInstructionNecessary(offset) ? " + " : " - ")+instruction.toString(offset)); 409 410 if (partialEvaluator.isTraced(offset)) 411 { 412 int initializationOffset = partialEvaluator.initializationOffset(offset); 413 if (initializationOffset != PartialEvaluator.NONE) 414 { 415 System.out.println(" is to be initialized at ["+initializationOffset+"]"); 416 } 417 418 InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); 419 if (branchTargets != null) 420 { 421 System.out.println(" has overall been branching to "+branchTargets); 422 } 423 424 boolean deleted = codeAttributeEditor.deleted[offset]; 425 if (isInstructionNecessary(offset) && deleted) 426 { 427 System.out.println(" is deleted"); 428 } 429 430 Instruction preInsertion = codeAttributeEditor.preInsertions[offset]; 431 if (preInsertion != null) 432 { 433 System.out.println(" is preceded by: "+preInsertion); 434 } 435 436 Instruction replacement = codeAttributeEditor.replacements[offset]; 437 if (replacement != null) 438 { 439 System.out.println(" is replaced by: "+replacement); 440 } 441 442 Instruction postInsertion = codeAttributeEditor.postInsertions[offset]; 443 if (postInsertion != null) 444 { 445 System.out.println(" is followed by: "+postInsertion); 446 } 447 } 448 449 offset += instruction.length(offset); 450 } 451 while (offset < codeLength); 452 } 453 454 // Apply all accumulated changes to the code. 455 codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); 456 } 457 458 459 /** 460 * This MemberVisitor marks stack entries that aren't necessary because 461 * parameters aren't used in the methods that are visited. 462 */ 463 private class MyUnusedParameterSimplifier 464 extends SimplifiedVisitor 465 implements InstructionVisitor, 466 ConstantVisitor, 467 MemberVisitor 468 { 469 private int invocationOffset; 470 private ConstantInstruction invocationInstruction; 471 472 473 // Implementations for InstructionVisitor. 474 475 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} 476 477 478 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 479 { 480 switch (constantInstruction.opcode) 481 { 482 case InstructionConstants.OP_INVOKEVIRTUAL: 483 case InstructionConstants.OP_INVOKESPECIAL: 484 case InstructionConstants.OP_INVOKESTATIC: 485 case InstructionConstants.OP_INVOKEINTERFACE: 486 this.invocationOffset = offset; 487 this.invocationInstruction = constantInstruction; 488 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); 489 break; 490 } 491 } 492 493 494 // Implementations for ConstantVisitor. 495 496 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 497 { 498 refConstant.referencedMemberAccept(this); 499 } 500 501 502 // Implementations for MemberVisitor. 503 504 public void visitAnyMember(Clazz clazz, Member member) {} 505 506 507 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 508 { 509 // Get the total size of the parameters. 510 int parameterSize = ParameterUsageMarker.getParameterSize(programMethod); 511 512 // Make the method invocation static, if possible. 513 if ((programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0 && 514 !ParameterUsageMarker.isParameterUsed(programMethod, 0)) 515 { 516 replaceByStaticInvocation(programClass, 517 invocationOffset, 518 invocationInstruction); 519 } 520 521 // Remove unused parameters. 522 for (int index = 0; index < parameterSize; index++) 523 { 524 if (!ParameterUsageMarker.isParameterUsed(programMethod, index)) 525 { 526 TracedStack stack = 527 partialEvaluator.getStackBefore(invocationOffset); 528 529 int stackIndex = stack.size() - parameterSize + index; 530 531 if (DEBUG) 532 { 533 System.out.println(" ["+invocationOffset+"] Ignoring parameter #"+index+" of "+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] (stack entry #"+stackIndex+" ["+stack.getBottom(stackIndex)+"])"); 534 System.out.println(" Full stack: "+stack); 535 } 536 537 markStackSimplificationBefore(invocationOffset, stackIndex); 538 } 539 } 540 } 541 } 542 543 544 /** 545 * This InstructionVisitor marks the producing instructions and produced 546 * variables and stack entries of the instructions that it visits. 547 * Simplified method arguments are ignored. 548 */ 549 private class MyProducerMarker 550 extends SimplifiedVisitor 551 implements InstructionVisitor 552 { 553 // Implementations for InstructionVisitor. 554 555 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) 556 { 557 markStackProducers(clazz, offset, instruction); 558 } 559 560 561 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 562 { 563 switch (simpleInstruction.opcode) 564 { 565 case InstructionConstants.OP_DUP: 566 conditionallyMarkStackEntryProducers(offset, 0, 0); 567 conditionallyMarkStackEntryProducers(offset, 1, 0); 568 break; 569 case InstructionConstants.OP_DUP_X1: 570 conditionallyMarkStackEntryProducers(offset, 0, 0); 571 conditionallyMarkStackEntryProducers(offset, 1, 1); 572 conditionallyMarkStackEntryProducers(offset, 2, 0); 573 break; 574 case InstructionConstants.OP_DUP_X2: 575 conditionallyMarkStackEntryProducers(offset, 0, 0); 576 conditionallyMarkStackEntryProducers(offset, 1, 1); 577 conditionallyMarkStackEntryProducers(offset, 2, 2); 578 conditionallyMarkStackEntryProducers(offset, 3, 0); 579 break; 580 case InstructionConstants.OP_DUP2: 581 conditionallyMarkStackEntryProducers(offset, 0, 0); 582 conditionallyMarkStackEntryProducers(offset, 1, 1); 583 conditionallyMarkStackEntryProducers(offset, 2, 0); 584 conditionallyMarkStackEntryProducers(offset, 3, 1); 585 break; 586 case InstructionConstants.OP_DUP2_X1: 587 conditionallyMarkStackEntryProducers(offset, 0, 0); 588 conditionallyMarkStackEntryProducers(offset, 1, 1); 589 conditionallyMarkStackEntryProducers(offset, 2, 2); 590 conditionallyMarkStackEntryProducers(offset, 3, 0); 591 conditionallyMarkStackEntryProducers(offset, 4, 1); 592 break; 593 case InstructionConstants.OP_DUP2_X2: 594 conditionallyMarkStackEntryProducers(offset, 0, 0); 595 conditionallyMarkStackEntryProducers(offset, 1, 1); 596 conditionallyMarkStackEntryProducers(offset, 2, 2); 597 conditionallyMarkStackEntryProducers(offset, 3, 3); 598 conditionallyMarkStackEntryProducers(offset, 4, 0); 599 conditionallyMarkStackEntryProducers(offset, 5, 1); 600 break; 601 case InstructionConstants.OP_SWAP: 602 conditionallyMarkStackEntryProducers(offset, 0, 1); 603 conditionallyMarkStackEntryProducers(offset, 1, 0); 604 break; 605 default: 606 markStackProducers(clazz, offset, simpleInstruction); 607 break; 608 } 609 } 610 611 612 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 613 { 614 // Is the variable being loaded (or incremented)? 615 if (variableInstruction.opcode < InstructionConstants.OP_ISTORE) 616 { 617 markVariableProducers(offset, variableInstruction.variableIndex); 618 } 619 else 620 { 621 markStackProducers(clazz, offset, variableInstruction); 622 } 623 } 624 625 626 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 627 { 628 // Mark the initializer invocation, if this is a 'new' instruction. 629 if (constantInstruction.opcode == InstructionConstants.OP_NEW) 630 { 631 markInitialization(offset); 632 } 633 634 markStackProducers(clazz, offset, constantInstruction); 635 } 636 637 638 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 639 { 640 // Explicitly mark the produced stack entry of a 'jsr' instruction, 641 // because the consuming 'astore' instruction of the subroutine is 642 // cleared every time it is traced. 643 if (branchInstruction.opcode == InstructionConstants.OP_JSR || 644 branchInstruction.opcode == InstructionConstants.OP_JSR_W) 645 { 646 markStackEntryAfter(offset, 0); 647 } 648 else 649 { 650 markStackProducers(clazz, offset, branchInstruction); 651 } 652 } 653 } 654 655 656 /** 657 * This InstructionVisitor marks variable initializations that are 658 * necessary to appease the JVM. 659 */ 660 private class MyVariableInitializationMarker 661 extends SimplifiedVisitor 662 implements InstructionVisitor 663 { 664 // Implementations for InstructionVisitor. 665 666 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} 667 668 669 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 670 { 671 if (!variableInstruction.isLoad()) 672 { 673 int variableIndex = variableInstruction.variableIndex; 674 675 if (isVariableInitialization(offset, 676 variableIndex) && 677 isVariableInitializationNecessary(clazz, 678 method, 679 codeAttribute, 680 offset, 681 variableIndex)) 682 { 683 markInstruction(offset); 684 } 685 } 686 } 687 } 688 689 690 /** 691 * This InstructionVisitor fixes instructions locally, popping any unused 692 * produced stack entries after marked instructions, and popping produced 693 * stack entries and pushing missing stack entries instead of unmarked 694 * instructions. 695 */ 696 private class MyStackConsistencyFixer 697 extends SimplifiedVisitor 698 implements InstructionVisitor 699 { 700 // Implementations for InstructionVisitor. 701 702 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) 703 { 704 // Has the instruction been marked? 705 if (isInstructionNecessary(offset)) 706 { 707 // Check all stack entries that are popped. 708 // Typical case: a freshly marked variable initialization that 709 // requires some value on the stack. 710 int popCount = instruction.stackPopCount(clazz); 711 if (popCount > 0) 712 { 713 TracedStack tracedStack = 714 partialEvaluator.getStackBefore(offset); 715 716 int top = tracedStack.size() - 1; 717 718 int requiredPushCount = 0; 719 for (int stackIndex = 0; stackIndex < popCount; stackIndex++) 720 { 721 InstructionOffsetValue producerOffsets = 722 tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(); 723 724 if (!isStackSimplifiedBefore(offset, top - stackIndex)) 725 { 726 // Is the stack entry pushed by any producer, 727 // because it is required by other consumers? 728 if (isAnyStackEntryNecessaryAfter(producerOffsets, top - stackIndex)) 729 { 730 // Make sure it is pushed after all producers. 731 markStackEntriesAfter(producerOffsets, top - stackIndex); 732 } 733 else 734 { 735 // Remember to push it. 736 requiredPushCount++; 737 } 738 } 739 } 740 741 // Push some necessary stack entries. 742 if (requiredPushCount > 0) 743 { 744 if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset)); 745 746 if (requiredPushCount > (instruction.isCategory2() ? 2 : 1)) 747 { 748 throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"]"); 749 } 750 751 insertPushInstructions(offset, false, tracedStack.getTop(0).computationalType()); 752 } 753 } 754 755 // Check all stack entries that are pushed. 756 // Typical case: a return value that wasn't really required and 757 // that should be popped. 758 int pushCount = instruction.stackPushCount(clazz); 759 if (pushCount > 0) 760 { 761 TracedStack tracedStack = 762 partialEvaluator.getStackAfter(offset); 763 764 int top = tracedStack.size() - 1; 765 766 int requiredPopCount = 0; 767 for (int stackIndex = 0; stackIndex < pushCount; stackIndex++) 768 { 769 // Is the stack entry required by consumers? 770 if (!isStackEntryNecessaryAfter(offset, top - stackIndex)) 771 { 772 // Remember to pop it. 773 requiredPopCount++; 774 } 775 } 776 777 // Pop the unnecessary stack entries. 778 if (requiredPopCount > 0) 779 { 780 if (DEBUG) System.out.println(" Inserting after marked producer "+instruction.toString(offset)); 781 782 insertPopInstructions(offset, false, requiredPopCount); 783 } 784 } 785 } 786 else 787 { 788 // Check all stack entries that would be popped. 789 // Typical case: a stack value that is required elsewhere and 790 // that still has to be popped. 791 int popCount = instruction.stackPopCount(clazz); 792 if (popCount > 0) 793 { 794 TracedStack tracedStack = 795 partialEvaluator.getStackBefore(offset); 796 797 int top = tracedStack.size() - 1; 798 799 int expectedPopCount = 0; 800 for (int stackIndex = 0; stackIndex < popCount; stackIndex++) 801 { 802 InstructionOffsetValue producerOffsets = 803 tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(); 804 805 // Is the stack entry pushed by any producer, 806 // because it is required by other consumers? 807 if (isAnyStackEntryNecessaryAfter(producerOffsets, top - stackIndex)) 808 { 809 // Make sure it is pushed after all producers. 810 markStackEntriesAfter(producerOffsets, top - stackIndex); 811 812 // Remember to pop it. 813 expectedPopCount++; 814 } 815 } 816 817 // Pop the unnecessary stack entries. 818 if (expectedPopCount > 0) 819 { 820 if (DEBUG) System.out.println(" Replacing unmarked consumer "+instruction.toString(offset)); 821 822 insertPopInstructions(offset, true, expectedPopCount); 823 } 824 } 825 826 // Check all stack entries that would be pushed. 827 // Typical case: never. 828 int pushCount = instruction.stackPushCount(clazz); 829 if (pushCount > 0) 830 { 831 TracedStack tracedStack = 832 partialEvaluator.getStackAfter(offset); 833 834 int top = tracedStack.size() - 1; 835 836 int expectedPushCount = 0; 837 for (int stackIndex = 0; stackIndex < pushCount; stackIndex++) 838 { 839 // Is the stack entry required by consumers? 840 if (isStackEntryNecessaryAfter(offset, top - stackIndex)) 841 { 842 // Remember to push it. 843 expectedPushCount++; 844 } 845 } 846 847 // Push some necessary stack entries. 848 if (expectedPushCount > 0) 849 { 850 if (DEBUG) System.out.println(" Replacing unmarked producer "+instruction.toString(offset)); 851 852 insertPushInstructions(offset, true, tracedStack.getTop(0).computationalType()); 853 } 854 } 855 } 856 } 857 858 859 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 860 { 861 if (isInstructionNecessary(offset) && 862 isDupOrSwap(simpleInstruction)) 863 { 864 fixDupInstruction(clazz, codeAttribute, offset, simpleInstruction); 865 } 866 else 867 { 868 visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); 869 } 870 } 871 } 872 873 874 // Small utility methods. 875 876 /** 877 * Marks the variable and the corresponding producing instructions 878 * of the consumer at the given offset. 879 * @param consumerOffset the offset of the consumer. 880 * @param variableIndex the index of the variable to be marked. 881 */ 882 private void markVariableProducers(int consumerOffset, 883 int variableIndex) 884 { 885 TracedVariables tracedVariables = 886 partialEvaluator.getVariablesBefore(consumerOffset); 887 888 // Mark the producer of the loaded value. 889 markVariableProducers(tracedVariables.getProducerValue(variableIndex).instructionOffsetValue(), 890 variableIndex); 891 } 892 893 894 /** 895 * Marks the variable and its producing instructions at the given offsets. 896 * @param producerOffsets the offsets of the producers to be marked. 897 * @param variableIndex the index of the variable to be marked. 898 */ 899 private void markVariableProducers(InstructionOffsetValue producerOffsets, 900 int variableIndex) 901 { 902 if (producerOffsets != null) 903 { 904 int offsetCount = producerOffsets.instructionOffsetCount(); 905 for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) 906 { 907 // Make sure the variable and the instruction are marked 908 // at the producing offset. 909 int offset = producerOffsets.instructionOffset(offsetIndex); 910 911 markVariableAfter(offset, variableIndex); 912 markInstruction(offset); 913 } 914 } 915 } 916 917 918 /** 919 * Marks the stack entries and their producing instructions of the 920 * consumer at the given offset. 921 * @param clazz the containing class. 922 * @param consumerOffset the offset of the consumer. 923 * @param consumer the consumer of the stack entries. 924 */ 925 private void markStackProducers(Clazz clazz, 926 int consumerOffset, 927 Instruction consumer) 928 { 929 // Mark the producers of the popped values. 930 int popCount = consumer.stackPopCount(clazz); 931 for (int stackIndex = 0; stackIndex < popCount; stackIndex++) 932 { 933 markStackEntryProducers(consumerOffset, stackIndex); 934 } 935 } 936 937 938 /** 939 * Marks the stack entry and the corresponding producing instructions 940 * of the consumer at the given offset, if the stack entry of the 941 * consumer is marked. 942 * @param consumerOffset the offset of the consumer. 943 * @param consumerStackIndex the index of the stack entry to be checked 944 * (counting from the top). 945 * @param producerStackIndex the index of the stack entry to be marked 946 * (counting from the top). 947 */ 948 private void conditionallyMarkStackEntryProducers(int consumerOffset, 949 int consumerStackIndex, 950 int producerStackIndex) 951 { 952 int top = partialEvaluator.getStackAfter(consumerOffset).size() - 1; 953 954 if (isStackEntryNecessaryAfter(consumerOffset, top - consumerStackIndex)) 955 { 956 markStackEntryProducers(consumerOffset, producerStackIndex); 957 } 958 } 959 960 961 /** 962 * Marks the stack entry and the corresponding producing instructions 963 * of the consumer at the given offset. 964 * @param consumerOffset the offset of the consumer. 965 * @param stackIndex the index of the stack entry to be marked 966 * (counting from the top). 967 */ 968 private void markStackEntryProducers(int consumerOffset, 969 int stackIndex) 970 { 971 TracedStack tracedStack = 972 partialEvaluator.getStackBefore(consumerOffset); 973 974 int stackBottomIndex = tracedStack.size() - 1 - stackIndex; 975 976 if (!isStackSimplifiedBefore(consumerOffset, stackBottomIndex)) 977 { 978 markStackEntryProducers(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), 979 stackBottomIndex); 980 } 981 } 982 983 984 /** 985 * Marks the stack entry and its producing instructions at the given 986 * offsets. 987 * @param producerOffsets the offsets of the producers to be marked. 988 * @param stackIndex the index of the stack entry to be marked 989 * (counting from the bottom). 990 */ 991 private void markStackEntryProducers(InstructionOffsetValue producerOffsets, 992 int stackIndex) 993 { 994 if (producerOffsets != null) 995 { 996 int offsetCount = producerOffsets.instructionOffsetCount(); 997 for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) 998 { 999 // Make sure the stack entry and the instruction are marked 1000 // at the producing offset. 1001 int offset = producerOffsets.instructionOffset(offsetIndex); 1002 1003 markStackEntryAfter(offset, stackIndex); 1004 markInstruction(offset); 1005 } 1006 } 1007 } 1008 1009 1010 /** 1011 * Marks the stack entry and its initializing instruction 1012 * ('invokespecial *.<init>') for the given 'new' instruction offset. 1013 * @param newInstructionOffset the offset of the 'new' instruction. 1014 */ 1015 private void markInitialization(int newInstructionOffset) 1016 { 1017 int initializationOffset = 1018 partialEvaluator.initializationOffset(newInstructionOffset); 1019 1020 TracedStack tracedStack = 1021 partialEvaluator.getStackAfter(newInstructionOffset); 1022 1023 markStackEntryAfter(initializationOffset, tracedStack.size() - 1); 1024 markInstruction(initializationOffset); 1025 } 1026 1027 1028 /** 1029 * Marks the branch instructions of straddling branches, if they straddle 1030 * some code that has been marked. 1031 * @param instructionOffset the offset of the branch origin or branch target. 1032 * @param branchOffsets the offsets of the straddling branch targets 1033 * or branch origins. 1034 * @param isPointingToTargets <code>true</code> if the above offsets are 1035 * branch targets, <code>false</code> if they 1036 * are branch origins. 1037 */ 1038 private void markStraddlingBranches(int instructionOffset, 1039 InstructionOffsetValue branchOffsets, 1040 boolean isPointingToTargets) 1041 { 1042 if (branchOffsets != null) 1043 { 1044 // Loop over all branch offsets. 1045 int branchCount = branchOffsets.instructionOffsetCount(); 1046 for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) 1047 { 1048 // Is the branch straddling forward any necessary instructions? 1049 int branchOffset = branchOffsets.instructionOffset(branchIndex); 1050 1051 // Is the offset pointing to a branch origin or to a branch target? 1052 if (isPointingToTargets) 1053 { 1054 markStraddlingBranch(instructionOffset, 1055 branchOffset, 1056 instructionOffset, 1057 branchOffset); 1058 } 1059 else 1060 { 1061 markStraddlingBranch(instructionOffset, 1062 branchOffset, 1063 branchOffset, 1064 instructionOffset); 1065 } 1066 } 1067 } 1068 } 1069 1070 1071 private void markStraddlingBranch(int instructionOffsetStart, 1072 int instructionOffsetEnd, 1073 int branchOrigin, 1074 int branchTarget) 1075 { 1076 if (!isInstructionNecessary(branchOrigin) && 1077 isAnyInstructionNecessary(instructionOffsetStart, instructionOffsetEnd)) 1078 { 1079 if (DEBUG) System.out.print("["+branchOrigin+"->"+branchTarget+"]"); 1080 1081 // Mark the branch instruction. 1082 markInstruction(branchOrigin); 1083 } 1084 } 1085 1086 1087 /** 1088 * Marks the specified instruction if it is a required dup/swap instruction, 1089 * replacing it by an appropriate variant if necessary. 1090 * @param clazz the class that is being checked. 1091 * @param codeAttribute the code that is being checked. 1092 * @param dupOffset the offset of the dup/swap instruction. 1093 * @param instruction the dup/swap instruction. 1094 */ 1095 private void fixDupInstruction(Clazz clazz, 1096 CodeAttribute codeAttribute, 1097 int dupOffset, 1098 Instruction instruction) 1099 { 1100 int top = partialEvaluator.getStackAfter(dupOffset).size() - 1; 1101 1102 byte oldOpcode = instruction.opcode; 1103 byte newOpcode = 0; 1104 byte addOpcode = 0; 1105 1106 // Simplify the popping instruction if possible. 1107 switch (oldOpcode) 1108 { 1109 case InstructionConstants.OP_DUP: 1110 { 1111 boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); 1112 boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); 1113 1114 // Should either the original element or the copy be present? 1115 if (stackEntryPresent0 || 1116 stackEntryPresent1) 1117 { 1118 // Should both the original element and the copy be present? 1119 if (stackEntryPresent0 && 1120 stackEntryPresent1) 1121 { 1122 newOpcode = InstructionConstants.OP_DUP; 1123 } 1124 } 1125 break; 1126 } 1127 case InstructionConstants.OP_DUP_X1: 1128 { 1129 boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); 1130 boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); 1131 boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); 1132 1133 // Should either the original element or the copy be present? 1134 if (stackEntryPresent0 || 1135 stackEntryPresent2) 1136 { 1137 // Should the copy be present? 1138 if (stackEntryPresent2) 1139 { 1140 // Compute the number of elements to be skipped. 1141 int skipCount = stackEntryPresent1 ? 1 : 0; 1142 1143 // Should the original element be present? 1144 if (stackEntryPresent0) 1145 { 1146 // Copy the original element. 1147 newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount); 1148 } 1149 else if (skipCount == 1) 1150 { 1151 // Move the original element. 1152 newOpcode = InstructionConstants.OP_SWAP; 1153 } 1154 } 1155 } 1156 break; 1157 } 1158 case InstructionConstants.OP_DUP_X2: 1159 { 1160 boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); 1161 boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); 1162 boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); 1163 boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3); 1164 1165 // Should either the original element or the copy be present? 1166 if (stackEntryPresent0 || 1167 stackEntryPresent3) 1168 { 1169 // Should the copy be present? 1170 if (stackEntryPresent3) 1171 { 1172 int skipCount = (stackEntryPresent1 ? 1 : 0) + 1173 (stackEntryPresent2 ? 1 : 0); 1174 1175 // Should the original element be present? 1176 if (stackEntryPresent0) 1177 { 1178 // Copy the original element. 1179 newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount); 1180 } 1181 else if (skipCount == 1) 1182 { 1183 // Move the original element. 1184 newOpcode = InstructionConstants.OP_SWAP; 1185 } 1186 else if (skipCount == 2) 1187 { 1188 // We can't easily move the original element. 1189 throw new UnsupportedOperationException("Can't handle dup_x2 instruction moving original element across two elements at ["+dupOffset +"]"); 1190 } 1191 } 1192 } 1193 break; 1194 } 1195 case InstructionConstants.OP_DUP2: 1196 { 1197 boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); 1198 boolean stackEntriesPresent23 = isStackEntriesNecessaryAfter(dupOffset, top - 2, top - 3); 1199 1200 // Should either the original element or the copy be present? 1201 if (stackEntriesPresent01 || 1202 stackEntriesPresent23) 1203 { 1204 // Should both the original element and the copy be present? 1205 if (stackEntriesPresent01 && 1206 stackEntriesPresent23) 1207 { 1208 newOpcode = InstructionConstants.OP_DUP2; 1209 } 1210 } 1211 break; 1212 } 1213 case InstructionConstants.OP_DUP2_X1: 1214 { 1215 boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); 1216 boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); 1217 boolean stackEntriesPresent34 = isStackEntriesNecessaryAfter(dupOffset, top - 3, top - 4); 1218 1219 // Should either the original element or the copy be present? 1220 if (stackEntriesPresent01 || 1221 stackEntriesPresent34) 1222 { 1223 // Should the copy be present? 1224 if (stackEntriesPresent34) 1225 { 1226 int skipCount = stackEntryPresent2 ? 1 : 0; 1227 1228 // Should the original element be present? 1229 if (stackEntriesPresent01) 1230 { 1231 // Copy the original element. 1232 newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount); 1233 } 1234 else if (skipCount > 0) 1235 { 1236 // We can't easily move the original element. 1237 throw new UnsupportedOperationException("Can't handle dup2_x1 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]"); 1238 } 1239 } 1240 } 1241 break; 1242 } 1243 case InstructionConstants.OP_DUP2_X2: 1244 { 1245 boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); 1246 boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); 1247 boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3); 1248 boolean stackEntriesPresent45 = isStackEntriesNecessaryAfter(dupOffset, top - 4, top - 5); 1249 1250 // Should either the original element or the copy be present? 1251 if (stackEntriesPresent01 || 1252 stackEntriesPresent45) 1253 { 1254 // Should the copy be present? 1255 if (stackEntriesPresent45) 1256 { 1257 int skipCount = (stackEntryPresent2 ? 1 : 0) + 1258 (stackEntryPresent3 ? 1 : 0); 1259 1260 // Should the original element be present? 1261 if (stackEntriesPresent01) 1262 { 1263 // Copy the original element. 1264 newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount); 1265 } 1266 else if (skipCount > 0) 1267 { 1268 // We can't easily move the original element. 1269 throw new UnsupportedOperationException("Can't handle dup2_x2 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]"); 1270 } 1271 } 1272 } 1273 break; 1274 } 1275 case InstructionConstants.OP_SWAP: 1276 { 1277 boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); 1278 boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); 1279 1280 // Will either element be present? 1281 if (stackEntryPresent0 || 1282 stackEntryPresent1) 1283 { 1284 // Will both elements be present? 1285 if (!stackEntryPresent1) 1286 { 1287 // Pop the original top element (later bottom element). 1288 newOpcode = InstructionConstants.OP_POP; 1289 } 1290 else if (!stackEntryPresent0) 1291 { 1292 // Swap both elements and pop the top one. 1293 newOpcode = InstructionConstants.OP_SWAP; 1294 addOpcode = InstructionConstants.OP_POP; 1295 } 1296 else 1297 { 1298 // Just swap both elements. 1299 newOpcode = InstructionConstants.OP_SWAP; 1300 } 1301 } 1302 break; 1303 } 1304 } 1305 1306 // Is there a replacement opcode? 1307 if (newOpcode == 0) 1308 { 1309 // Delete the instruction. 1310 codeAttributeEditor.deleteInstruction(dupOffset); 1311 1312 if (extraDeletedInstructionVisitor != null) 1313 { 1314 extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null); 1315 } 1316 1317 if (DEBUG) System.out.println(" Marking but deleting instruction "+instruction.toString(dupOffset)); 1318 } 1319 else if (newOpcode == oldOpcode) 1320 { 1321 // Leave the instruction unchanged. 1322 codeAttributeEditor.undeleteInstruction(dupOffset); 1323 1324 if (DEBUG) System.out.println(" Marking unchanged instruction "+instruction.toString(dupOffset)); 1325 } 1326 else 1327 { 1328 // Replace the instruction. 1329 Instruction replacementInstruction = new SimpleInstruction(newOpcode); 1330 codeAttributeEditor.replaceInstruction(dupOffset, 1331 replacementInstruction); 1332 1333 if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString()); 1334 } 1335 1336 // Is there an additional opcode? 1337 if (addOpcode != 0) 1338 { 1339 // Add the instruction. 1340 Instruction additionalInstruction = new SimpleInstruction(addOpcode); 1341 codeAttributeEditor.insertAfterInstruction(dupOffset, additionalInstruction); 1342 1343 if (extraAddedInstructionVisitor != null) 1344 { 1345 extraAddedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null); 1346 } 1347 1348 if (DEBUG) System.out.println(" Adding instruction "+additionalInstruction.toString(dupOffset)); 1349 } 1350 } 1351 1352 1353 /** 1354 * Pushes a specified type of stack entry before or at the given offset. 1355 * The instruction is marked as necessary. 1356 */ 1357 private void insertPushInstructions(int offset, 1358 boolean replace, 1359 int computationalType) 1360 { 1361 // Mark this instruction. 1362 markInstruction(offset); 1363 1364 // Create a simple push instrucion. 1365 Instruction replacementInstruction = 1366 new SimpleInstruction(pushOpcode(computationalType)); 1367 1368 if (DEBUG) System.out.println(": "+replacementInstruction.toString(offset)); 1369 1370 // Replace or insert the push instruction. 1371 if (replace) 1372 { 1373 // Replace the push instruction. 1374 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 1375 } 1376 else 1377 { 1378 // Insert the push instruction. 1379 codeAttributeEditor.insertBeforeInstruction(offset, replacementInstruction); 1380 1381 if (extraAddedInstructionVisitor != null) 1382 { 1383 replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); 1384 } 1385 } 1386 } 1387 1388 1389 /** 1390 * Returns the opcode of a push instruction corresponding to the given 1391 * computational type. 1392 * @param computationalType the computational type to be pushed on the stack. 1393 */ 1394 private byte pushOpcode(int computationalType) 1395 { 1396 switch (computationalType) 1397 { 1398 case Value.TYPE_INTEGER: return InstructionConstants.OP_ICONST_0; 1399 case Value.TYPE_LONG: return InstructionConstants.OP_LCONST_0; 1400 case Value.TYPE_FLOAT: return InstructionConstants.OP_FCONST_0; 1401 case Value.TYPE_DOUBLE: return InstructionConstants.OP_DCONST_0; 1402 case Value.TYPE_REFERENCE: 1403 case Value.TYPE_INSTRUCTION_OFFSET: return InstructionConstants.OP_ACONST_NULL; 1404 } 1405 1406 throw new IllegalArgumentException("No push opcode for computational type ["+computationalType+"]"); 1407 } 1408 1409 1410 /** 1411 * Pops the given number of stack entries at or after the given offset. 1412 * The instructions are marked as necessary. 1413 */ 1414 private void insertPopInstructions(int offset, boolean replace, int popCount) 1415 { 1416 // Mark this instruction. 1417 markInstruction(offset); 1418 1419 switch (popCount) 1420 { 1421 case 1: 1422 { 1423 // Replace or insert a single pop instruction. 1424 Instruction popInstruction = 1425 new SimpleInstruction(InstructionConstants.OP_POP); 1426 1427 if (replace) 1428 { 1429 codeAttributeEditor.replaceInstruction(offset, popInstruction); 1430 } 1431 else 1432 { 1433 codeAttributeEditor.insertAfterInstruction(offset, popInstruction); 1434 1435 if (extraAddedInstructionVisitor != null) 1436 { 1437 popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); 1438 } 1439 } 1440 break; 1441 } 1442 case 2: 1443 { 1444 // Replace or insert a single pop2 instruction. 1445 Instruction popInstruction = 1446 new SimpleInstruction(InstructionConstants.OP_POP2); 1447 1448 if (replace) 1449 { 1450 codeAttributeEditor.replaceInstruction(offset, popInstruction); 1451 } 1452 else 1453 { 1454 codeAttributeEditor.insertAfterInstruction(offset, popInstruction); 1455 1456 if (extraAddedInstructionVisitor != null) 1457 { 1458 popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); 1459 } 1460 } 1461 break; 1462 } 1463 default: 1464 { 1465 // Replace or insert the specified number of pop instructions. 1466 Instruction[] popInstructions = 1467 new Instruction[popCount / 2 + popCount % 2]; 1468 1469 Instruction popInstruction = 1470 new SimpleInstruction(InstructionConstants.OP_POP2); 1471 1472 for (int index = 0; index < popCount / 2; index++) 1473 { 1474 popInstructions[index] = popInstruction; 1475 } 1476 1477 if (popCount % 2 == 1) 1478 { 1479 popInstruction = 1480 new SimpleInstruction(InstructionConstants.OP_POP); 1481 1482 popInstructions[popCount / 2] = popInstruction; 1483 } 1484 1485 if (replace) 1486 { 1487 codeAttributeEditor.replaceInstruction(offset, popInstructions); 1488 1489 for (int index = 1; index < popInstructions.length; index++) 1490 { 1491 if (extraAddedInstructionVisitor != null) 1492 { 1493 popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); 1494 } 1495 } 1496 } 1497 else 1498 { 1499 codeAttributeEditor.insertAfterInstruction(offset, popInstructions); 1500 1501 for (int index = 0; index < popInstructions.length; index++) 1502 { 1503 if (extraAddedInstructionVisitor != null) 1504 { 1505 popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); 1506 } 1507 } 1508 } 1509 break; 1510 } 1511 } 1512 } 1513 1514 1515 /** 1516 * Replaces the instruction at a given offset by a static invocation. 1517 */ 1518 private void replaceByStaticInvocation(Clazz clazz, 1519 int offset, 1520 ConstantInstruction constantInstruction) 1521 { 1522 // Remember the replacement instruction. 1523 Instruction replacementInstruction = 1524 new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 1525 constantInstruction.constantIndex).shrink(); 1526 1527 if (DEBUG) System.out.println(" Replacing by static invocation "+constantInstruction.toString(offset)+" -> "+replacementInstruction.toString()); 1528 1529 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 1530 } 1531 1532 1533 /** 1534 * Replaces the given instruction by an infinite loop. 1535 */ 1536 private void replaceByInfiniteLoop(Clazz clazz, 1537 int offset) 1538 { 1539 if (DEBUG) System.out.println(" Inserting infinite loop at ["+offset+"]"); 1540 1541 // Mark the instruction. 1542 markInstruction(offset); 1543 1544 // Replace the instruction by an infinite loop. 1545 Instruction replacementInstruction = 1546 new BranchInstruction(InstructionConstants.OP_GOTO, 0); 1547 1548 codeAttributeEditor.replaceInstruction(offset, replacementInstruction); 1549 } 1550 1551 1552 // Small utility methods. 1553 1554 /** 1555 * Returns whether the given instruction is a dup or swap instruction 1556 * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap). 1557 */ 1558 private boolean isDupOrSwap(Instruction instruction) 1559 { 1560 return instruction.opcode >= InstructionConstants.OP_DUP && 1561 instruction.opcode <= InstructionConstants.OP_SWAP; 1562 } 1563 1564 1565 /** 1566 * Returns whether the given instruction is a pop instruction 1567 * (pop, pop2). 1568 */ 1569 private boolean isPop(Instruction instruction) 1570 { 1571 return instruction.opcode == InstructionConstants.OP_POP || 1572 instruction.opcode == InstructionConstants.OP_POP2; 1573 } 1574 1575 1576 /** 1577 * Returns whether any traced but unnecessary instruction between the two 1578 * given offsets is branching over the second given offset. 1579 */ 1580 private boolean isAnyUnnecessaryInstructionBranchingOver(int instructionOffset1, 1581 int instructionOffset2) 1582 { 1583 for (int offset = instructionOffset1; offset < instructionOffset2; offset++) 1584 { 1585 // Is it a traced but unmarked straddling branch? 1586 if (partialEvaluator.isTraced(offset) && 1587 !isInstructionNecessary(offset) && 1588 isAnyLargerThan(partialEvaluator.branchTargets(offset), 1589 instructionOffset2)) 1590 { 1591 return true; 1592 } 1593 } 1594 1595 return false; 1596 } 1597 1598 1599 /** 1600 * Returns whether all of the given instruction offsets (at least one) 1601 * are smaller than or equal to the given offset. 1602 */ 1603 private boolean isAllSmallerThanOrEqual(InstructionOffsetValue instructionOffsets, 1604 int instructionOffset) 1605 { 1606 if (instructionOffsets != null) 1607 { 1608 // Loop over all instruction offsets. 1609 int branchCount = instructionOffsets.instructionOffsetCount(); 1610 if (branchCount > 0) 1611 { 1612 for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) 1613 { 1614 // Is the offset larger than the reference offset? 1615 if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset) 1616 { 1617 return false; 1618 } 1619 } 1620 1621 return true; 1622 } 1623 } 1624 1625 return false; 1626 } 1627 1628 1629 /** 1630 * Returns whether any of the given instruction offsets (at least one) 1631 * is larger than the given offset. 1632 */ 1633 private boolean isAnyLargerThan(InstructionOffsetValue instructionOffsets, 1634 int instructionOffset) 1635 { 1636 if (instructionOffsets != null) 1637 { 1638 // Loop over all instruction offsets. 1639 int branchCount = instructionOffsets.instructionOffsetCount(); 1640 if (branchCount > 0) 1641 { 1642 for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) 1643 { 1644 // Is the offset larger than the reference offset? 1645 if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset) 1646 { 1647 return true; 1648 } 1649 } 1650 } 1651 } 1652 1653 return false; 1654 } 1655 1656 1657 /** 1658 * Initializes the necessary data structure. 1659 */ 1660 private void initializeNecessary(CodeAttribute codeAttribute) 1661 { 1662 int codeLength = codeAttribute.u4codeLength; 1663 int maxLocals = codeAttribute.u2maxLocals; 1664 int maxStack = codeAttribute.u2maxStack; 1665 1666 // Create new arrays for storing information at each instruction offset. 1667 if (variablesNecessaryAfter.length < codeLength || 1668 variablesNecessaryAfter[0].length < maxLocals) 1669 { 1670 variablesNecessaryAfter = new boolean[codeLength][maxLocals]; 1671 } 1672 else 1673 { 1674 for (int offset = 0; offset < codeLength; offset++) 1675 { 1676 Arrays.fill(variablesNecessaryAfter[offset], 0, maxLocals, false); 1677 } 1678 } 1679 1680 if (stacksNecessaryAfter.length < codeLength || 1681 stacksNecessaryAfter[0].length < maxStack) 1682 { 1683 stacksNecessaryAfter = new boolean[codeLength][maxStack]; 1684 } 1685 else 1686 { 1687 for (int offset = 0; offset < codeLength; offset++) 1688 { 1689 Arrays.fill(stacksNecessaryAfter[offset], 0, maxStack, false); 1690 } 1691 } 1692 1693 if (stacksSimplifiedBefore.length < codeLength || 1694 stacksSimplifiedBefore[0].length < maxStack) 1695 { 1696 stacksSimplifiedBefore = new boolean[codeLength][maxStack]; 1697 } 1698 else 1699 { 1700 for (int offset = 0; offset < codeLength; offset++) 1701 { 1702 Arrays.fill(stacksSimplifiedBefore[offset], 0, maxStack, false); 1703 } 1704 } 1705 1706 if (instructionsNecessary.length < codeLength) 1707 { 1708 instructionsNecessary = new boolean[codeLength]; 1709 } 1710 else 1711 { 1712 Arrays.fill(instructionsNecessary, 0, codeLength, false); 1713 } 1714 } 1715 1716 1717 /** 1718 * Returns whether the given stack entry is present after execution of the 1719 * instruction at the given offset. 1720 */ 1721 private boolean isStackEntriesNecessaryAfter(int instructionOffset, 1722 int stackIndex1, 1723 int stackIndex2) 1724 { 1725 boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1); 1726 boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2); 1727 1728// if (present1 ^ present2) 1729// { 1730// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); 1731// } 1732 1733 return present1 || present2; 1734 } 1735 1736 1737 /** 1738 * Returns whether the specified variable is initialized at the specified 1739 * offset. 1740 */ 1741 private boolean isVariableInitialization(int instructionOffset, 1742 int variableIndex) 1743 { 1744 // Wasn't the variable set yet? 1745 Value valueBefore = partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex); 1746 if (valueBefore == null) 1747 { 1748 return true; 1749 } 1750 1751 // Is the computational type different now? 1752 Value valueAfter = partialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex); 1753 if (valueAfter.computationalType() != valueBefore.computationalType()) 1754 { 1755 return true; 1756 } 1757 1758 // Was the producer an argument (which may be removed)? 1759 Value producersBefore = partialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex); 1760 return producersBefore.instructionOffsetValue().instructionOffsetCount() == 1 && 1761 producersBefore.instructionOffsetValue().instructionOffset(0) == PartialEvaluator.AT_METHOD_ENTRY; 1762 } 1763 1764 1765 /** 1766 * Returns whether the specified variable must be initialized at the 1767 * specified offset, according to the verifier of the JVM. 1768 */ 1769 private boolean isVariableInitializationNecessary(Clazz clazz, 1770 Method method, 1771 CodeAttribute codeAttribute, 1772 int initializationOffset, 1773 int variableIndex) 1774 { 1775 int codeLength = codeAttribute.u4codeLength; 1776 1777 // Is the variable necessary anywhere at all? 1778 if (isVariableNecessaryAfterAny(0, codeLength, variableIndex)) 1779 { 1780 if (DEBUG) System.out.println("Simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]"); 1781 1782 // Lazily perform simple partial evaluation, the way the JVM 1783 // verifier would do it. 1784 simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 1785 1786 if (DEBUG) System.out.println("End of simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]"); 1787 1788 // Check if the variable is necessary elsewhere. 1789 for (int offset = 0; offset < codeLength; offset++) 1790 { 1791 if (partialEvaluator.isTraced(offset)) 1792 { 1793 Value producer = partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex); 1794 if (producer != null) 1795 { 1796 Value simpleProducer = simplePartialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex); 1797 if (simpleProducer != null) 1798 { 1799 InstructionOffsetValue producerOffsets = 1800 producer.instructionOffsetValue(); 1801 InstructionOffsetValue simpleProducerOffsets = 1802 simpleProducer.instructionOffsetValue(); 1803 1804 if (DEBUG) 1805 { 1806 System.out.println(" ["+offset+"] producers ["+producerOffsets+"], simple producers ["+simpleProducerOffsets+"]"); 1807 } 1808 1809 // Is the variable being used without all of its 1810 // immediate simple producers being marked? 1811 if (isVariableNecessaryAfterAny(producerOffsets, variableIndex) && 1812 !isVariableNecessaryAfterAll(simpleProducerOffsets, variableIndex)) 1813 { 1814 if (DEBUG) 1815 { 1816 System.out.println(" => initialization of variable v"+variableIndex+" at ["+initializationOffset+"] necessary"); 1817 } 1818 1819 // Then the initialization may be necessary. 1820 return true; 1821 } 1822 } 1823 } 1824 } 1825 } 1826 } 1827 1828 if (DEBUG) 1829 { 1830 System.out.println(" => initialization of variable v"+variableIndex+" at ["+initializationOffset+"] not necessary"); 1831 } 1832 1833 return false; 1834 } 1835 1836 1837 private void markVariableAfter(int instructionOffset, 1838 int variableIndex) 1839 { 1840 if (!isVariableNecessaryAfter(instructionOffset, variableIndex)) 1841 { 1842 if (DEBUG) System.out.print("["+instructionOffset+".v"+variableIndex+"],"); 1843 1844 variablesNecessaryAfter[instructionOffset][variableIndex] = true; 1845 1846 if (maxMarkedOffset < instructionOffset) 1847 { 1848 maxMarkedOffset = instructionOffset; 1849 } 1850 } 1851 } 1852 1853 1854 /** 1855 * Returns whether the specified variable is ever necessary after any 1856 * instructions in the specified block. 1857 */ 1858 private boolean isVariableNecessaryAfterAny(int startOffset, 1859 int endOffset, 1860 int variableIndex) 1861 { 1862 for (int offset = startOffset; offset < endOffset; offset++) 1863 { 1864 if (isVariableNecessaryAfter(offset, variableIndex)) 1865 { 1866 return true; 1867 } 1868 } 1869 1870 return false; 1871 } 1872 1873 1874 /** 1875 * Returns whether the specified variable is ever necessary after any 1876 * instructions in the specified set of instructions offsets. 1877 */ 1878 private boolean isVariableNecessaryAfterAny(InstructionOffsetValue instructionOffsetValue, 1879 int variableIndex) 1880 { 1881 int count = instructionOffsetValue.instructionOffsetCount(); 1882 1883 for (int index = 0; index < count; index++) 1884 { 1885 if (isVariableNecessaryAfter(instructionOffsetValue.instructionOffset(index), 1886 variableIndex)) 1887 { 1888 return true; 1889 } 1890 } 1891 1892 return false; 1893 } 1894 1895 1896 /** 1897 * Returns whether the specified variable is ever necessary after all 1898 * instructions in the specified set of instructions offsets. 1899 */ 1900 private boolean isVariableNecessaryAfterAll(InstructionOffsetValue instructionOffsetValue, 1901 int variableIndex) 1902 { 1903 int count = instructionOffsetValue.instructionOffsetCount(); 1904 1905 for (int index = 0; index < count; index++) 1906 { 1907 if (!isVariableNecessaryAfter(instructionOffsetValue.instructionOffset(index), 1908 variableIndex)) 1909 { 1910 return false; 1911 } 1912 } 1913 1914 return true; 1915 } 1916 1917 1918 private boolean isVariableNecessaryAfter(int instructionOffset, 1919 int variableIndex) 1920 { 1921 return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY || 1922 variablesNecessaryAfter[instructionOffset][variableIndex]; 1923 } 1924 1925 1926 /** 1927 * Marks the stack entries after the given offsets. 1928 * @param instructionOffsets the offsets of the stack entries to be marked. 1929 * @param stackIndex the index of the stack entries to be marked 1930 * (counting from the bottom). 1931 */ 1932 private void markStackEntriesAfter(InstructionOffsetValue instructionOffsets, 1933 int stackIndex) 1934 { 1935 if (instructionOffsets != null) 1936 { 1937 int offsetCount = instructionOffsets.instructionOffsetCount(); 1938 for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) 1939 { 1940 // Make sure the stack entry and the instruction are marked 1941 // at the producing offset. 1942 int offset = instructionOffsets.instructionOffset(offsetIndex); 1943 1944 markStackEntryAfter(offset, stackIndex); 1945 } 1946 } 1947 } 1948 1949 1950 /** 1951 * Marks the stack entry after the given offset. 1952 * @param instructionOffset the offset of the stack entry to be marked. 1953 * @param stackIndex the index of the stack entry to be marked 1954 * (counting from the bottom). 1955 */ 1956 private void markStackEntryAfter(int instructionOffset, 1957 int stackIndex) 1958 { 1959 if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex)) 1960 { 1961 if (DEBUG) System.out.print("["+instructionOffset+".s"+stackIndex+"],"); 1962 1963 stacksNecessaryAfter[instructionOffset][stackIndex] = true; 1964 1965 if (maxMarkedOffset < instructionOffset) 1966 { 1967 maxMarkedOffset = instructionOffset; 1968 } 1969 } 1970 } 1971 1972 1973 /** 1974 * Returns whether any of the stack entries after the given offsets are 1975 * necessary. 1976 * @param instructionOffsets the offsets of the stack entries to be checked. 1977 * @param stackIndex the index of the stack entries to be checked 1978 * (counting from the bottom). 1979 */ 1980 private boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets, 1981 int stackIndex) 1982 { 1983 int offsetCount = instructionOffsets.instructionOffsetCount(); 1984 1985 for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) 1986 { 1987 if (isStackEntryNecessaryAfter(instructionOffsets.instructionOffset(offsetIndex), stackIndex)) 1988 { 1989 return true; 1990 } 1991 } 1992 1993 return false; 1994 } 1995 1996 1997 /** 1998 * Returns whether any of the stack entries after the given offset are 1999 * necessary. 2000 * @param instructionOffset the offset of the stack entry to be checked. 2001 * @param stackIndex the index of the stack entry to be checked 2002 * (counting from the bottom). 2003 */ 2004 private boolean isStackEntryNecessaryAfter(int instructionOffset, 2005 int stackIndex) 2006 { 2007 return instructionOffset == PartialEvaluator.AT_CATCH_ENTRY || 2008 stacksNecessaryAfter[instructionOffset][stackIndex]; 2009 } 2010 2011 2012 private void markStackSimplificationBefore(int instructionOffset, 2013 int stackIndex) 2014 { 2015 stacksSimplifiedBefore[instructionOffset][stackIndex] = true; 2016 } 2017 2018 2019 private boolean isStackSimplifiedBefore(int instructionOffset, 2020 int stackIndex) 2021 { 2022 return stacksSimplifiedBefore[instructionOffset][stackIndex]; 2023 } 2024 2025 2026 private void markInstruction(int instructionOffset) 2027 { 2028 if (!isInstructionNecessary(instructionOffset)) 2029 { 2030 if (DEBUG) System.out.print(instructionOffset+","); 2031 2032 instructionsNecessary[instructionOffset] = true; 2033 2034 if (maxMarkedOffset < instructionOffset) 2035 { 2036 maxMarkedOffset = instructionOffset; 2037 } 2038 } 2039 } 2040 2041 2042 private boolean isAnyInstructionNecessary(int instructionOffset1, 2043 int instructionOffset2) 2044 { 2045 for (int instructionOffset = instructionOffset1; 2046 instructionOffset < instructionOffset2; 2047 instructionOffset++) 2048 { 2049 if (isInstructionNecessary(instructionOffset)) 2050 { 2051 return true; 2052 } 2053 } 2054 2055 return false; 2056 } 2057 2058 2059 /** 2060 * Returns the highest offset of an instruction that has been marked as 2061 * necessary, before the given offset. 2062 */ 2063 private int lastNecessaryInstructionOffset(int instructionOffset) 2064 { 2065 for (int offset = instructionOffset-1; offset >= 0; offset--) 2066 { 2067 if (isInstructionNecessary(instructionOffset)) 2068 { 2069 return offset; 2070 } 2071 } 2072 2073 return 0; 2074 } 2075 2076 2077 private boolean isInstructionNecessary(int instructionOffset) 2078 { 2079 return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY || 2080 instructionsNecessary[instructionOffset]; 2081 } 2082}