• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
21 package proguard.optimize;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.annotation.*;
26 import proguard.classfile.attribute.visitor.AttributeVisitor;
27 import proguard.classfile.editor.ConstantPoolEditor;
28 import proguard.classfile.util.*;
29 import proguard.classfile.visitor.MemberVisitor;
30 
31 /**
32  * This MemberVisitor adds an additional parameter to the duplicate
33  * initialization methods that it visits.
34  */
35 public class DuplicateInitializerFixer
36 extends      SimplifiedVisitor
37 implements   MemberVisitor,
38              AttributeVisitor
39 {
40     private static final boolean DEBUG = false;
41 
42     private static final char[] TYPES = new char[]
43     {
44         ClassConstants.TYPE_BYTE,
45         ClassConstants.TYPE_CHAR,
46         ClassConstants.TYPE_SHORT,
47         ClassConstants.TYPE_INT,
48         ClassConstants.TYPE_BOOLEAN
49     };
50 
51 
52     private final MemberVisitor extraFixedInitializerVisitor;
53 
54 
55     /**
56      * Creates a new DuplicateInitializerFixer.
57      */
DuplicateInitializerFixer()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      */
DuplicateInitializerFixer(MemberVisitor extraFixedInitializerVisitor)69     public DuplicateInitializerFixer(MemberVisitor extraFixedInitializerVisitor)
70     {
71         this.extraFixedInitializerVisitor = extraFixedInitializerVisitor;
72     }
73 
74 
75     // Implementations for MemberVisitor.
76 
visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)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.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.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.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 
visitAnyAttribute(Clazz clazz, Attribute attribute)154     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
155 
156 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)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 
visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)171     public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
172     {
173         String descriptor      = method.getDescriptor(clazz);
174         int    descriptorIndex = descriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE);
175         String signature       = signatureAttribute.getSignature(clazz);
176         int    signatureIndex  = signature.indexOf(ClassConstants.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 
visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)188     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
189     {
190         // Update the number of parameters.
191         int oldParametersCount = parameterAnnotationsAttribute.u1parametersCount++;
192 
193         if (parameterAnnotationsAttribute.u2parameterAnnotationsCount == null ||
194             parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u1parametersCount)
195         {
196             int[]          annotationsCounts = new int[parameterAnnotationsAttribute.u1parametersCount];
197             Annotation[][] annotations       = new Annotation[parameterAnnotationsAttribute.u1parametersCount][];
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 }