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