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.editor.*;
     27 import proguard.classfile.instruction.*;
     28 import proguard.classfile.instruction.visitor.InstructionVisitor;
     29 import proguard.classfile.util.*;
     30 import proguard.classfile.visitor.ClassPrinter;
     31 import proguard.evaluation.TracedVariables;
     32 import proguard.evaluation.value.*;
     33 import proguard.optimize.info.SideEffectInstructionChecker;
     34 
     35 import java.util.Arrays;
     36 
     37 /**
     38  * This AttributeVisitor simplifies the code attributes that it visits, based
     39  * on partial evaluation.
     40  *
     41  * @author Eric Lafortune
     42  */
     43 public class EvaluationSimplifier
     44 extends      SimplifiedVisitor
     45 implements   AttributeVisitor,
     46              InstructionVisitor
     47 {
     48     private static final int  POS_ZERO_FLOAT_BITS  = Float.floatToIntBits(0.0f);
     49     private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0);
     50 
     51     //*
     52     private static final boolean DEBUG = false;
     53     /*/
     54     private static       boolean DEBUG = System.getProperty("es") != null;
     55     //*/
     56 
     57     private final InstructionVisitor extraInstructionVisitor;
     58 
     59     private final PartialEvaluator             partialEvaluator;
     60     private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true);
     61     private final CodeAttributeEditor          codeAttributeEditor          = new CodeAttributeEditor(false, true);
     62 
     63 
     64     /**
     65      * Creates a new EvaluationSimplifier.
     66      */
     67     public EvaluationSimplifier()
     68     {
     69         this(new PartialEvaluator(), null);
     70     }
     71 
     72 
     73     /**
     74      * Creates a new EvaluationSimplifier.
     75      * @param partialEvaluator        the partial evaluator that will
     76      *                                execute the code and provide
     77      *                                information about the results.
     78      * @param extraInstructionVisitor an optional extra visitor for all
     79      *                                simplified instructions.
     80      */
     81     public EvaluationSimplifier(PartialEvaluator   partialEvaluator,
     82                                 InstructionVisitor extraInstructionVisitor)
     83     {
     84         this.partialEvaluator        = partialEvaluator;
     85         this.extraInstructionVisitor = extraInstructionVisitor;
     86     }
     87 
     88 
     89     // Implementations for AttributeVisitor.
     90 
     91     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
     92 
     93 
     94     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     95     {
     96 //        DEBUG =
     97 //            clazz.getName().equals("abc/Def") &&
     98 //            method.getName(clazz).equals("abc");
     99 
    100         // TODO: Remove this when the evaluation simplifier has stabilized.
    101         // Catch any unexpected exceptions from the actual visiting method.
    102         try
    103         {
    104             // Process the code.
    105             visitCodeAttribute0(clazz, method, codeAttribute);
    106         }
    107         catch (RuntimeException ex)
    108         {
    109             System.err.println("Unexpected error while simplifying instructions after partial evaluation:");
    110             System.err.println("  Class       = ["+clazz.getName()+"]");
    111             System.err.println("  Method      = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
    112             System.err.println("  Exception   = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
    113             System.err.println("Not optimizing this method");
    114 
    115             if (DEBUG)
    116             {
    117                 method.accept(clazz, new ClassPrinter());
    118 
    119                 throw ex;
    120             }
    121         }
    122     }
    123 
    124 
    125     public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
    126     {
    127         if (DEBUG)
    128         {
    129             System.out.println();
    130             System.out.println("EvaluationSimplifier ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
    131         }
    132 
    133         // Evaluate the method.
    134         partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
    135 
    136         int codeLength = codeAttribute.u4codeLength;
    137 
    138         // Reset the code changes.
    139         codeAttributeEditor.reset(codeLength);
    140 
    141         // Replace any instructions that can be simplified.
    142         for (int offset = 0; offset < codeLength; offset++)
    143         {
    144             if (partialEvaluator.isTraced(offset))
    145             {
    146                 Instruction instruction = InstructionFactory.create(codeAttribute.code,
    147                                                                     offset);
    148 
    149                 instruction.accept(clazz, method, codeAttribute, offset, this);
    150             }
    151         }
    152 
    153         // Apply all accumulated changes to the code.
    154         codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
    155     }
    156 
    157 
    158     // Implementations for InstructionVisitor.
    159 
    160     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
    161     {
    162         switch (simpleInstruction.opcode)
    163         {
    164             case InstructionConstants.OP_IALOAD:
    165             case InstructionConstants.OP_BALOAD:
    166             case InstructionConstants.OP_CALOAD:
    167             case InstructionConstants.OP_SALOAD:
    168             case InstructionConstants.OP_IADD:
    169             case InstructionConstants.OP_ISUB:
    170             case InstructionConstants.OP_IMUL:
    171             case InstructionConstants.OP_IDIV:
    172             case InstructionConstants.OP_IREM:
    173             case InstructionConstants.OP_INEG:
    174             case InstructionConstants.OP_ISHL:
    175             case InstructionConstants.OP_ISHR:
    176             case InstructionConstants.OP_IUSHR:
    177             case InstructionConstants.OP_IAND:
    178             case InstructionConstants.OP_IOR:
    179             case InstructionConstants.OP_IXOR:
    180             case InstructionConstants.OP_L2I:
    181             case InstructionConstants.OP_F2I:
    182             case InstructionConstants.OP_D2I:
    183             case InstructionConstants.OP_I2B:
    184             case InstructionConstants.OP_I2C:
    185             case InstructionConstants.OP_I2S:
    186             case InstructionConstants.OP_ARRAYLENGTH:
    187                 replaceIntegerPushInstruction(clazz, offset, simpleInstruction);
    188                 break;
    189 
    190             case InstructionConstants.OP_LALOAD:
    191             case InstructionConstants.OP_LADD:
    192             case InstructionConstants.OP_LSUB:
    193             case InstructionConstants.OP_LMUL:
    194             case InstructionConstants.OP_LDIV:
    195             case InstructionConstants.OP_LREM:
    196             case InstructionConstants.OP_LNEG:
    197             case InstructionConstants.OP_LSHL:
    198             case InstructionConstants.OP_LSHR:
    199             case InstructionConstants.OP_LUSHR:
    200             case InstructionConstants.OP_LAND:
    201             case InstructionConstants.OP_LOR:
    202             case InstructionConstants.OP_LXOR:
    203             case InstructionConstants.OP_I2L:
    204             case InstructionConstants.OP_F2L:
    205             case InstructionConstants.OP_D2L:
    206                 replaceLongPushInstruction(clazz, offset, simpleInstruction);
    207                 break;
    208 
    209             case InstructionConstants.OP_FALOAD:
    210             case InstructionConstants.OP_FADD:
    211             case InstructionConstants.OP_FSUB:
    212             case InstructionConstants.OP_FMUL:
    213             case InstructionConstants.OP_FDIV:
    214             case InstructionConstants.OP_FREM:
    215             case InstructionConstants.OP_FNEG:
    216             case InstructionConstants.OP_I2F:
    217             case InstructionConstants.OP_L2F:
    218             case InstructionConstants.OP_D2F:
    219                 replaceFloatPushInstruction(clazz, offset, simpleInstruction);
    220                 break;
    221 
    222             case InstructionConstants.OP_DALOAD:
    223             case InstructionConstants.OP_DADD:
    224             case InstructionConstants.OP_DSUB:
    225             case InstructionConstants.OP_DMUL:
    226             case InstructionConstants.OP_DDIV:
    227             case InstructionConstants.OP_DREM:
    228             case InstructionConstants.OP_DNEG:
    229             case InstructionConstants.OP_I2D:
    230             case InstructionConstants.OP_L2D:
    231             case InstructionConstants.OP_F2D:
    232                 replaceDoublePushInstruction(clazz, offset, simpleInstruction);
    233                 break;
    234 
    235             case InstructionConstants.OP_AALOAD:
    236                 replaceReferencePushInstruction(clazz, offset, simpleInstruction);
    237                 break;
    238         }
    239     }
    240 
    241 
    242     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
    243     {
    244         int variableIndex = variableInstruction.variableIndex;
    245 
    246         switch (variableInstruction.opcode)
    247         {
    248             case InstructionConstants.OP_ILOAD:
    249             case InstructionConstants.OP_ILOAD_0:
    250             case InstructionConstants.OP_ILOAD_1:
    251             case InstructionConstants.OP_ILOAD_2:
    252             case InstructionConstants.OP_ILOAD_3:
    253                 replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex);
    254                 break;
    255 
    256             case InstructionConstants.OP_LLOAD:
    257             case InstructionConstants.OP_LLOAD_0:
    258             case InstructionConstants.OP_LLOAD_1:
    259             case InstructionConstants.OP_LLOAD_2:
    260             case InstructionConstants.OP_LLOAD_3:
    261                 replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex);
    262                 break;
    263 
    264             case InstructionConstants.OP_FLOAD:
    265             case InstructionConstants.OP_FLOAD_0:
    266             case InstructionConstants.OP_FLOAD_1:
    267             case InstructionConstants.OP_FLOAD_2:
    268             case InstructionConstants.OP_FLOAD_3:
    269                 replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex);
    270                 break;
    271 
    272             case InstructionConstants.OP_DLOAD:
    273             case InstructionConstants.OP_DLOAD_0:
    274             case InstructionConstants.OP_DLOAD_1:
    275             case InstructionConstants.OP_DLOAD_2:
    276             case InstructionConstants.OP_DLOAD_3:
    277                 replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex);
    278                 break;
    279 
    280             case InstructionConstants.OP_ALOAD:
    281             case InstructionConstants.OP_ALOAD_0:
    282             case InstructionConstants.OP_ALOAD_1:
    283             case InstructionConstants.OP_ALOAD_2:
    284             case InstructionConstants.OP_ALOAD_3:
    285                 replaceReferencePushInstruction(clazz, offset, variableInstruction);
    286                 break;
    287 
    288             case InstructionConstants.OP_ASTORE:
    289             case InstructionConstants.OP_ASTORE_0:
    290             case InstructionConstants.OP_ASTORE_1:
    291             case InstructionConstants.OP_ASTORE_2:
    292             case InstructionConstants.OP_ASTORE_3:
    293                 deleteReferencePopInstruction(clazz, offset, variableInstruction);
    294                 break;
    295 
    296             case InstructionConstants.OP_RET:
    297                 replaceBranchInstruction(clazz, offset, variableInstruction);
    298                 break;
    299         }
    300     }
    301 
    302 
    303     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
    304     {
    305         switch (constantInstruction.opcode)
    306         {
    307             case InstructionConstants.OP_GETSTATIC:
    308             case InstructionConstants.OP_GETFIELD:
    309                 replaceAnyPushInstruction(clazz, offset, constantInstruction);
    310                 break;
    311 
    312             case InstructionConstants.OP_INVOKEVIRTUAL:
    313             case InstructionConstants.OP_INVOKESPECIAL:
    314             case InstructionConstants.OP_INVOKESTATIC:
    315             case InstructionConstants.OP_INVOKEINTERFACE:
    316                 if (constantInstruction.stackPushCount(clazz) > 0 &&
    317                     !sideEffectInstructionChecker.hasSideEffects(clazz,
    318                                                                  method,
    319                                                                  codeAttribute,
    320                                                                  offset,
    321                                                                  constantInstruction))
    322                 {
    323                     replaceAnyPushInstruction(clazz, offset, constantInstruction);
    324                 }
    325 
    326                 break;
    327 
    328             case InstructionConstants.OP_CHECKCAST:
    329                 replaceReferencePushInstruction(clazz, offset, constantInstruction);
    330                 break;
    331         }
    332     }
    333 
    334 
    335     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
    336     {
    337         switch (branchInstruction.opcode)
    338         {
    339             case InstructionConstants.OP_GOTO:
    340             case InstructionConstants.OP_GOTO_W:
    341                 // Don't replace unconditional branches.
    342                 break;
    343 
    344             case InstructionConstants.OP_JSR:
    345             case InstructionConstants.OP_JSR_W:
    346                 replaceJsrInstruction(clazz, offset, branchInstruction);
    347                 break;
    348 
    349             default:
    350                 replaceBranchInstruction(clazz, offset, branchInstruction);
    351                 break;
    352         }
    353     }
    354 
    355 
    356     public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
    357     {
    358         // First try to simplify it to a simple branch.
    359         replaceBranchInstruction(clazz, offset, tableSwitchInstruction);
    360 
    361         // Otherwise try to simplify simple enum switches.
    362         if (!codeAttributeEditor.isModified(offset))
    363         {
    364             replaceSimpleEnumSwitchInstruction(clazz,
    365                                                codeAttribute,
    366                                                offset,
    367                                                tableSwitchInstruction);
    368 
    369             // Otherwise make sure all branch targets are valid.
    370             if (!codeAttributeEditor.isModified(offset))
    371             {
    372                 cleanUpSwitchInstruction(clazz, offset, tableSwitchInstruction);
    373 
    374                 trimSwitchInstruction(clazz, offset, tableSwitchInstruction);
    375             }
    376         }
    377     }
    378 
    379 
    380     public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
    381     {
    382         // First try to simplify it to a simple branch.
    383         replaceBranchInstruction(clazz, offset, lookUpSwitchInstruction);
    384 
    385         // Otherwise try to simplify simple enum switches.
    386         if (!codeAttributeEditor.isModified(offset))
    387         {
    388             replaceSimpleEnumSwitchInstruction(clazz,
    389                                                codeAttribute,
    390                                                offset,
    391                                                lookUpSwitchInstruction);
    392 
    393             // Otherwise make sure all branch targets are valid.
    394             if (!codeAttributeEditor.isModified(offset))
    395             {
    396                 cleanUpSwitchInstruction(clazz, offset, lookUpSwitchInstruction);
    397 
    398                 trimSwitchInstruction(clazz, offset, lookUpSwitchInstruction);
    399             }
    400         }
    401     }
    402 
    403 
    404     // Small utility methods.
    405 
    406     /**
    407      * Replaces the push instruction at the given offset by a simpler push
    408      * instruction, if possible.
    409      */
    410     private void replaceAnyPushInstruction(Clazz       clazz,
    411                                            int         offset,
    412                                            Instruction instruction)
    413     {
    414         Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
    415         if (pushedValue.isParticular())
    416         {
    417             switch (pushedValue.computationalType())
    418             {
    419                 case Value.TYPE_INTEGER:
    420                     replaceIntegerPushInstruction(clazz, offset, instruction);
    421                     break;
    422                 case Value.TYPE_LONG:
    423                     replaceLongPushInstruction(clazz, offset, instruction);
    424                     break;
    425                 case Value.TYPE_FLOAT:
    426                     replaceFloatPushInstruction(clazz, offset, instruction);
    427                     break;
    428                 case Value.TYPE_DOUBLE:
    429                     replaceDoublePushInstruction(clazz, offset, instruction);
    430                     break;
    431                 case Value.TYPE_REFERENCE:
    432                     replaceReferencePushInstruction(clazz, offset, instruction);
    433                     break;
    434             }
    435         }
    436     }
    437 
    438 
    439     /**
    440      * Replaces the integer pushing instruction at the given offset by a simpler
    441      * push instruction, if possible.
    442      */
    443     private void replaceIntegerPushInstruction(Clazz       clazz,
    444                                                int         offset,
    445                                                Instruction instruction)
    446     {
    447         replaceIntegerPushInstruction(clazz,
    448                                       offset,
    449                                       instruction,
    450                                       partialEvaluator.getVariablesBefore(offset).size());
    451     }
    452 
    453 
    454     /**
    455      * Replaces the integer pushing instruction at the given offset by a simpler
    456      * push instruction, if possible.
    457      */
    458     private void replaceIntegerPushInstruction(Clazz       clazz,
    459                                                int         offset,
    460                                                Instruction instruction,
    461                                                int         maxVariableIndex)
    462     {
    463         Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
    464         if (pushedValue.isParticular())
    465         {
    466             // Push a constant instead.
    467             int value = pushedValue.integerValue().value();
    468             if ((short)value == value)
    469             {
    470                 replaceConstantPushInstruction(clazz,
    471                                                offset,
    472                                                instruction,
    473                                                InstructionConstants.OP_SIPUSH,
    474                                                value);
    475             }
    476             else
    477             {
    478                 ConstantPoolEditor constantPoolEditor =
    479                     new ConstantPoolEditor((ProgramClass)clazz);
    480 
    481                 Instruction replacementInstruction =
    482                     new ConstantInstruction(InstructionConstants.OP_LDC,
    483                                             constantPoolEditor.addIntegerConstant(value));
    484 
    485                 replaceInstruction(clazz, offset, instruction, replacementInstruction);
    486             }
    487         }
    488         else if (pushedValue.isSpecific())
    489         {
    490             // Load an equivalent lower-numbered variable instead, if any.
    491             TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
    492             for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
    493             {
    494                 if (pushedValue.equals(variables.load(variableIndex)))
    495                 {
    496                     replaceVariablePushInstruction(clazz,
    497                                                    offset,
    498                                                    instruction,
    499                                                    InstructionConstants.OP_ILOAD,
    500                                                    variableIndex);
    501                     break;
    502                 }
    503             }
    504         }
    505     }
    506 
    507 
    508     /**
    509      * Replaces the long pushing instruction at the given offset by a simpler
    510      * push instruction, if possible.
    511      */
    512     private void replaceLongPushInstruction(Clazz       clazz,
    513                                             int         offset,
    514                                             Instruction instruction)
    515     {
    516         replaceLongPushInstruction(clazz,
    517                                    offset,
    518                                    instruction,
    519                                    partialEvaluator.getVariablesBefore(offset).size());
    520     }
    521 
    522 
    523     /**
    524      * Replaces the long pushing instruction at the given offset by a simpler
    525      * push instruction, if possible.
    526      */
    527     private void replaceLongPushInstruction(Clazz       clazz,
    528                                             int         offset,
    529                                             Instruction instruction,
    530                                             int         maxVariableIndex)
    531     {
    532         Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
    533         if (pushedValue.isParticular())
    534         {
    535             // Push a constant instead.
    536             long value = pushedValue.longValue().value();
    537             if (value == 0L ||
    538                 value == 1L)
    539             {
    540                 replaceConstantPushInstruction(clazz,
    541                                        offset,
    542                                        instruction,
    543                                        InstructionConstants.OP_LCONST_0,
    544                                        (int)value);
    545             }
    546             else
    547             {
    548                 ConstantPoolEditor constantPoolEditor =
    549                     new ConstantPoolEditor((ProgramClass)clazz);
    550 
    551                 Instruction replacementInstruction =
    552                     new ConstantInstruction(InstructionConstants.OP_LDC2_W,
    553                                             constantPoolEditor.addLongConstant(value));
    554 
    555                 replaceInstruction(clazz, offset, instruction, replacementInstruction);
    556             }
    557         }
    558         else if (pushedValue.isSpecific())
    559         {
    560             // Load an equivalent lower-numbered variable instead, if any.
    561             TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
    562             for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
    563             {
    564                 // Note that we have to check the second part as well.
    565                 if (pushedValue.equals(variables.load(variableIndex)) &&
    566                     variables.load(variableIndex + 1) != null         &&
    567                     variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP)
    568                 {
    569                     replaceVariablePushInstruction(clazz,
    570                                                    offset,
    571                                                    instruction,
    572                                                    InstructionConstants.OP_LLOAD,
    573                                                    variableIndex);
    574                 }
    575             }
    576         }
    577     }
    578 
    579 
    580     /**
    581      * Replaces the float pushing instruction at the given offset by a simpler
    582      * push instruction, if possible.
    583      */
    584     private void replaceFloatPushInstruction(Clazz       clazz,
    585                                              int         offset,
    586                                              Instruction instruction)
    587     {
    588         replaceFloatPushInstruction(clazz,
    589                                     offset,
    590                                     instruction,
    591                                     partialEvaluator.getVariablesBefore(offset).size());
    592     }
    593 
    594 
    595     /**
    596      * Replaces the float pushing instruction at the given offset by a simpler
    597      * push instruction, if possible.
    598      */
    599     private void replaceFloatPushInstruction(Clazz       clazz,
    600                                              int         offset,
    601                                              Instruction instruction,
    602                                              int         maxVariableIndex)
    603     {
    604         Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
    605         if (pushedValue.isParticular())
    606         {
    607             // Push a constant instead.
    608             // Make sure to distinguish between +0.0 and -0.0.
    609             float value = pushedValue.floatValue().value();
    610             if (value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS ||
    611                 value == 1.0f ||
    612                 value == 2.0f)
    613             {
    614                 replaceConstantPushInstruction(clazz,
    615                                                offset,
    616                                                instruction,
    617                                                InstructionConstants.OP_FCONST_0,
    618                                                (int)value);
    619             }
    620             else
    621             {
    622                 ConstantPoolEditor constantPoolEditor =
    623                     new ConstantPoolEditor((ProgramClass)clazz);
    624 
    625                 Instruction replacementInstruction =
    626                     new ConstantInstruction(InstructionConstants.OP_LDC,
    627                                             constantPoolEditor.addFloatConstant(value));
    628 
    629                 replaceInstruction(clazz, offset, instruction, replacementInstruction);
    630             }
    631         }
    632         else if (pushedValue.isSpecific())
    633         {
    634             // Load an equivalent lower-numbered variable instead, if any.
    635             TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
    636             for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
    637             {
    638                 if (pushedValue.equals(variables.load(variableIndex)))
    639                 {
    640                     replaceVariablePushInstruction(clazz,
    641                                                    offset,
    642                                                    instruction,
    643                                                    InstructionConstants.OP_FLOAD,
    644                                                    variableIndex);
    645                 }
    646             }
    647         }
    648     }
    649 
    650 
    651     /**
    652      * Replaces the double pushing instruction at the given offset by a simpler
    653      * push instruction, if possible.
    654      */
    655     private void replaceDoublePushInstruction(Clazz       clazz,
    656                                               int         offset,
    657                                               Instruction instruction)
    658     {
    659         replaceDoublePushInstruction(clazz,
    660                                      offset,
    661                                      instruction,
    662                                      partialEvaluator.getVariablesBefore(offset).size());
    663     }
    664 
    665 
    666     /**
    667      * Replaces the double pushing instruction at the given offset by a simpler
    668      * push instruction, if possible.
    669      */
    670     private void replaceDoublePushInstruction(Clazz       clazz,
    671                                               int         offset,
    672                                               Instruction instruction,
    673                                               int         maxVariableIndex)
    674     {
    675         Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
    676         if (pushedValue.isParticular())
    677         {
    678             // Push a constant instead.
    679             // Make sure to distinguish between +0.0 and -0.0.
    680             double value = pushedValue.doubleValue().value();
    681             if (value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS ||
    682                 value == 1.0)
    683             {
    684                 replaceConstantPushInstruction(clazz,
    685                                                offset,
    686                                                instruction,
    687                                                InstructionConstants.OP_DCONST_0,
    688                                                (int)value);
    689             }
    690             else
    691             {
    692                 ConstantPoolEditor constantPoolEditor =
    693                     new ConstantPoolEditor((ProgramClass)clazz);
    694 
    695                 Instruction replacementInstruction =
    696                     new ConstantInstruction(InstructionConstants.OP_LDC2_W,
    697                                             constantPoolEditor.addDoubleConstant(value));
    698 
    699                 replaceInstruction(clazz, offset, instruction, replacementInstruction);
    700             }
    701         }
    702         else if (pushedValue.isSpecific())
    703         {
    704             // Load an equivalent lower-numbered variable instead, if any.
    705             TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
    706             for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
    707             {
    708                 // Note that we have to check the second part as well.
    709                 if (pushedValue.equals(variables.load(variableIndex)) &&
    710                     variables.load(variableIndex + 1) != null         &&
    711                     variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP)
    712                 {
    713                     replaceVariablePushInstruction(clazz,
    714                                                    offset,
    715                                                    instruction,
    716                                                    InstructionConstants.OP_DLOAD,
    717                                                    variableIndex);
    718                 }
    719             }
    720         }
    721     }
    722 
    723 
    724     /**
    725      * Replaces the reference pushing instruction at the given offset by a
    726      * simpler push instruction, if possible.
    727      */
    728     private void replaceReferencePushInstruction(Clazz       clazz,
    729                                                  int         offset,
    730                                                  Instruction instruction)
    731     {
    732         Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
    733         if (pushedValue.isParticular())
    734         {
    735             // A reference value can only be specific if it is null.
    736             replaceConstantPushInstruction(clazz,
    737                                            offset,
    738                                            instruction,
    739                                            InstructionConstants.OP_ACONST_NULL,
    740                                            0);
    741         }
    742     }
    743 
    744 
    745     /**
    746      * Replaces the instruction at a given offset by a given push instruction
    747      * of a constant.
    748      */
    749     private void replaceConstantPushInstruction(Clazz       clazz,
    750                                                 int         offset,
    751                                                 Instruction instruction,
    752                                                 byte        replacementOpcode,
    753                                                 int         value)
    754     {
    755         Instruction replacementInstruction =
    756             new SimpleInstruction(replacementOpcode, value);
    757 
    758         replaceInstruction(clazz, offset, instruction, replacementInstruction);
    759     }
    760 
    761 
    762     /**
    763      * Replaces the instruction at a given offset by a given push instruction
    764      * of a variable.
    765      */
    766     private void replaceVariablePushInstruction(Clazz       clazz,
    767                                                 int         offset,
    768                                                 Instruction instruction,
    769                                                 byte        replacementOpcode,
    770                                                 int         variableIndex)
    771     {
    772         Instruction replacementInstruction =
    773             new VariableInstruction(replacementOpcode, variableIndex);
    774 
    775         replaceInstruction(clazz, offset, instruction, replacementInstruction);
    776     }
    777 
    778 
    779     /**
    780      * Replaces the given 'jsr' instruction by a simpler branch instruction,
    781      * if it jumps to a subroutine that doesn't return or a subroutine that
    782      * is only called from one place.
    783      */
    784     private void replaceJsrInstruction(Clazz             clazz,
    785                                        int               offset,
    786                                        BranchInstruction branchInstruction)
    787     {
    788         // Is the subroutine ever returning?
    789         int subroutineStart = offset + branchInstruction.branchOffset;
    790         if (!partialEvaluator.isSubroutineReturning(subroutineStart) ||
    791             partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1)
    792         {
    793             // All 'jsr' instructions to this subroutine can be replaced
    794             // by unconditional branch instructions.
    795             replaceBranchInstruction(clazz, offset, branchInstruction);
    796         }
    797         else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset)))
    798         {
    799             // We have to make sure the instruction after this 'jsr'
    800             // instruction is valid, even if it is never reached.
    801             replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction);
    802         }
    803     }
    804 
    805 
    806     /**
    807      * Deletes the reference popping instruction at the given offset, if
    808      * it is at the start of a subroutine that doesn't return or a subroutine
    809      * that is only called from one place.
    810      */
    811     private void deleteReferencePopInstruction(Clazz       clazz,
    812                                                int         offset,
    813                                                Instruction instruction)
    814     {
    815         if (partialEvaluator.isSubroutineStart(offset) &&
    816             (!partialEvaluator.isSubroutineReturning(offset) ||
    817              partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1))
    818         {
    819             if (DEBUG) System.out.println("  Deleting store of subroutine return address "+instruction.toString(offset));
    820 
    821             // A reference value can only be specific if it is null.
    822             codeAttributeEditor.deleteInstruction(offset);
    823         }
    824     }
    825 
    826 
    827     /**
    828      * Deletes the given branch instruction, or replaces it by a simpler branch
    829      * instruction, if possible.
    830      */
    831     private void replaceBranchInstruction(Clazz       clazz,
    832                                           int         offset,
    833                                           Instruction instruction)
    834     {
    835         InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
    836 
    837         // Is there exactly one branch target (not from a goto or jsr)?
    838         if (branchTargets != null &&
    839             branchTargets.instructionOffsetCount() == 1)
    840         {
    841             // Is it branching to the next instruction?
    842             int branchOffset = branchTargets.instructionOffset(0) - offset;
    843             if (branchOffset == instruction.length(offset))
    844             {
    845                 if (DEBUG) System.out.println("  Ignoring zero branch instruction at ["+offset+"]");
    846             }
    847             else
    848             {
    849                 // Replace the branch instruction by a simple branch instruction.
    850                 Instruction replacementInstruction =
    851                     new BranchInstruction(InstructionConstants.OP_GOTO,
    852                                           branchOffset);
    853 
    854                 replaceInstruction(clazz, offset, instruction, replacementInstruction);
    855             }
    856         }
    857     }
    858 
    859 
    860     /**
    861      * Replaces the given table switch instruction, if it is based on the value
    862      * of a fixed array. This is typical for switches on simple enums.
    863      */
    864     private void replaceSimpleEnumSwitchInstruction(Clazz                  clazz,
    865                                                     CodeAttribute          codeAttribute,
    866                                                     int                    offset,
    867                                                     TableSwitchInstruction tableSwitchInstruction)
    868     {
    869         // Check if the switch instruction is consuming a single value loaded
    870         // from a fully specified array.
    871         InstructionOffsetValue producerOffsets =
    872             partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue();
    873 
    874         if (producerOffsets.instructionOffsetCount() == 1)
    875         {
    876             int producerOffset = producerOffsets.instructionOffset(0);
    877 
    878             if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD &&
    879                 !codeAttributeEditor.isModified(producerOffset))
    880             {
    881                 ReferenceValue referenceValue =
    882                     partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue();
    883 
    884                 if (referenceValue.isParticular())
    885                 {
    886                     // Simplify the entire construct.
    887                     replaceSimpleEnumSwitchInstruction(clazz,
    888                                                        codeAttribute,
    889                                                        producerOffset,
    890                                                        offset,
    891                                                        tableSwitchInstruction,
    892                                                        referenceValue);
    893                 }
    894             }
    895         }
    896     }
    897 
    898 
    899     /**
    900      * Replaces the given table switch instruction that is based on a value of
    901      * the given fixed array.
    902      */
    903     private void replaceSimpleEnumSwitchInstruction(Clazz                  clazz,
    904                                                     CodeAttribute          codeAttribute,
    905                                                     int                    loadOffset,
    906                                                     int                    switchOffset,
    907                                                     TableSwitchInstruction tableSwitchInstruction,
    908                                                     ReferenceValue         mappingValue)
    909     {
    910         ValueFactory valueFactory = new ParticularValueFactory();
    911 
    912         // Transform the jump offsets.
    913         int[] jumpOffsets    = tableSwitchInstruction.jumpOffsets;
    914         int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()];
    915 
    916         for (int index = 0; index < newJumpOffsets.length; index++)
    917         {
    918             int switchCase =
    919                 mappingValue.integerArrayLoad(valueFactory.createIntegerValue(
    920                     index),
    921                                               valueFactory).value();
    922 
    923             newJumpOffsets[index] =
    924                 switchCase >= tableSwitchInstruction.lowCase &&
    925                 switchCase <= tableSwitchInstruction.highCase ?
    926                     jumpOffsets[switchCase - tableSwitchInstruction.lowCase] :
    927                     tableSwitchInstruction.defaultOffset;
    928         }
    929 
    930         // Update the instruction.
    931         tableSwitchInstruction.lowCase     = 0;
    932         tableSwitchInstruction.highCase    = newJumpOffsets.length - 1;
    933         tableSwitchInstruction.jumpOffsets = newJumpOffsets;
    934 
    935         // Replace the original one with the new version.
    936         replaceSimpleEnumSwitchInstruction(clazz,
    937                                            loadOffset,
    938                                            switchOffset,
    939                                            tableSwitchInstruction);
    940 
    941         cleanUpSwitchInstruction(clazz, switchOffset, tableSwitchInstruction);
    942 
    943         trimSwitchInstruction(clazz, switchOffset, tableSwitchInstruction);
    944     }
    945 
    946 
    947     /**
    948      * Replaces the given look up switch instruction, if it is based on the
    949      * value of a fixed array. This is typical for switches on simple enums.
    950      */
    951     private void replaceSimpleEnumSwitchInstruction(Clazz                   clazz,
    952                                                     CodeAttribute           codeAttribute,
    953                                                     int                     offset,
    954                                                     LookUpSwitchInstruction lookupSwitchInstruction)
    955     {
    956         // Check if the switch instruction is consuming a single value loaded
    957         // from a fully specified array.
    958         InstructionOffsetValue producerOffsets =
    959             partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue();
    960 
    961         if (producerOffsets.instructionOffsetCount() == 1)
    962         {
    963             int producerOffset = producerOffsets.instructionOffset(0);
    964 
    965             if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD &&
    966                 !codeAttributeEditor.isModified(producerOffset))
    967             {
    968                 ReferenceValue referenceValue =
    969                     partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue();
    970 
    971                 if (referenceValue.isParticular())
    972                 {
    973                     // Simplify the entire construct.
    974                     replaceSimpleEnumSwitchInstruction(clazz,
    975                                                        codeAttribute,
    976                                                        producerOffset,
    977                                                        offset,
    978                                                        lookupSwitchInstruction,
    979                                                        referenceValue);
    980                 }
    981             }
    982         }
    983     }
    984 
    985 
    986     /**
    987      * Replaces the given look up switch instruction that is based on a value of
    988      * the given fixed array. This is typical for switches on simple enums.
    989      */
    990     private void replaceSimpleEnumSwitchInstruction(Clazz                   clazz,
    991                                                     CodeAttribute           codeAttribute,
    992                                                     int                     loadOffset,
    993                                                     int                     switchOffset,
    994                                                     LookUpSwitchInstruction lookupSwitchInstruction,
    995                                                     ReferenceValue          mappingValue)
    996     {
    997         ValueFactory valueFactory = new ParticularValueFactory();
    998 
    999         // Transform the jump offsets.
   1000         int[] cases          = lookupSwitchInstruction.cases;
   1001         int[] jumpOffsets    = lookupSwitchInstruction.jumpOffsets;
   1002         int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()];
   1003 
   1004         for (int index = 0; index < newJumpOffsets.length; index++)
   1005         {
   1006             int switchCase =
   1007                 mappingValue.integerArrayLoad(valueFactory.createIntegerValue(index),
   1008                                               valueFactory).value();
   1009 
   1010             int caseIndex = Arrays.binarySearch(cases, switchCase);
   1011 
   1012             newJumpOffsets[index] = caseIndex >= 0 ?
   1013                 jumpOffsets[caseIndex] :
   1014                 lookupSwitchInstruction.defaultOffset;
   1015         }
   1016 
   1017         // Replace the original lookup switch with a table switch.
   1018         TableSwitchInstruction replacementSwitchInstruction =
   1019             new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH,
   1020                                        lookupSwitchInstruction.defaultOffset,
   1021                                        0,
   1022                                        newJumpOffsets.length - 1,
   1023                                        newJumpOffsets);
   1024 
   1025         replaceSimpleEnumSwitchInstruction(clazz,
   1026                                            loadOffset,
   1027                                            switchOffset,
   1028                                            replacementSwitchInstruction);
   1029 
   1030         cleanUpSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction);
   1031 
   1032         trimSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction);
   1033     }
   1034 
   1035 
   1036     /**
   1037      * Makes sure all branch targets of the given switch instruction are valid.
   1038      */
   1039     private void cleanUpSwitchInstruction(Clazz             clazz,
   1040                                           int               offset,
   1041                                           SwitchInstruction switchInstruction)
   1042     {
   1043         // Get the actual branch targets.
   1044         InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
   1045 
   1046         // Get an offset that can serve as a valid default offset.
   1047         int defaultOffset =
   1048             branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) -
   1049             offset;
   1050 
   1051         Instruction replacementInstruction = null;
   1052 
   1053         // Check the jump offsets.
   1054         int[] jumpOffsets = switchInstruction.jumpOffsets;
   1055         for (int index = 0; index < jumpOffsets.length; index++)
   1056         {
   1057             if (!branchTargets.contains(offset + jumpOffsets[index]))
   1058             {
   1059                 // Replace the unused offset.
   1060                 jumpOffsets[index] = defaultOffset;
   1061 
   1062                 // Remember to replace the instruction.
   1063                 replacementInstruction = switchInstruction;
   1064             }
   1065         }
   1066 
   1067         // Check the default offset.
   1068         if (!branchTargets.contains(offset + switchInstruction.defaultOffset))
   1069         {
   1070             // Replace the unused offset.
   1071             switchInstruction.defaultOffset = defaultOffset;
   1072 
   1073             // Remember to replace the instruction.
   1074             replacementInstruction = switchInstruction;
   1075         }
   1076 
   1077         if (replacementInstruction != null)
   1078         {
   1079             replaceInstruction(clazz, offset, switchInstruction, replacementInstruction);
   1080         }
   1081     }
   1082 
   1083 
   1084     /**
   1085      * Trims redundant offsets from the given switch instruction.
   1086      */
   1087     private void trimSwitchInstruction(Clazz                  clazz,
   1088                                        int                    offset,
   1089                                        TableSwitchInstruction tableSwitchInstruction)
   1090     {
   1091         // Get an offset that can serve as a valid default offset.
   1092         int   defaultOffset = tableSwitchInstruction.defaultOffset;
   1093         int[] jumpOffsets   = tableSwitchInstruction.jumpOffsets;
   1094         int   length        = jumpOffsets.length;
   1095 
   1096         // Find the lowest index with a non-default jump offset.
   1097         int lowIndex = 0;
   1098         while (lowIndex < length &&
   1099                jumpOffsets[lowIndex] == defaultOffset)
   1100         {
   1101             lowIndex++;
   1102         }
   1103 
   1104         // Find the highest index with a non-default jump offset.
   1105         int highIndex = length - 1;
   1106         while (highIndex >= 0 &&
   1107                jumpOffsets[highIndex] == defaultOffset)
   1108         {
   1109             highIndex--;
   1110         }
   1111 
   1112         // Can we use a shorter array?
   1113         int newLength = highIndex - lowIndex + 1;
   1114         if (newLength < length)
   1115         {
   1116             if (newLength <= 0)
   1117             {
   1118                 // Replace the switch instruction by a simple branch instruction.
   1119                 Instruction replacementInstruction =
   1120                     new BranchInstruction(InstructionConstants.OP_GOTO,
   1121                                           defaultOffset);
   1122 
   1123                 replaceInstruction(clazz, offset, tableSwitchInstruction,
   1124                                    replacementInstruction);
   1125             }
   1126             else
   1127             {
   1128                 // Trim the array.
   1129                 int[] newJumpOffsets = new int[newLength];
   1130 
   1131                 System.arraycopy(jumpOffsets, lowIndex, newJumpOffsets, 0, newLength);
   1132 
   1133                 tableSwitchInstruction.jumpOffsets = newJumpOffsets;
   1134                 tableSwitchInstruction.lowCase    += lowIndex;
   1135                 tableSwitchInstruction.highCase   -= length - newLength - lowIndex;
   1136 
   1137                 replaceInstruction(clazz, offset, tableSwitchInstruction,
   1138                                    tableSwitchInstruction);
   1139             }
   1140         }
   1141     }
   1142 
   1143 
   1144     /**
   1145      * Trims redundant offsets from the given switch instruction.
   1146      */
   1147     private void trimSwitchInstruction(Clazz                   clazz,
   1148                                        int                     offset,
   1149                                        LookUpSwitchInstruction lookUpSwitchInstruction)
   1150     {
   1151         // Get an offset that can serve as a valid default offset.
   1152         int   defaultOffset = lookUpSwitchInstruction.defaultOffset;
   1153         int[] jumpOffsets   = lookUpSwitchInstruction.jumpOffsets;
   1154         int   length        = jumpOffsets.length;
   1155         int   newLength     = length;
   1156 
   1157         // Count the default jump offsets.
   1158         for (int index = 0; index < length; index++)
   1159         {
   1160             if (jumpOffsets[index] == defaultOffset)
   1161             {
   1162                 newLength--;
   1163             }
   1164         }
   1165 
   1166         // Can we use shorter arrays?
   1167         if (newLength < length)
   1168         {
   1169             if (newLength <= 0)
   1170             {
   1171                 // Replace the switch instruction by a simple branch instruction.
   1172                 Instruction replacementInstruction =
   1173                     new BranchInstruction(InstructionConstants.OP_GOTO,
   1174                                           defaultOffset);
   1175 
   1176                 replaceInstruction(clazz, offset, lookUpSwitchInstruction,
   1177                                    replacementInstruction);
   1178             }
   1179             else
   1180             {
   1181                 // Remove redundant entries from the arrays.
   1182                 int[] cases          = lookUpSwitchInstruction.cases;
   1183                 int[] newJumpOffsets = new int[newLength];
   1184                 int[] newCases       = new int[newLength];
   1185 
   1186                 int newIndex = 0;
   1187 
   1188                 for (int index = 0; index < length; index++)
   1189                 {
   1190                     if (jumpOffsets[index] != defaultOffset)
   1191                     {
   1192                         newJumpOffsets[newIndex] = jumpOffsets[index];
   1193                         newCases[newIndex++]     = cases[index];
   1194                     }
   1195                 }
   1196 
   1197                 lookUpSwitchInstruction.jumpOffsets = newJumpOffsets;
   1198                 lookUpSwitchInstruction.cases       = newCases;
   1199 
   1200                 replaceInstruction(clazz, offset, lookUpSwitchInstruction,
   1201                                    lookUpSwitchInstruction);
   1202             }
   1203         }
   1204     }
   1205 
   1206 
   1207     /**
   1208      * Replaces the given instruction by an infinite loop.
   1209      */
   1210     private void replaceByInfiniteLoop(Clazz       clazz,
   1211                                        int         offset,
   1212                                        Instruction instruction)
   1213     {
   1214         // Replace the instruction by an infinite loop.
   1215         Instruction replacementInstruction =
   1216             new BranchInstruction(InstructionConstants.OP_GOTO, 0);
   1217 
   1218         if (DEBUG) System.out.println("  Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset));
   1219 
   1220         codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
   1221 
   1222         // Visit the instruction, if required.
   1223         if (extraInstructionVisitor != null)
   1224         {
   1225             // Note: we're not passing the right arguments for now, knowing that
   1226             // they aren't used anyway.
   1227             instruction.accept(clazz,
   1228                                null,
   1229                                null,
   1230                                offset,
   1231                                extraInstructionVisitor);
   1232         }
   1233     }
   1234 
   1235 
   1236     /**
   1237      * Replaces the instruction at a given offset by a given push instruction.
   1238      */
   1239     private void replaceInstruction(Clazz       clazz,
   1240                                     int         offset,
   1241                                     Instruction instruction,
   1242                                     Instruction replacementInstruction)
   1243     {
   1244         // Pop unneeded stack entries if necessary.
   1245         int popCount =
   1246             instruction.stackPopCount(clazz) -
   1247             replacementInstruction.stackPopCount(clazz);
   1248 
   1249         insertPopInstructions(offset, popCount);
   1250 
   1251         if (DEBUG) System.out.println("  Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)"));
   1252 
   1253         codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
   1254 
   1255         // Visit the instruction, if required.
   1256         if (extraInstructionVisitor != null)
   1257         {
   1258             // Note: we're not passing the right arguments for now, knowing that
   1259             // they aren't used anyway.
   1260             instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
   1261         }
   1262     }
   1263 
   1264 
   1265     /**
   1266      * Pops the given number of stack entries before the instruction at the
   1267      * given offset.
   1268      */
   1269     private void insertPopInstructions(int offset, int popCount)
   1270     {
   1271         switch (popCount)
   1272         {
   1273             case 0:
   1274             {
   1275                 break;
   1276             }
   1277             case 1:
   1278             {
   1279                 // Insert a single pop instruction.
   1280                 Instruction popInstruction =
   1281                     new SimpleInstruction(InstructionConstants.OP_POP);
   1282 
   1283                 codeAttributeEditor.insertBeforeInstruction(offset,
   1284                                                             popInstruction);
   1285                 break;
   1286             }
   1287             case 2:
   1288             {
   1289                 // Insert a single pop2 instruction.
   1290                 Instruction popInstruction =
   1291                     new SimpleInstruction(InstructionConstants.OP_POP2);
   1292 
   1293                 codeAttributeEditor.insertBeforeInstruction(offset,
   1294                                                             popInstruction);
   1295                 break;
   1296             }
   1297             default:
   1298             {
   1299                 // Insert the specified number of pop instructions.
   1300                 Instruction[] popInstructions =
   1301                     new Instruction[popCount / 2 + popCount % 2];
   1302 
   1303                 Instruction popInstruction =
   1304                     new SimpleInstruction(InstructionConstants.OP_POP2);
   1305 
   1306                 for (int index = 0; index < popCount / 2; index++)
   1307                 {
   1308                       popInstructions[index] = popInstruction;
   1309                 }
   1310 
   1311                 if (popCount % 2 == 1)
   1312                 {
   1313                     popInstruction =
   1314                         new SimpleInstruction(InstructionConstants.OP_POP);
   1315 
   1316                     popInstructions[popCount / 2] = popInstruction;
   1317                 }
   1318 
   1319                 codeAttributeEditor.insertBeforeInstruction(offset,
   1320                                                             popInstructions);
   1321                 break;
   1322             }
   1323         }
   1324     }
   1325 
   1326 
   1327     /**
   1328      * Replaces the simple enum switch instructions at a given offsets by a
   1329      * given replacement instruction.
   1330      */
   1331     private void replaceSimpleEnumSwitchInstruction(Clazz             clazz,
   1332                                                     int               loadOffset,
   1333                                                     int               switchOffset,
   1334                                                     SwitchInstruction replacementSwitchInstruction)
   1335     {
   1336         if (DEBUG) System.out.println("  Replacing switch instruction at ["+switchOffset+"] -> ["+loadOffset+"] swap + pop, "+replacementSwitchInstruction.toString(switchOffset)+")");
   1337 
   1338         // Remove the array load instruction.
   1339         codeAttributeEditor.replaceInstruction(loadOffset, new Instruction[]
   1340             {
   1341                 new SimpleInstruction(InstructionConstants.OP_SWAP),
   1342                 new SimpleInstruction(InstructionConstants.OP_POP),
   1343             });
   1344 
   1345         // Replace the switch instruction.
   1346         codeAttributeEditor.replaceInstruction(switchOffset, replacementSwitchInstruction);
   1347 
   1348         // Visit the instruction, if required.
   1349         if (extraInstructionVisitor != null)
   1350         {
   1351             // Note: we're not passing the right arguments for now, knowing that
   1352             // they aren't used anyway.
   1353             replacementSwitchInstruction.accept(clazz,
   1354                                                 null,
   1355                                                 null,
   1356                                                 switchOffset,
   1357                                                 extraInstructionVisitor);
   1358         }
   1359     }
   1360 }
   1361