DuplicateInitializerFixer.java revision db267bc191f906f55eaef21a27110cce2ec57fdf
1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 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;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.visitor.AttributeVisitor;
25import proguard.classfile.attribute.*;
26import proguard.classfile.attribute.annotation.*;
27import proguard.classfile.editor.ConstantPoolEditor;
28import proguard.classfile.util.*;
29import proguard.classfile.visitor.MemberVisitor;
30
31/**
32 * This MemberVisitor adds an additional parameter to the duplicate
33 * initialization methods that it visits.
34 */
35public class DuplicateInitializerFixer
36extends      SimplifiedVisitor
37implements   MemberVisitor,
38             AttributeVisitor
39{
40    private static final boolean DEBUG = false;
41
42    private static final char[] TYPES = new char[]
43    {
44        ClassConstants.INTERNAL_TYPE_BYTE,
45        ClassConstants.INTERNAL_TYPE_CHAR,
46        ClassConstants.INTERNAL_TYPE_SHORT,
47        ClassConstants.INTERNAL_TYPE_INT,
48        ClassConstants.INTERNAL_TYPE_BOOLEAN
49    };
50
51
52    private final MemberVisitor extraFixedInitializerVisitor;
53
54
55    /**
56     * Creates a new DuplicateInitializerFixer.
57     */
58    public DuplicateInitializerFixer()
59    {
60        this(null);
61    }
62
63
64    /**
65     * Creates a new DuplicateInitializerFixer with an extra visitor.
66     * @param extraFixedInitializerVisitor an optional extra visitor for all
67     *                                     initializers that have been fixed.
68     */
69    public DuplicateInitializerFixer(MemberVisitor extraFixedInitializerVisitor)
70    {
71        this.extraFixedInitializerVisitor = extraFixedInitializerVisitor;
72    }
73
74
75    // Implementations for MemberVisitor.
76
77    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
78    {
79        // Is it a class instance initializer?
80        String name = programMethod.getName(programClass);
81        if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
82        {
83            // Is there already another initializer with the same descriptor?
84            String descriptor    = programMethod.getDescriptor(programClass);
85            Method similarMethod = programClass.findMethod(name, descriptor);
86            if (!programMethod.equals(similarMethod))
87            {
88                // Should this initializer be preserved?
89                if (!KeepMarker.isKept(programMethod))
90                {
91                    // Fix the other initializer.
92                    programMethod = (ProgramMethod)similarMethod;
93                }
94
95                int index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
96
97                // Try to find a new, unique descriptor.
98                for (int typeIndex = 0; typeIndex < TYPES.length; typeIndex++)
99                {
100                    String newDescriptor =
101                        descriptor.substring(0, index) +
102                        TYPES[typeIndex] +
103                        descriptor.substring(index);
104
105                    // Is the new initializer descriptor unique?
106                    if (programClass.findMethod(name, newDescriptor) == null)
107                    {
108                        if (DEBUG)
109                        {
110                            System.out.println("DuplicateInitializerFixer:");
111                            System.out.println("  ["+programClass.getName()+"]: "+name+descriptor+" -> "+newDescriptor);
112                        }
113
114                        // Update the descriptor.
115                        programMethod.u2descriptorIndex =
116                            new ConstantPoolEditor(programClass).addUtf8Constant(newDescriptor);
117
118                        // Fix the local variable frame size, the method
119                        // signature, and the parameter annotations, if
120                        // necessary.
121                        programMethod.attributesAccept(programClass,
122                                                       this);
123
124                        // Visit the initializer, if required.
125                        if (extraFixedInitializerVisitor != null)
126                        {
127                            extraFixedInitializerVisitor.visitProgramMethod(programClass, programMethod);
128                        }
129
130                        // We're done with this constructor.
131                        return;
132                    }
133                }
134
135                throw new IllegalStateException("Can't find unique constructor descriptor for ["+
136                                                programClass.getName()+"."+
137                                                programMethod.getName(programClass)+
138                                                programMethod.getDescriptor(programClass)+"]");
139            }
140        }
141    }
142
143
144    // Implementations for AttributeVisitor.
145
146    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
147
148
149    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
150    {
151        // The minimum variable size is determined by the arguments.
152        int maxLocals =
153            ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
154                                                  method.getAccessFlags());
155
156        if (codeAttribute.u2maxLocals < maxLocals)
157        {
158            codeAttribute.u2maxLocals = maxLocals;
159        }
160    }
161
162
163    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
164    {
165        String descriptor      = method.getDescriptor(clazz);
166        int    descriptorIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
167        String signature       = clazz.getString(signatureAttribute.u2signatureIndex);
168        int    signatureIndex  = signature.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
169
170        String newSignature = signature.substring(0, signatureIndex) +
171                              descriptor.charAt(descriptorIndex - 1) +
172                              signature.substring(signatureIndex);
173
174        // Update the signature.
175        signatureAttribute.u2signatureIndex =
176            new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
177    }
178
179
180    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
181    {
182        // Update the number of parameters.
183        int oldParametersCount = parameterAnnotationsAttribute.u2parametersCount++;
184
185        if (parameterAnnotationsAttribute.u2parameterAnnotationsCount == null ||
186            parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u2parametersCount)
187        {
188            int[]          annotationsCounts = new int[parameterAnnotationsAttribute.u2parametersCount];
189            Annotation[][] annotations       = new Annotation[parameterAnnotationsAttribute.u2parametersCount][];
190
191            System.arraycopy(parameterAnnotationsAttribute.u2parameterAnnotationsCount,
192                             0,
193                             annotationsCounts,
194                             0,
195                             oldParametersCount);
196
197            System.arraycopy(parameterAnnotationsAttribute.parameterAnnotations,
198                             0,
199                             annotations,
200                             0,
201                             oldParametersCount);
202
203            parameterAnnotationsAttribute.u2parameterAnnotationsCount = annotationsCounts;
204            parameterAnnotationsAttribute.parameterAnnotations        = annotations;
205        }
206    }
207}