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.*;
26import proguard.classfile.constant.ClassConstant;
27import proguard.classfile.constant.visitor.ConstantVisitor;
28import proguard.classfile.instruction.*;
29import proguard.classfile.instruction.visitor.InstructionVisitor;
30import proguard.classfile.util.*;
31import proguard.classfile.visitor.*;
32import proguard.evaluation.*;
33import proguard.evaluation.value.*;
34import proguard.optimize.info.SimpleEnumMarker;
35
36/**
37 * This ClassVisitor marks enums that can't be simplified due to the way they
38 * are used in the classes that it visits.
39 *
40 * @see SimpleEnumMarker
41 * @author Eric Lafortune
42 */
43public class SimpleEnumUseChecker
44extends      SimplifiedVisitor
45implements   ClassVisitor,
46             MemberVisitor,
47             AttributeVisitor,
48             InstructionVisitor,
49             ConstantVisitor,
50             ParameterVisitor
51{
52    //*
53    private static final boolean DEBUG = false;
54    /*/
55    private static       boolean DEBUG = System.getProperty("enum") != null;
56    //*/
57
58    private final PartialEvaluator       partialEvaluator;
59    private final MemberVisitor          methodCodeChecker           = new AllAttributeVisitor(this);
60    private final ConstantVisitor        invokedMethodChecker        = new ReferencedMemberVisitor(this);
61    private final ConstantVisitor        parameterChecker            = new ReferencedMemberVisitor(new AllParameterVisitor(this));
62    private final ClassVisitor           complexEnumMarker           = new SimpleEnumMarker(false);
63    private final ReferencedClassVisitor referencedComplexEnumMarker = new ReferencedClassVisitor(complexEnumMarker);
64
65
66    // Fields acting as parameters and return values for the visitor methods.
67    private int invocationOffset;
68
69
70    /**
71     * Creates a new SimpleEnumUseSimplifier.
72     */
73    public SimpleEnumUseChecker()
74    {
75        this(new PartialEvaluator());
76    }
77
78
79    /**
80     * Creates a new SimpleEnumUseChecker.
81     * @param partialEvaluator the partial evaluator that will execute the code
82     *                         and provide information about the results.
83     */
84    public SimpleEnumUseChecker(PartialEvaluator partialEvaluator)
85    {
86        this.partialEvaluator = partialEvaluator;
87    }
88
89
90    // Implementations for ClassVisitor.
91
92    public void visitProgramClass(ProgramClass programClass)
93    {
94        if ((programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) != 0)
95        {
96            // Unmark the simple enum classes in annotations.
97            programClass.methodsAccept(referencedComplexEnumMarker);
98        }
99        else
100        {
101            // Unmark the simple enum classes that are used in a complex way.
102            programClass.methodsAccept(methodCodeChecker);
103        }
104    }
105
106
107    // Implementations for AttributeVisitor.
108
109    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
110
111
112    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
113    {
114        // Evaluate the method.
115        partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
116
117        int codeLength = codeAttribute.u4codeLength;
118
119        // Check all traced instructions.
120        for (int offset = 0; offset < codeLength; offset++)
121        {
122            if (partialEvaluator.isTraced(offset))
123            {
124                Instruction instruction = InstructionFactory.create(codeAttribute.code,
125                                                                    offset);
126
127                instruction.accept(clazz, method, codeAttribute, offset, this);
128
129                // Check generalized stacks and variables at branch targets.
130                if (partialEvaluator.isBranchOrExceptionTarget(offset))
131                {
132                    checkMixedStackEntriesBefore(offset);
133
134                    checkMixedVariablesBefore(offset);
135                }
136            }
137        }
138    }
139
140
141    // Implementations for InstructionVisitor.
142
143    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
144    {
145        switch (simpleInstruction.opcode)
146        {
147            case InstructionConstants.OP_AASTORE:
148            {
149                // Check if the instruction is storing a simple enum in a
150                // more general array.
151                if (!isPoppingSimpleEnumType(offset, 2))
152                {
153                    if (DEBUG)
154                    {
155                        if (isPoppingSimpleEnumType(offset))
156                        {
157                            System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] stores enum ["+
158                                               partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] in more general array ["+
159                                               partialEvaluator.getStackBefore(offset).getTop(2).referenceValue().getType()+"]");
160                        }
161                    }
162
163                    markPoppedComplexEnumType(offset);
164                }
165                break;
166            }
167            case InstructionConstants.OP_ARETURN:
168            {
169                // Check if the instruction is returning a simple enum as a
170                // more general type.
171                if (!isReturningSimpleEnumType(clazz, method))
172                {
173                    if (DEBUG)
174                    {
175                        if (isPoppingSimpleEnumType(offset))
176                        {
177                            System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] returns enum [" +
178                                               partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as more general type");
179                        }
180                    }
181
182                    markPoppedComplexEnumType(offset);
183                }
184                break;
185            }
186            case InstructionConstants.OP_MONITORENTER:
187            case InstructionConstants.OP_MONITOREXIT:
188            {
189                // Make sure the popped type is not a simple enum type.
190                if (DEBUG)
191                {
192                    if (isPoppingSimpleEnumType(offset))
193                    {
194                        System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] uses enum ["+
195                                           partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as monitor");
196                    }
197                }
198
199                markPoppedComplexEnumType(offset);
200
201                break;
202            }
203        }
204    }
205
206
207    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
208    {
209    }
210
211
212    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
213    {
214        switch (constantInstruction.opcode)
215        {
216            case InstructionConstants.OP_PUTSTATIC:
217            case InstructionConstants.OP_PUTFIELD:
218            {
219                // Check if the instruction is generalizing a simple enum to a
220                // different type.
221                invocationOffset = offset;
222                clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
223                                              parameterChecker);
224                break;
225            }
226            case InstructionConstants.OP_INVOKEVIRTUAL:
227            {
228                // Check if the instruction is calling a simple enum.
229                String invokedMethodName =
230                    clazz.getRefName(constantInstruction.constantIndex);
231                String invokedMethodType =
232                    clazz.getRefType(constantInstruction.constantIndex);
233                int stackEntryIndex =
234                    ClassUtil.internalMethodParameterSize(invokedMethodType);
235                if (isPoppingSimpleEnumType(offset, stackEntryIndex) &&
236                    !isSupportedMethod(invokedMethodName,
237                                       invokedMethodType))
238                {
239                    if (DEBUG)
240                    {
241                        System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] calls ["+partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue().getType()+"."+invokedMethodName+"]");
242                    }
243
244                    markPoppedComplexEnumType(offset, stackEntryIndex);
245                }
246
247                // Check if any of the parameters is generalizing a simple
248                // enum to a different type.
249                invocationOffset = offset;
250                clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
251                                              parameterChecker);
252                break;
253            }
254            case InstructionConstants.OP_INVOKESPECIAL:
255            case InstructionConstants.OP_INVOKESTATIC:
256            case InstructionConstants.OP_INVOKEINTERFACE:
257            {
258                // Check if it is calling a method that we can't simplify.
259                clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
260                                              invokedMethodChecker);
261
262                // Check if any of the parameters is generalizing a simple
263                // enum to a different type.
264                invocationOffset = offset;
265                clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
266                                              parameterChecker);
267                break;
268            }
269            case InstructionConstants.OP_CHECKCAST:
270            case InstructionConstants.OP_INSTANCEOF:
271            {
272                // Check if the instruction is popping a different type.
273                if (!isPoppingExpectedType(offset,
274                                           clazz,
275                                           constantInstruction.constantIndex))
276                {
277                    if (DEBUG)
278                    {
279                        if (isPoppingSimpleEnumType(offset))
280                        {
281                            System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+
282                                               partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+
283                                               clazz.getClassName(constantInstruction.constantIndex)+"]");
284                        }
285                    }
286
287                    // Make sure the popped type is not a simple enum type.
288                    markPoppedComplexEnumType(offset);
289
290                    // Make sure the checked type is not a simple enum type.
291                    // We're somewhat arbitrarily skipping casts in static
292                    // methods of simple enum classes, because they do occur
293                    // in values() and valueOf(String), without obstructing
294                    // simplification.
295                    if (!isSimpleEnum(clazz)                                       ||
296                        (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 ||
297                        constantInstruction.opcode != InstructionConstants.OP_CHECKCAST)
298                    {
299                        if (DEBUG)
300                        {
301                            if (isSimpleEnum(((ClassConstant)((ProgramClass)clazz).getConstant(constantInstruction.constantIndex)).referencedClass))
302                            {
303                                System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+
304                                                   partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+
305                                                   clazz.getClassName(constantInstruction.constantIndex)+"]");
306                            }
307                        }
308
309                        markConstantComplexEnumType(clazz, constantInstruction.constantIndex);
310                    }
311                }
312                break;
313            }
314        }
315    }
316
317
318    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
319    {
320        switch (branchInstruction.opcode)
321        {
322            case InstructionConstants.OP_IFACMPEQ:
323            case InstructionConstants.OP_IFACMPNE:
324            {
325                // Check if the instruction is comparing different types.
326                if (!isPoppingIdenticalTypes(offset, 0, 1))
327                {
328                    if (DEBUG)
329                    {
330                        if (isPoppingSimpleEnumType(offset, 0))
331                        {
332                            System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] to plain type");
333                        }
334
335                        if (isPoppingSimpleEnumType(offset, 1))
336                        {
337                            System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(1).referenceValue().getType()+"] to plain type");
338                        }
339                    }
340
341                    // Make sure the first popped type is not a simple enum type.
342                    markPoppedComplexEnumType(offset, 0);
343
344                    // Make sure the second popped type is not a simple enum type.
345                    markPoppedComplexEnumType(offset, 1);
346                }
347                break;
348            }
349        }
350    }
351
352
353    public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
354    {
355    }
356
357
358    // Implementations for MemberVisitor.
359
360    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {}
361
362
363    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
364    {
365        if (isSimpleEnum(programClass) &&
366            isUnsupportedMethod(programMethod.getName(programClass),
367                                programMethod.getDescriptor(programClass)))
368        {
369            if (DEBUG)
370            {
371                System.out.println("SimpleEnumUseChecker: invocation of ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]");
372            }
373
374            complexEnumMarker.visitProgramClass(programClass);
375        }
376    }
377
378
379    // Implementations for ParameterVisitor.
380
381    public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass)
382    {
383        // Check if the parameter is passing a simple enum as a more general
384        // type.
385        int stackEntryIndex = parameterSize - parameterOffset - 1;
386        if (ClassUtil.isInternalClassType(parameterType) &&
387            !isPoppingExpectedType(invocationOffset, stackEntryIndex,
388                                   ClassUtil.isInternalArrayType(parameterType) ?
389                                       parameterType :
390                                       ClassUtil.internalClassNameFromClassType(parameterType)))
391        {
392            if (DEBUG)
393            {
394                ReferenceValue poppedValue =
395                    partialEvaluator.getStackBefore(invocationOffset).getTop(stackEntryIndex).referenceValue();
396                if (isSimpleEnumType(poppedValue))
397                {
398                    System.out.println("SimpleEnumUseChecker: ["+poppedValue.getType()+"] "+
399                                       (member instanceof Field ?
400                                            ("is stored as more general type ["+parameterType+"] in field ["+clazz.getName()+"."+member.getName(clazz)+"]") :
401                                            ("is passed as more general argument #"+parameterIndex+" ["+parameterType+"] to ["+clazz.getName()+"."+member.getName(clazz)+"]")));
402                }
403            }
404
405            // Make sure the popped type is not a simple enum type.
406            markPoppedComplexEnumType(invocationOffset, stackEntryIndex);
407        }
408    }
409
410
411    // Small utility methods.
412
413    /**
414     * Returns whether the specified enum method is supported for simple enums.
415     */
416    private boolean isSupportedMethod(String name, String type)
417    {
418        return
419            name.equals(ClassConstants.METHOD_NAME_ORDINAL) &&
420            type.equals(ClassConstants.METHOD_TYPE_ORDINAL) ||
421
422            name.equals(ClassConstants.METHOD_NAME_CLONE) &&
423            type.equals(ClassConstants.METHOD_TYPE_CLONE);
424    }
425
426
427    /**
428     * Returns whether the specified enum method is unsupported for simple enums.
429     */
430    private boolean isUnsupportedMethod(String name, String type)
431    {
432        return
433            name.equals(ClassConstants.METHOD_NAME_VALUEOF);
434    }
435
436
437    /**
438     * Unmarks simple enum classes that are mixed with incompatible reference
439     * types in the stack before the given instruction offset.
440     */
441    private void checkMixedStackEntriesBefore(int offset)
442    {
443        TracedStack stackBefore = partialEvaluator.getStackBefore(offset);
444
445        // Check all stack entries.
446        int stackSize = stackBefore.size();
447
448        for (int stackEntryIndex = 0; stackEntryIndex < stackSize; stackEntryIndex++)
449        {
450            // Check reference entries.
451            Value stackEntry = stackBefore.getBottom(stackEntryIndex);
452            if (stackEntry.computationalType() == Value.TYPE_REFERENCE)
453            {
454                // Check reference entries with multiple producers.
455                InstructionOffsetValue producerOffsets =
456                    stackBefore.getBottomActualProducerValue(stackEntryIndex).instructionOffsetValue();
457
458                int producerCount = producerOffsets.instructionOffsetCount();
459                if (producerCount > 1)
460                {
461                    // Is the consumed stack entry not a simple enum?
462                    ReferenceValue consumedStackEntry =
463                        stackEntry.referenceValue();
464
465                    if (!isSimpleEnumType(consumedStackEntry))
466                    {
467                        // Check all producers.
468                        for (int producerIndex = 0; producerIndex < producerCount; producerIndex++)
469                        {
470                            int producerOffset =
471                                producerOffsets.instructionOffset(producerIndex);
472
473                            if (producerOffset >= 0)
474                            {
475                                if (DEBUG)
476                                {
477                                    ReferenceValue producedValue =
478                                        partialEvaluator.getStackAfter(producerOffset).getTop(0).referenceValue();
479                                    if (isSimpleEnumType(producedValue))
480                                    {
481                                        System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type on stack");
482                                    }
483                                }
484
485                                // Make sure the produced stack entry isn't a
486                                // simple enum either.
487                                markPushedComplexEnumType(producerOffset);
488                            }
489                        }
490                    }
491                }
492            }
493        }
494    }
495
496
497    /**
498     * Unmarks simple enum classes that are mixed with incompatible reference
499     * types in the variables before the given instruction offset.
500     */
501    private void checkMixedVariablesBefore(int offset)
502    {
503        TracedVariables variablesBefore =
504            partialEvaluator.getVariablesBefore(offset);
505
506        // Check all variables.
507        int variablesSize = variablesBefore.size();
508
509        for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++)
510        {
511            // Check reference variables.
512            Value variable = variablesBefore.getValue(variableIndex);
513            if (variable != null &&
514                variable.computationalType() == Value.TYPE_REFERENCE)
515            {
516                // Check reference variables with multiple producers.
517                InstructionOffsetValue producerOffsets =
518                    variablesBefore.getProducerValue(variableIndex).instructionOffsetValue();
519
520                int producerCount = producerOffsets.instructionOffsetCount();
521                if (producerCount > 1)
522                {
523                    // Is the consumed variable not a simple enum?
524                    ReferenceValue consumedVariable =
525                        variable.referenceValue();
526
527                    if (!isSimpleEnumType(consumedVariable))
528                    {
529                        // Check all producers.
530                        for (int producerIndex = 0; producerIndex < producerCount; producerIndex++)
531                        {
532                            int producerOffset =
533                                producerOffsets.instructionOffset(producerIndex);
534
535                            if (producerOffset >= 0)
536                            {
537                                if (DEBUG)
538                                {
539                                    ReferenceValue producedValue =
540                                        partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue();
541                                    if (isSimpleEnumType(producedValue))
542                                    {
543                                        System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type in variables");
544                                    }
545                                }
546
547                                // Make sure the stored variable entry isn't a
548                                // simple enum either.
549                                markStoredComplexEnumType(producerOffset, variableIndex);
550                            }
551                        }
552                    }
553                }
554            }
555        }
556    }
557
558
559    /**
560     * Returns whether the instruction at the given offset is popping two
561     * identical reference types.
562     */
563    private boolean isPoppingIdenticalTypes(int offset,
564                                            int stackEntryIndex1,
565                                            int stackEntryIndex2)
566    {
567        TracedStack stackBefore = partialEvaluator.getStackBefore(offset);
568
569        String type1 =
570            stackBefore.getTop(stackEntryIndex1).referenceValue().getType();
571        String type2 =
572            stackBefore.getTop(stackEntryIndex2).referenceValue().getType();
573
574        return type1 == null ? type2 == null : type1.equals(type2);
575    }
576
577
578    /**
579     * Returns whether the instruction at the given offset is popping exactly
580     * the reference type of the specified class constant.
581     */
582    private boolean isPoppingExpectedType(int   offset,
583                                          Clazz clazz,
584                                          int   constantIndex)
585    {
586        return isPoppingExpectedType(offset, 0, clazz, constantIndex);
587    }
588
589
590    /**
591     * Returns whether the instruction at the given offset is popping exactly
592     * the reference type of the specified class constant.
593     */
594    private boolean isPoppingExpectedType(int   offset,
595                                          int   stackEntryIndex,
596                                          Clazz clazz,
597                                          int   constantIndex)
598    {
599        return isPoppingExpectedType(offset,
600                                     stackEntryIndex,
601                                     clazz.getClassName(constantIndex));
602    }
603
604
605    /**
606     * Returns whether the instruction at the given offset is popping exactly
607     * the given reference type.
608     */
609    private boolean isPoppingExpectedType(int    offset,
610                                          int    stackEntryIndex,
611                                          String expectedType)
612    {
613        TracedStack stackBefore = partialEvaluator.getStackBefore(offset);
614
615        String poppedType =
616            stackBefore.getTop(stackEntryIndex).referenceValue().getType();
617
618        return expectedType.equals(poppedType);
619    }
620
621
622    /**
623     * Returns whether the given method is returning a simple enum type.
624     * This includes simple enum arrays.
625     */
626    private boolean isReturningSimpleEnumType(Clazz clazz, Method method)
627    {
628        String descriptor = method.getDescriptor(clazz);
629        String returnType = ClassUtil.internalMethodReturnType(descriptor);
630
631        if (ClassUtil.isInternalClassType(returnType))
632        {
633            Clazz[] referencedClasses =
634                ((ProgramMethod)method).referencedClasses;
635
636            if (referencedClasses != null)
637            {
638                int returnedClassIndex =
639                    new DescriptorClassEnumeration(descriptor).classCount() - 1;
640
641                Clazz returnedClass = referencedClasses[returnedClassIndex];
642
643                return isSimpleEnum(returnedClass);
644            }
645        }
646
647        return false;
648    }
649
650
651    /**
652     * Returns whether the instruction at the given offset is popping a type
653     * with a simple enum class. This includes simple enum arrays.
654     */
655    private boolean isPoppingSimpleEnumType(int offset)
656    {
657        return isPoppingSimpleEnumType(offset, 0);
658    }
659
660
661    /**
662     * Returns whether the instruction at the given offset is popping a type
663     * with a simple enum class. This includes simple enum arrays.
664     */
665    private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex)
666    {
667        ReferenceValue referenceValue =
668            partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
669
670        return isSimpleEnumType(referenceValue);
671    }
672
673
674    /**
675     * Returns whether the given value is a simple enum type. This includes
676     * simple enum arrays.
677     */
678    private boolean isSimpleEnumType(ReferenceValue referenceValue)
679    {
680        return isSimpleEnum(referenceValue.getReferencedClass());
681    }
682
683
684    /**
685     * Returns whether the given class is not null and a simple enum class.
686     */
687    private boolean isSimpleEnum(Clazz clazz)
688    {
689        return clazz != null &&
690               SimpleEnumMarker.isSimpleEnum(clazz);
691    }
692
693
694    /**
695     * Marks the enum class of the popped type as complex.
696     */
697    private void markConstantComplexEnumType(Clazz clazz, int constantIndex)
698    {
699        clazz.constantPoolEntryAccept(constantIndex,
700                                      referencedComplexEnumMarker);
701    }
702
703
704    /**
705     * Marks the enum class of the popped type as complex.
706     */
707    private void markPoppedComplexEnumType(int offset)
708    {
709        markPoppedComplexEnumType(offset, 0);
710    }
711
712
713    /**
714     * Marks the enum class of the specified popped type as complex.
715     */
716    private void markPoppedComplexEnumType(int offset, int stackEntryIndex)
717    {
718        ReferenceValue referenceValue =
719            partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
720
721        markComplexEnumType(referenceValue);
722    }
723
724
725    /**
726     * Marks the enum class of the specified pushed type as complex.
727     */
728    private void markPushedComplexEnumType(int offset)
729    {
730        ReferenceValue referenceValue =
731            partialEvaluator.getStackAfter(offset).getTop(0).referenceValue();
732
733        markComplexEnumType(referenceValue);
734    }
735
736
737    /**
738     * Marks the enum class of the specified stored type as complex.
739     */
740    private void markStoredComplexEnumType(int offset, int variableIndex)
741    {
742        ReferenceValue referenceValue =
743            partialEvaluator.getVariablesAfter(offset).getValue(variableIndex).referenceValue();
744
745        markComplexEnumType(referenceValue);
746    }
747
748
749    /**
750     * Marks the enum class of the specified value as complex.
751     */
752    private void markComplexEnumType(ReferenceValue referenceValue)
753    {
754        Clazz clazz = referenceValue.getReferencedClass();
755        if (clazz != null)
756        {
757            clazz.accept(complexEnumMarker);
758        }
759    }
760}
761