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