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.*;
27import proguard.classfile.editor.*;
28import proguard.classfile.instruction.*;
29import proguard.classfile.instruction.visitor.InstructionVisitor;
30import proguard.classfile.util.SimplifiedVisitor;
31import proguard.classfile.visitor.*;
32import proguard.optimize.info.SimpleEnumMarker;
33import proguard.optimize.peephole.*;
34
35/**
36 * This ClassVisitor simplifies the classes that it visits to simple enums.
37 *
38 * @see SimpleEnumMarker
39 * @see MemberReferenceFixer
40 * @author Eric Lafortune
41 */
42public class SimpleEnumClassSimplifier
43extends      SimplifiedVisitor
44implements   ClassVisitor,
45             AttributeVisitor,
46             InstructionVisitor
47{
48    //*
49    private static final boolean DEBUG = false;
50    /*/
51    private static       boolean DEBUG = System.getProperty("enum") != null;
52    //*/
53
54
55    private static final int ENUM_CLASS_NAME          = InstructionSequenceReplacer.A;
56    private static final int ENUM_TYPE_NAME           = InstructionSequenceReplacer.B;
57    private static final int ENUM_CONSTANT_NAME       = InstructionSequenceReplacer.X;
58    private static final int ENUM_CONSTANT_ORDINAL    = InstructionSequenceReplacer.Y;
59    private static final int ENUM_CONSTANT_FIELD_NAME = InstructionSequenceReplacer.Z;
60
61    private static final int STRING_ENUM_CONSTANT_NAME   = 0;
62
63    private static final int METHOD_ENUM_INIT            = 1;
64    private static final int FIELD_ENUM_CONSTANT         = 2;
65
66    private static final int CLASS_ENUM                  = 3;
67
68    private static final int NAME_AND_TYPE_ENUM_INIT     = 4;
69    private static final int NAME_AND_TYPE_ENUM_CONSTANT = 5;
70
71    private static final int UTF8_INIT                   = 6;
72    private static final int UTF8_STRING_I               = 7;
73
74
75    private static final Constant[] CONSTANTS = new Constant[]
76    {
77        new StringConstant(ENUM_CONSTANT_NAME, null, null),
78
79        new MethodrefConstant(CLASS_ENUM, NAME_AND_TYPE_ENUM_INIT,     null, null),
80        new FieldrefConstant( CLASS_ENUM, NAME_AND_TYPE_ENUM_CONSTANT, null, null),
81
82        new ClassConstant(ENUM_CLASS_NAME,  null),
83
84        new NameAndTypeConstant(UTF8_INIT, UTF8_STRING_I),
85        new NameAndTypeConstant(ENUM_CONSTANT_FIELD_NAME, ENUM_TYPE_NAME),
86
87        new Utf8Constant(ClassConstants.METHOD_NAME_INIT),
88        new Utf8Constant(ClassConstants.METHOD_TYPE_INIT_ENUM),
89    };
90
91    private static final Instruction[] INSTRUCTIONS = new Instruction[]
92    {
93        new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_ENUM),
94        new SimpleInstruction(InstructionConstants.OP_DUP),
95        new ConstantInstruction(InstructionConstants.OP_LDC, STRING_ENUM_CONSTANT_NAME),
96        new SimpleInstruction(InstructionConstants.OP_ICONST_0, ENUM_CONSTANT_ORDINAL),
97        new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_ENUM_INIT),
98    };
99
100    private static final Instruction[] REPLACEMENT_INSTRUCTIONS = new Instruction[]
101    {
102        new SimpleInstruction(InstructionConstants.OP_SIPUSH, ENUM_CONSTANT_ORDINAL),
103        new SimpleInstruction(InstructionConstants.OP_ICONST_1),
104        new SimpleInstruction(InstructionConstants.OP_IADD),
105    };
106
107
108    private final CodeAttributeEditor codeAttributeEditor =
109        new CodeAttributeEditor(true, true);
110
111    private final InstructionSequenceReplacer instructionSequenceReplacer =
112        new InstructionSequenceReplacer(CONSTANTS,
113                                        INSTRUCTIONS,
114                                        REPLACEMENT_INSTRUCTIONS,
115                                        null,
116                                        codeAttributeEditor);
117
118    private final MemberVisitor initializerSimplifier = new AllAttributeVisitor(this);
119
120
121    // Implementations for ClassVisitor.
122
123    public void visitProgramClass(ProgramClass programClass)
124    {
125        if (DEBUG)
126        {
127            System.out.println("SimpleEnumClassSimplifier: ["+programClass.getName()+"]");
128        }
129
130        // Unmark the class as an enum.
131        programClass.u2accessFlags &= ~ClassConstants.ACC_ENUM;
132
133        // Remove the valueOf method, if present.
134        Method valueOfMethod =
135            programClass.findMethod(ClassConstants.METHOD_NAME_VALUEOF, null);
136        if (valueOfMethod != null)
137        {
138            new ClassEditor(programClass).removeMethod(valueOfMethod);
139        }
140
141        // Simplify the static initializer.
142        programClass.methodAccept(ClassConstants.METHOD_NAME_CLINIT,
143                                  ClassConstants.METHOD_TYPE_CLINIT,
144                                  initializerSimplifier);
145    }
146
147
148    // Implementations for AttributeVisitor.
149
150    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
151
152
153    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
154    {
155        // Set up the code attribute editor.
156        codeAttributeEditor.reset(codeAttribute.u4codeLength);
157
158        // Find the peephole changes.
159        codeAttribute.instructionsAccept(clazz, method, instructionSequenceReplacer);
160
161        // Apply the peephole changes.
162        codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
163    }
164}
165