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}