• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 package proguard.optimize.evaluation;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.visitor.AttributeVisitor;
26 import proguard.classfile.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      */
EvaluationSimplifier()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      */
EvaluationSimplifier(PartialEvaluator partialEvaluator, InstructionVisitor extraInstructionVisitor)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 
visitAnyAttribute(Clazz clazz, Attribute attribute)91     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
92 
93 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)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 
visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)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 
visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)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 
visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)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 
visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)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 
visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)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 
visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)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 
visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)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      */
replaceAnyPushInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceIntegerPushInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceIntegerPushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)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      */
replaceLongPushInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceLongPushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)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      */
replaceFloatPushInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceFloatPushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)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      */
replaceDoublePushInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceDoublePushInstruction(Clazz clazz, int offset, Instruction instruction, int maxVariableIndex)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      */
replaceReferencePushInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceConstantPushInstruction(Clazz clazz, int offset, Instruction instruction, byte replacementOpcode, int value)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      */
replaceVariablePushInstruction(Clazz clazz, int offset, Instruction instruction, byte replacementOpcode, int variableIndex)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      */
replaceJsrInstruction(Clazz clazz, int offset, BranchInstruction branchInstruction)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      */
deleteReferencePopInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceBranchInstruction(Clazz clazz, int offset, Instruction instruction)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      */
replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)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      */
replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int loadOffset, int switchOffset, TableSwitchInstruction tableSwitchInstruction, ReferenceValue mappingValue)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      */
replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookupSwitchInstruction)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      */
replaceSimpleEnumSwitchInstruction(Clazz clazz, CodeAttribute codeAttribute, int loadOffset, int switchOffset, LookUpSwitchInstruction lookupSwitchInstruction, ReferenceValue mappingValue)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      */
cleanUpSwitchInstruction(Clazz clazz, int offset, SwitchInstruction switchInstruction)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      */
trimSwitchInstruction(Clazz clazz, int offset, TableSwitchInstruction tableSwitchInstruction)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      */
trimSwitchInstruction(Clazz clazz, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)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      */
replaceByInfiniteLoop(Clazz clazz, int offset, Instruction instruction)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      */
replaceInstruction(Clazz clazz, int offset, Instruction instruction, Instruction replacementInstruction)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      */
insertPopInstructions(int offset, int popCount)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      */
replaceSimpleEnumSwitchInstruction(Clazz clazz, int loadOffset, int switchOffset, SwitchInstruction replacementSwitchInstruction)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