/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package proguard.optimize; import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.editor.ConstantPoolEditor; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; /** * This MemberVisitor adds an additional parameter to the duplicate * initialization methods that it visits. */ public class DuplicateInitializerFixer extends SimplifiedVisitor implements MemberVisitor, AttributeVisitor { private static final boolean DEBUG = false; private static final char[] TYPES = new char[] { ClassConstants.TYPE_BYTE, ClassConstants.TYPE_CHAR, ClassConstants.TYPE_SHORT, ClassConstants.TYPE_INT, ClassConstants.TYPE_BOOLEAN }; private final MemberVisitor extraFixedInitializerVisitor; /** * Creates a new DuplicateInitializerFixer. */ public DuplicateInitializerFixer() { this(null); } /** * Creates a new DuplicateInitializerFixer with an extra visitor. * @param extraFixedInitializerVisitor an optional extra visitor for all * initializers that have been fixed. */ public DuplicateInitializerFixer(MemberVisitor extraFixedInitializerVisitor) { this.extraFixedInitializerVisitor = extraFixedInitializerVisitor; } // Implementations for MemberVisitor. public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { // Is it a class instance initializer? String name = programMethod.getName(programClass); if (name.equals(ClassConstants.METHOD_NAME_INIT)) { // Is there already another initializer with the same descriptor? String descriptor = programMethod.getDescriptor(programClass); Method similarMethod = programClass.findMethod(name, descriptor); if (!programMethod.equals(similarMethod)) { // Should this initializer be preserved? if (KeepMarker.isKept(programMethod)) { // Fix the other initializer. programMethod = (ProgramMethod)similarMethod; } int index = descriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE); // Try to find a new, unique descriptor. int typeCounter = 0; while (true) { // Construct the new descriptor by inserting a new type // as an additional last argument. StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.substring(0, index)); for (int arrayDimension = 0; arrayDimension < typeCounter / TYPES.length; arrayDimension++) { newDescriptorBuffer.append(ClassConstants.TYPE_ARRAY); } newDescriptorBuffer.append(TYPES[typeCounter % TYPES.length]); newDescriptorBuffer.append(descriptor.substring(index)); String newDescriptor = newDescriptorBuffer.toString(); // Is the new initializer descriptor unique? if (programClass.findMethod(name, newDescriptor) == null) { if (DEBUG) { System.out.println("DuplicateInitializerFixer:"); System.out.println(" ["+programClass.getName()+"."+name+descriptor+"] ("+ClassUtil.externalClassAccessFlags(programMethod.getAccessFlags())+") -> ["+newDescriptor+"]"); } // Update the descriptor. programMethod.u2descriptorIndex = new ConstantPoolEditor(programClass).addUtf8Constant(newDescriptor); // Fix the local variable frame size, the method // signature, and the parameter annotations, if // necessary. programMethod.attributesAccept(programClass, this); // Visit the initializer, if required. if (extraFixedInitializerVisitor != null) { extraFixedInitializerVisitor.visitProgramMethod(programClass, programMethod); } // We're done with this constructor. return; } typeCounter++; } } } } // Implementations for AttributeVisitor. public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // The minimum variable size is determined by the arguments. int maxLocals = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), method.getAccessFlags()); if (codeAttribute.u2maxLocals < maxLocals) { codeAttribute.u2maxLocals = maxLocals; } } public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) { String descriptor = method.getDescriptor(clazz); int descriptorIndex = descriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE); String signature = signatureAttribute.getSignature(clazz); int signatureIndex = signature.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE); String newSignature = signature.substring(0, signatureIndex) + descriptor.charAt(descriptorIndex - 1) + signature.substring(signatureIndex); // Update the signature. signatureAttribute.u2signatureIndex = new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); } public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) { // Update the number of parameters. int oldParametersCount = parameterAnnotationsAttribute.u1parametersCount++; if (parameterAnnotationsAttribute.u2parameterAnnotationsCount == null || parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u1parametersCount) { int[] annotationsCounts = new int[parameterAnnotationsAttribute.u1parametersCount]; Annotation[][] annotations = new Annotation[parameterAnnotationsAttribute.u1parametersCount][]; System.arraycopy(parameterAnnotationsAttribute.u2parameterAnnotationsCount, 0, annotationsCounts, 0, oldParametersCount); System.arraycopy(parameterAnnotationsAttribute.parameterAnnotations, 0, annotations, 0, oldParametersCount); parameterAnnotationsAttribute.u2parameterAnnotationsCount = annotationsCounts; parameterAnnotationsAttribute.parameterAnnotations = annotations; } } }