• 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.constant.*;
27 import proguard.classfile.constant.visitor.ConstantVisitor;
28 import proguard.classfile.editor.*;
29 import proguard.classfile.instruction.*;
30 import proguard.classfile.instruction.visitor.InstructionVisitor;
31 import proguard.classfile.util.*;
32 import proguard.classfile.visitor.*;
33 import proguard.evaluation.value.*;
34 import proguard.optimize.info.SimpleEnumMarker;
35 
36 /**
37  * This AttributeVisitor simplifies the use of enums in the code attributes that
38  * it visits.
39  *
40  * @see SimpleEnumMarker
41  * @see MemberReferenceFixer
42  * @author Eric Lafortune
43  */
44 public class SimpleEnumUseSimplifier
45 extends      SimplifiedVisitor
46 implements   AttributeVisitor,
47              InstructionVisitor,
48              ConstantVisitor,
49              ParameterVisitor
50 {
51     //*
52     private static final boolean DEBUG = false;
53     /*/
54     private static       boolean DEBUG = System.getProperty("enum") != null;
55     //*/
56 
57     private final InstructionVisitor extraInstructionVisitor;
58 
59     private final PartialEvaluator    partialEvaluator;
60     private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true);
61     private final ConstantVisitor     nullParameterFixer  = new ReferencedMemberVisitor(new AllParameterVisitor(this));
62 
63     // Fields acting as parameters and return values for the visitor methods.
64     private Clazz         invocationClazz;
65     private Method        invocationMethod;
66     private CodeAttribute invocationCodeAttribute;
67     private int           invocationOffset;
68     private boolean       isSimpleEnum;
69 
70 
71     /**
72      * Creates a new SimpleEnumUseSimplifier.
73      */
SimpleEnumUseSimplifier()74     public SimpleEnumUseSimplifier()
75     {
76         this(new PartialEvaluator(), null);
77     }
78 
79 
80     /**
81      * Creates a new SimpleEnumDescriptorSimplifier.
82      * @param partialEvaluator        the partial evaluator that will
83      *                                execute the code and provide
84      *                                information about the results.
85      * @param extraInstructionVisitor an optional extra visitor for all
86      *                                simplified instructions.
87      */
SimpleEnumUseSimplifier(PartialEvaluator partialEvaluator, InstructionVisitor extraInstructionVisitor)88     public SimpleEnumUseSimplifier(PartialEvaluator   partialEvaluator,
89                                    InstructionVisitor extraInstructionVisitor)
90     {
91         this.partialEvaluator        = partialEvaluator;
92         this.extraInstructionVisitor = extraInstructionVisitor;
93     }
94 
95 
96     // Implementations for AttributeVisitor.
97 
visitAnyAttribute(Clazz clazz, Attribute attribute)98     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
99 
100 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)101     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
102     {
103         if (DEBUG)
104         {
105             System.out.println("SimpleEnumUseSimplifier: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
106         }
107 
108         // Skip the non-static methods of simple enum classes.
109         if (SimpleEnumMarker.isSimpleEnum(clazz) &&
110             (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0)
111         {
112             return;
113         }
114 
115         // Evaluate the method.
116         partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
117 
118         int codeLength = codeAttribute.u4codeLength;
119 
120         // Reset the code changes.
121         codeAttributeEditor.reset(codeLength);
122 
123         // Replace any instructions that can be simplified.
124         for (int offset = 0; offset < codeLength; offset++)
125         {
126             if (partialEvaluator.isTraced(offset))
127             {
128                 Instruction instruction = InstructionFactory.create(codeAttribute.code,
129                                                                     offset);
130 
131                 instruction.accept(clazz, method, codeAttribute, offset, this);
132             }
133         }
134 
135         // Apply all accumulated changes to the code.
136         codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
137     }
138 
139 
140     // Implementations for InstructionVisitor.
141 
visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)142     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
143     {
144         switch (simpleInstruction.opcode)
145         {
146             case InstructionConstants.OP_AALOAD:
147             {
148                 if (isPushingSimpleEnum(offset))
149                 {
150                     // Load a simple enum integer from an integer array.
151                     replaceInstruction(clazz,
152                                        offset,
153                                        simpleInstruction,
154                                        new SimpleInstruction(
155                                            InstructionConstants.OP_IALOAD));
156                 }
157                 break;
158             }
159             case InstructionConstants.OP_AASTORE:
160             {
161                 if (isPoppingSimpleEnumArray(offset, 2))
162                 {
163                     // Store a simple enum integer in an integer array.
164                     replaceInstruction(clazz,
165                                        offset,
166                                        simpleInstruction,
167                                        new SimpleInstruction(InstructionConstants.OP_IASTORE));
168 
169                     // Replace any producers of null constants.
170                     replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
171                 }
172                 break;
173             }
174             case InstructionConstants.OP_ARETURN:
175             {
176                 if (isReturningSimpleEnum(clazz, method))
177                 {
178                     // Return a simple enum integer instead of an enum.
179                     replaceInstruction(clazz,
180                                        offset,
181                                        simpleInstruction,
182                                        new SimpleInstruction(InstructionConstants.OP_IRETURN));
183 
184                     // Replace any producers of null constants.
185                     replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
186                 }
187                 break;
188             }
189         }
190     }
191 
192 
visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)193     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
194     {
195         int variableIndex = variableInstruction.variableIndex;
196 
197         switch (variableInstruction.opcode)
198         {
199             case InstructionConstants.OP_ALOAD:
200             case InstructionConstants.OP_ALOAD_0:
201             case InstructionConstants.OP_ALOAD_1:
202             case InstructionConstants.OP_ALOAD_2:
203             case InstructionConstants.OP_ALOAD_3:
204             {
205                 if (isPushingSimpleEnum(offset))
206                 {
207                     // Load a simple enum integer instead of an enum.
208                     replaceInstruction(clazz,
209                                        offset,
210                                        variableInstruction,
211                                        new VariableInstruction(InstructionConstants.OP_ILOAD,
212                                                                variableIndex));
213 
214                     // Replace any producers of null constants.
215                     replaceNullVariableProducers(clazz,
216                                                  method,
217                                                  codeAttribute,
218                                                  offset,
219                                                  variableIndex);
220                 }
221                 break;
222             }
223             case InstructionConstants.OP_ASTORE:
224             case InstructionConstants.OP_ASTORE_0:
225             case InstructionConstants.OP_ASTORE_1:
226             case InstructionConstants.OP_ASTORE_2:
227             case InstructionConstants.OP_ASTORE_3:
228             {
229                 if (!partialEvaluator.isSubroutineStart(offset) &&
230                     isPoppingSimpleEnum(offset))
231                 {
232                     // Store a simple enum integer instead of an enum.
233                     replaceInstruction(clazz,
234                                        offset,
235                                        variableInstruction,
236                                        new VariableInstruction(InstructionConstants.OP_ISTORE,
237                                                                variableIndex));
238 
239                     // Replace any producers of null constants.
240                     replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
241                 }
242                 break;
243             }
244         }
245     }
246 
247 
visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)248     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
249     {
250         switch (constantInstruction.opcode)
251         {
252             case InstructionConstants.OP_PUTSTATIC:
253             case InstructionConstants.OP_PUTFIELD:
254             {
255                 // Replace any producers of null constants.
256                 invocationClazz         = clazz;
257                 invocationMethod        = method;
258                 invocationCodeAttribute = codeAttribute;
259                 invocationOffset        = offset;
260                 clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
261                                               nullParameterFixer);
262                 break;
263             }
264             case InstructionConstants.OP_INVOKEVIRTUAL:
265             {
266                 // Check if the instruction is calling a simple enum.
267                 String invokedMethodName =
268                     clazz.getRefName(constantInstruction.constantIndex);
269                 String invokedMethodType =
270                     clazz.getRefType(constantInstruction.constantIndex);
271                 int stackEntryIndex =
272                     ClassUtil.internalMethodParameterSize(invokedMethodType);
273                 if (isPoppingSimpleEnum(offset, stackEntryIndex))
274                 {
275                     replaceSupportedMethod(clazz,
276                                            offset,
277                                            constantInstruction,
278                                            invokedMethodName,
279                                            invokedMethodType);
280                 }
281 
282                 // Fall through to check the parameters.
283             }
284             case InstructionConstants.OP_INVOKESPECIAL:
285             case InstructionConstants.OP_INVOKESTATIC:
286             case InstructionConstants.OP_INVOKEINTERFACE:
287             {
288                 // Replace any producers of null constants.
289                 invocationClazz         = clazz;
290                 invocationMethod        = method;
291                 invocationCodeAttribute = codeAttribute;
292                 invocationOffset        = offset;
293                 clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
294                                               nullParameterFixer);
295                 break;
296             }
297             case InstructionConstants.OP_ANEWARRAY:
298             {
299                 int constantIndex = constantInstruction.constantIndex;
300 
301                 if (isReferencingSimpleEnum(clazz, constantIndex) &&
302                     !ClassUtil.isInternalArrayType(clazz.getClassName(constantIndex)))
303                 {
304                     // Create an integer array instead of an enum array.
305                     replaceInstruction(clazz,
306                                        offset,
307                                        constantInstruction,
308                                        new SimpleInstruction(InstructionConstants.OP_NEWARRAY,
309                                                              InstructionConstants.ARRAY_T_INT));
310                 }
311                 break;
312             }
313             case InstructionConstants.OP_CHECKCAST:
314             {
315                 if (isPoppingSimpleEnum(offset))
316                 {
317                     // Enum classes can only be simple if the checkcast
318                     // succeeds, so we can delete it.
319                     deleteInstruction(clazz,
320                                       offset,
321                                       constantInstruction);
322 
323                     // Replace any producers of null constants.
324                     replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
325                 }
326                 break;
327             }
328             case InstructionConstants.OP_INSTANCEOF:
329             {
330                 if (isPoppingSimpleEnum(offset))
331                 {
332                     // Enum classes can only be simple if the instanceof
333                     // succeeds, so we can push a constant result.
334                     replaceInstruction(clazz,
335                                        offset,
336                                        constantInstruction,
337                                        new SimpleInstruction(InstructionConstants.OP_ICONST_1));
338 
339                     // Replace any producers of null constants.
340                     replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
341                 }
342                 break;
343             }
344         }
345     }
346 
347 
visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)348     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
349     {
350         switch (branchInstruction.opcode)
351         {
352             case InstructionConstants.OP_IFACMPEQ:
353             {
354                 if (isPoppingSimpleEnum(offset))
355                 {
356                     // Compare simple enum integers instead of enums.
357                     replaceInstruction(clazz,
358                                        offset,
359                                        branchInstruction,
360                                        new BranchInstruction(InstructionConstants.OP_IFICMPEQ,
361                                                              branchInstruction.branchOffset));
362                 }
363                 break;
364             }
365             case InstructionConstants.OP_IFACMPNE:
366             {
367                 if (isPoppingSimpleEnum(offset))
368                 {
369                     // Compare simple enum integers instead of enums.
370                     replaceInstruction(clazz,
371                                        offset,
372                                        branchInstruction,
373                                        new BranchInstruction(InstructionConstants.OP_IFICMPNE,
374                                                              branchInstruction.branchOffset));
375                 }
376                 break;
377             }
378             case InstructionConstants.OP_IFNULL:
379             {
380                 if (isPoppingSimpleEnum(offset))
381                 {
382                     // Compare with 0 instead of null.
383                     replaceInstruction(clazz,
384                                        offset,
385                                        branchInstruction,
386                                        new BranchInstruction(
387                                            InstructionConstants.OP_IFEQ,
388                                            branchInstruction.branchOffset));
389                 }
390                 break;
391             }
392             case InstructionConstants.OP_IFNONNULL:
393             {
394                 if (isPoppingSimpleEnum(offset))
395                 {
396                     // Compare with 0 instead of null.
397                     replaceInstruction(clazz,
398                                        offset,
399                                        branchInstruction,
400                                        new BranchInstruction(InstructionConstants.OP_IFNE,
401                                                              branchInstruction.branchOffset));
402                 }
403                 break;
404             }
405         }
406     }
407 
408 
visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)409     public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
410     {
411     }
412 
413 
414     // Implementations for ConstantVisitor.
415 
visitAnyConstant(Clazz clazz, Constant constant)416     public void visitAnyConstant(Clazz clazz, Constant constant) {}
417 
418 
visitStringConstant(Clazz clazz, StringConstant stringConstant)419     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
420     {
421         // Does the constant refer to a simple enum type?
422         isSimpleEnum = isSimpleEnum(stringConstant.referencedClass);
423     }
424 
425 
visitClassConstant(Clazz clazz, ClassConstant classConstant)426     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
427     {
428         // Does the constant refer to a simple enum type?
429         isSimpleEnum = isSimpleEnum(classConstant.referencedClass);
430     }
431 
432 
433     // Implementations for ParameterVisitor.
434 
visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass)435     public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass)
436     {
437         // Check if the parameter is passing a simple enum as a more general
438         // type.
439         if (!ClassUtil.isInternalPrimitiveType(parameterType.charAt(0)) &&
440             isSimpleEnum(referencedClass))
441         {
442             // Replace any producers of null constants for this parameter.
443             int stackEntryIndex = parameterSize - parameterOffset - 1;
444 
445             replaceNullStackEntryProducers(invocationClazz,
446                                  invocationMethod,
447                                  invocationCodeAttribute,
448                                  invocationOffset,
449                                  stackEntryIndex);
450         }
451     }
452 
453 
454     // Small utility methods.
455 
456     /**
457      * Returns whether the constant at the given offset is referencing a
458      * simple enum class.
459      */
isReferencingSimpleEnum(Clazz clazz, int constantIndex)460     private boolean isReferencingSimpleEnum(Clazz clazz, int constantIndex)
461     {
462         isSimpleEnum = false;
463 
464         clazz.constantPoolEntryAccept(constantIndex, this);
465 
466         return isSimpleEnum;
467     }
468 
469 
470     /**
471      * Returns whether the given method is returning a simple enum class.
472      */
isReturningSimpleEnum(Clazz clazz, Method method)473     private boolean isReturningSimpleEnum(Clazz clazz, Method method)
474     {
475         String descriptor = method.getDescriptor(clazz);
476         String returnType = ClassUtil.internalMethodReturnType(descriptor);
477 
478         if (ClassUtil.isInternalClassType(returnType) &&
479             !ClassUtil.isInternalArrayType(returnType))
480         {
481             Clazz[] referencedClasses =
482                 ((ProgramMethod)method).referencedClasses;
483 
484             if (referencedClasses != null)
485             {
486                 int returnedClassIndex =
487                     new DescriptorClassEnumeration(descriptor).classCount() - 1;
488 
489                 Clazz returnedClass = referencedClasses[returnedClassIndex];
490 
491                 return isSimpleEnum(returnedClass);
492             }
493         }
494 
495         return false;
496     }
497 
498 
499     /**
500      * Returns whether the instruction at the given offset is pushing a simple
501      * enum class.
502      */
isPushingSimpleEnum(int offset)503     private boolean isPushingSimpleEnum(int offset)
504     {
505         ReferenceValue referenceValue =
506             partialEvaluator.getStackAfter(offset).getTop(0).referenceValue();
507 
508         Clazz referencedClass = referenceValue.getReferencedClass();
509 
510         return isSimpleEnum(referencedClass) &&
511                !ClassUtil.isInternalArrayType(referenceValue.getType());
512     }
513 
514 
515     /**
516      * Returns whether the instruction at the given offset is popping a simple
517      * enum class.
518      */
isPoppingSimpleEnum(int offset)519     private boolean isPoppingSimpleEnum(int offset)
520     {
521         return isPoppingSimpleEnum(offset, 0);
522     }
523 
524 
525     /**
526      * Returns whether the instruction at the given offset is popping a simple
527      * enum class.
528      */
isPoppingSimpleEnum(int offset, int stackEntryIndex)529     private boolean isPoppingSimpleEnum(int offset, int stackEntryIndex)
530     {
531         ReferenceValue referenceValue =
532             partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
533 
534         return isSimpleEnum(referenceValue.getReferencedClass()) &&
535                !ClassUtil.isInternalArrayType(referenceValue.getType());
536     }
537 
538 
539     /**
540      * Returns whether the instruction at the given offset is popping a simple
541      * enum type. This includes simple enum arrays.
542      */
isPoppingSimpleEnumType(int offset, int stackEntryIndex)543     private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex)
544     {
545         ReferenceValue referenceValue =
546             partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
547 
548         return isSimpleEnum(referenceValue.getReferencedClass());
549     }
550 
551 
552     /**
553      * Returns whether the instruction at the given offset is popping a
554      * one-dimensional simple enum array.
555      */
isPoppingSimpleEnumArray(int offset, int stackEntryIndex)556     private boolean isPoppingSimpleEnumArray(int offset, int stackEntryIndex)
557     {
558         ReferenceValue referenceValue =
559             partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
560 
561         return isSimpleEnum(referenceValue.getReferencedClass()) &&
562                ClassUtil.internalArrayTypeDimensionCount(referenceValue.getType()) == 1;
563     }
564 
565 
566     /**
567      * Returns whether the given class is not null and a simple enum class.
568      */
isSimpleEnum(Clazz clazz)569     private boolean isSimpleEnum(Clazz clazz)
570     {
571         return clazz != null &&
572                SimpleEnumMarker.isSimpleEnum(clazz);
573     }
574 
575 
576     /**
577      * Returns whether the specified enum method is supported for simple enums.
578      */
replaceSupportedMethod(Clazz clazz, int offset, Instruction instruction, String name, String type)579     private void replaceSupportedMethod(Clazz       clazz,
580                                         int         offset,
581                                         Instruction instruction,
582                                         String      name,
583                                         String      type)
584     {
585         if (name.equals(ClassConstants.METHOD_NAME_ORDINAL) &&
586             type.equals(ClassConstants.METHOD_TYPE_ORDINAL))
587         {
588             Instruction[] replacementInstructions = new Instruction[]
589             {
590                 new SimpleInstruction(InstructionConstants.OP_ICONST_1),
591                 new SimpleInstruction(InstructionConstants.OP_ISUB),
592             };
593 
594             replaceInstructions(clazz,
595                                 offset,
596                                 instruction,
597                                 replacementInstructions);
598         }
599     }
600 
601 
602     /**
603      * Replaces the instruction at the given offset by the given instructions.
604      */
replaceInstructions(Clazz clazz, int offset, Instruction instruction, Instruction[] replacementInstructions)605     private void replaceInstructions(Clazz         clazz,
606                                      int           offset,
607                                      Instruction   instruction,
608                                      Instruction[] replacementInstructions)
609     {
610         if (DEBUG) System.out.println("  Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstructions.length+" instructions");
611 
612         codeAttributeEditor.replaceInstruction(offset, replacementInstructions);
613 
614         // Visit the instruction, if required.
615         if (extraInstructionVisitor != null)
616         {
617             // Note: we're not passing the right arguments for now, knowing that
618             // they aren't used anyway.
619             instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
620         }
621     }
622 
623 
624     /**
625      * Replaces the instruction at the given offset by the given instruction,
626      * popping any now unused stack entries.
627      */
replaceInstruction(Clazz clazz, int offset, Instruction instruction, Instruction replacementInstruction)628     private void replaceInstruction(Clazz       clazz,
629                                     int         offset,
630                                     Instruction instruction,
631                                     Instruction replacementInstruction)
632     {
633         // Pop unneeded stack entries if necessary.
634         int popCount =
635             instruction.stackPopCount(clazz) -
636             replacementInstruction.stackPopCount(clazz);
637 
638         insertPopInstructions(offset, popCount);
639 
640         if (DEBUG) System.out.println("  Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)"));
641 
642         codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
643 
644         // Visit the instruction, if required.
645         if (extraInstructionVisitor != null)
646         {
647             // Note: we're not passing the right arguments for now, knowing that
648             // they aren't used anyway.
649             instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
650         }
651     }
652 
653 
654     /**
655      * Deletes the instruction at the given offset, popping any now unused
656      * stack entries.
657      */
deleteInstruction(Clazz clazz, int offset, Instruction instruction)658     private void deleteInstruction(Clazz       clazz,
659                                     int         offset,
660                                     Instruction instruction)
661     {
662         // Pop unneeded stack entries if necessary.
663         //int popCount = instruction.stackPopCount(clazz);
664         //
665         //insertPopInstructions(offset, popCount);
666         //
667         //if (DEBUG) System.out.println("  Deleting instruction "+instruction.toString(offset)+(popCount == 0 ? "" : " ("+popCount+" pops)"));
668 
669         if (DEBUG) System.out.println("  Deleting instruction "+instruction.toString(offset));
670 
671         codeAttributeEditor.deleteInstruction(offset);
672 
673         // Visit the instruction, if required.
674         if (extraInstructionVisitor != null)
675         {
676             // Note: we're not passing the right arguments for now, knowing that
677             // they aren't used anyway.
678             instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
679         }
680     }
681 
682 
683     /**
684      * Pops the given number of stack entries before the instruction at the
685      * given offset.
686      */
insertPopInstructions(int offset, int popCount)687     private void insertPopInstructions(int offset, int popCount)
688     {
689         switch (popCount)
690         {
691             case 0:
692             {
693                 break;
694             }
695             case 1:
696             {
697                 // Insert a single pop instruction.
698                 Instruction popInstruction =
699                     new SimpleInstruction(InstructionConstants.OP_POP);
700 
701                 codeAttributeEditor.insertBeforeInstruction(offset,
702                                                             popInstruction);
703                 break;
704             }
705             case 2:
706             {
707                 // Insert a single pop2 instruction.
708                 Instruction popInstruction =
709                     new SimpleInstruction(InstructionConstants.OP_POP2);
710 
711                 codeAttributeEditor.insertBeforeInstruction(offset,
712                                                             popInstruction);
713                 break;
714             }
715             default:
716             {
717                 // Insert the specified number of pop instructions.
718                 Instruction[] popInstructions =
719                     new Instruction[popCount / 2 + popCount % 2];
720 
721                 Instruction popInstruction =
722                     new SimpleInstruction(InstructionConstants.OP_POP2);
723 
724                 for (int index = 0; index < popCount / 2; index++)
725                 {
726                       popInstructions[index] = popInstruction;
727                 }
728 
729                 if (popCount % 2 == 1)
730                 {
731                     popInstruction =
732                         new SimpleInstruction(InstructionConstants.OP_POP);
733 
734                     popInstructions[popCount / 2] = popInstruction;
735                 }
736 
737                 codeAttributeEditor.insertBeforeInstruction(offset,
738                                                             popInstructions);
739                 break;
740             }
741         }
742     }
743 
744 
745     /**
746      * Replaces aconst_null producers of the consumer of the top stack entry
747      * at the given offset by iconst_0.
748      */
replaceNullStackEntryProducers(Clazz clazz, Method method, CodeAttribute codeAttribute, int consumerOffset)749     private void replaceNullStackEntryProducers(Clazz         clazz,
750                                                 Method        method,
751                                                 CodeAttribute codeAttribute,
752                                                 int           consumerOffset)
753     {
754         replaceNullStackEntryProducers(clazz, method, codeAttribute, consumerOffset, 0);
755     }
756 
757 
758     /**
759      * Replaces aconst_null producers of the specified stack entry by
760      * iconst_0.
761      */
replaceNullStackEntryProducers(Clazz clazz, Method method, CodeAttribute codeAttribute, int consumerOffset, int stackEntryIndex)762     private void replaceNullStackEntryProducers(Clazz         clazz,
763                                                 Method        method,
764                                                 CodeAttribute codeAttribute,
765                                                 int           consumerOffset,
766                                                 int           stackEntryIndex)
767     {
768         InstructionOffsetValue producerOffsets =
769             partialEvaluator.getStackBefore(consumerOffset).getTopActualProducerValue(stackEntryIndex).instructionOffsetValue();
770 
771         for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++)
772         {
773             int producerOffset = producerOffsets.instructionOffset(index);
774 
775             // TODO: A method might be pushing the null constant.
776             if (producerOffset >= 0 &&
777                 codeAttribute.code[producerOffset] == InstructionConstants.OP_ACONST_NULL)
778             {
779                 // Replace pushing null by pushing 0.
780                 replaceInstruction(clazz,
781                                    producerOffset,
782                                    new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
783                                    new SimpleInstruction(InstructionConstants.OP_ICONST_0));
784             }
785         }
786     }
787 
788 
789     /**
790      * Replaces aconst_null/astore producers of the specified reference variable by
791      * iconst_0/istore.
792      */
replaceNullVariableProducers(Clazz clazz, Method method, CodeAttribute codeAttribute, int consumerOffset, int variableIndex)793     private void replaceNullVariableProducers(Clazz         clazz,
794                                               Method        method,
795                                               CodeAttribute codeAttribute,
796                                               int           consumerOffset,
797                                               int           variableIndex)
798     {
799         InstructionOffsetValue producerOffsets =
800             partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue();
801 
802         for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++)
803         {
804             int producerOffset = producerOffsets.instructionOffset(index);
805 
806             if (producerOffset >= 0 &&
807                 partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue().isNull() == Value.ALWAYS)
808             {
809                 // Replace loading null by loading 0.
810                 replaceInstruction(clazz,
811                                    producerOffset,
812                                    new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex),
813                                    new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex));
814 
815                 // Replace pushing null by pushing 0.
816                 replaceNullStackEntryProducers(clazz, method, codeAttribute, producerOffset);
817             }
818         }
819     }
820 }
821