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 */
21package proguard.optimize.evaluation;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.*;
25import proguard.classfile.attribute.visitor.AttributeVisitor;
26import proguard.classfile.constant.*;
27import proguard.classfile.constant.visitor.ConstantVisitor;
28import proguard.classfile.editor.*;
29import proguard.classfile.instruction.*;
30import proguard.classfile.instruction.visitor.InstructionVisitor;
31import proguard.classfile.util.*;
32import proguard.classfile.visitor.*;
33import proguard.evaluation.value.*;
34import 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 */
44public class SimpleEnumUseSimplifier
45extends      SimplifiedVisitor
46implements   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     */
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     */
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
98    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
99
100
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
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
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
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
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
409    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
410    {
411    }
412
413
414    // Implementations for ConstantVisitor.
415
416    public void visitAnyConstant(Clazz clazz, Constant constant) {}
417
418
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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