1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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                int typeCounter = 0;
99                while (true)
100                {
101                    // Construct the new descriptor by inserting a new type
102                    // as an additional last argument.
103                    StringBuffer newDescriptorBuffer =
104                        new StringBuffer(descriptor.substring(0, index));
105
106                    for (int arrayDimension = 0; arrayDimension < typeCounter / TYPES.length; arrayDimension++)
107                    {
108                        newDescriptorBuffer.append(ClassConstants.INTERNAL_TYPE_ARRAY);
109                    }
110
111                    newDescriptorBuffer.append(TYPES[typeCounter % TYPES.length]);
112                    newDescriptorBuffer.append(descriptor.substring(index));
113
114                    String newDescriptor = newDescriptorBuffer.toString();
115
116                    // Is the new initializer descriptor unique?
117                    if (programClass.findMethod(name, newDescriptor) == null)
118                    {
119                        if (DEBUG)
120                        {
121                            System.out.println("DuplicateInitializerFixer:");
122                            System.out.println("  ["+programClass.getName()+"."+name+descriptor+"] ("+ClassUtil.externalClassAccessFlags(programMethod.getAccessFlags())+") -> ["+newDescriptor+"]");
123                        }
124
125                        // Update the descriptor.
126                        programMethod.u2descriptorIndex =
127                            new ConstantPoolEditor(programClass).addUtf8Constant(newDescriptor);
128
129                        // Fix the local variable frame size, the method
130                        // signature, and the parameter annotations, if
131                        // necessary.
132                        programMethod.attributesAccept(programClass,
133                                                       this);
134
135                        // Visit the initializer, if required.
136                        if (extraFixedInitializerVisitor != null)
137                        {
138                            extraFixedInitializerVisitor.visitProgramMethod(programClass, programMethod);
139                        }
140
141                        // We're done with this constructor.
142                        return;
143                    }
144
145                    typeCounter++;
146                }
147            }
148        }
149    }
150
151
152    // Implementations for AttributeVisitor.
153
154    public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
155
156
157    public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
158    {
159        // The minimum variable size is determined by the arguments.
160        int maxLocals =
161            ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz),
162                                                  method.getAccessFlags());
163
164        if (codeAttribute.u2maxLocals < maxLocals)
165        {
166            codeAttribute.u2maxLocals = maxLocals;
167        }
168    }
169
170
171    public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
172    {
173        String descriptor      = method.getDescriptor(clazz);
174        int    descriptorIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
175        String signature       = clazz.getString(signatureAttribute.u2signatureIndex);
176        int    signatureIndex  = signature.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
177
178        String newSignature = signature.substring(0, signatureIndex) +
179                              descriptor.charAt(descriptorIndex - 1) +
180                              signature.substring(signatureIndex);
181
182        // Update the signature.
183        signatureAttribute.u2signatureIndex =
184            new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
185    }
186
187
188    public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
189    {
190        // Update the number of parameters.
191        int oldParametersCount = parameterAnnotationsAttribute.u2parametersCount++;
192
193        if (parameterAnnotationsAttribute.u2parameterAnnotationsCount == null ||
194            parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u2parametersCount)
195        {
196            int[]          annotationsCounts = new int[parameterAnnotationsAttribute.u2parametersCount];
197            Annotation[][] annotations       = new Annotation[parameterAnnotationsAttribute.u2parametersCount][];
198
199            System.arraycopy(parameterAnnotationsAttribute.u2parameterAnnotationsCount,
200                             0,
201                             annotationsCounts,
202                             0,
203                             oldParametersCount);
204
205            System.arraycopy(parameterAnnotationsAttribute.parameterAnnotations,
206                             0,
207                             annotations,
208                             0,
209                             oldParametersCount);
210
211            parameterAnnotationsAttribute.u2parameterAnnotationsCount = annotationsCounts;
212            parameterAnnotationsAttribute.parameterAnnotations        = annotations;
213        }
214    }
215}