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.obfuscate; 22 23import proguard.classfile.*; 24import proguard.classfile.util.*; 25import proguard.classfile.visitor.MemberVisitor; 26 27import java.util.Map; 28 29/** 30 * This MemberInfoVisitor solves obfuscation naming conflicts in all class 31 * members that it visits. It avoids names from the given descriptor map, 32 * delegating to the given obfuscator in order to get a new name if necessary. 33 * 34 * @author Eric Lafortune 35 */ 36public class MemberNameConflictFixer implements MemberVisitor 37{ 38 private final boolean allowAggressiveOverloading; 39 private final Map descriptorMap; 40 private final WarningPrinter warningPrinter; 41 private final MemberObfuscator memberObfuscator; 42 43 44 /** 45 * Creates a new MemberNameConflictFixer. 46 * @param allowAggressiveOverloading a flag that specifies whether class 47 * members can be overloaded aggressively. 48 * @param descriptorMap the map of descriptors to 49 * [new name - old name] maps. 50 * @param warningPrinter an optional warning printer to which 51 * warnings about conflicting name 52 * mappings can be printed. 53 * @param memberObfuscator the obfuscator that can assign new 54 * names to members with conflicting 55 * names. 56 */ 57 public MemberNameConflictFixer(boolean allowAggressiveOverloading, 58 Map descriptorMap, 59 WarningPrinter warningPrinter, 60 MemberObfuscator memberObfuscator) 61 { 62 this.allowAggressiveOverloading = allowAggressiveOverloading; 63 this.descriptorMap = descriptorMap; 64 this.warningPrinter = warningPrinter; 65 this.memberObfuscator = memberObfuscator; 66 } 67 68 69 70 71 // Implementations for MemberVisitor. 72 73 public void visitProgramField(ProgramClass programClass, ProgramField programField) 74 { 75 visitMember(programClass, programField, true); 76 } 77 78 79 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 80 { 81 // Special cases: <clinit> and <init> are always kept unchanged. 82 // We can ignore them here. 83 String name = programMethod.getName(programClass); 84 if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || 85 name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) 86 { 87 return; 88 } 89 90 visitMember(programClass, programMethod, false); 91 } 92 93 94 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} 95 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} 96 97 98 /** 99 * Obfuscates the given class member. 100 * @param clazz the class of the given member. 101 * @param member the class member to be obfuscated. 102 * @param isField specifies whether the class member is a field. 103 */ 104 private void visitMember(Clazz clazz, 105 Member member, 106 boolean isField) 107 { 108 // Get the member's name and descriptor. 109 String name = member.getName(clazz); 110 String descriptor = member.getDescriptor(clazz); 111 112 // Check whether we're allowed to overload aggressively. 113 if (!allowAggressiveOverloading) 114 { 115 // Trim the return argument from the descriptor if not. 116 // Works for fields and methods alike. 117 descriptor = descriptor.substring(0, descriptor.indexOf(')')+1); 118 } 119 120 // Get the name map. 121 Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor); 122 123 // Get the member's new name. 124 String newName = MemberObfuscator.newMemberName(member); 125 126 // Get the expected old name for this new name. 127 String previousName = (String)nameMap.get(newName); 128 if (previousName != null && 129 !name.equals(previousName)) 130 { 131 // There's a conflict! A member (with a given old name) in a 132 // first namespace has received the same new name as this 133 // member (with a different old name) in a second name space, 134 // and now these two have to live together in this name space. 135 if (MemberObfuscator.hasFixedNewMemberName(member) && 136 warningPrinter != null) 137 { 138 descriptor = member.getDescriptor(clazz); 139 warningPrinter.print(clazz.getName(), 140 "Warning: " + ClassUtil.externalClassName(clazz.getName()) + 141 (isField ? 142 ": field '" + ClassUtil.externalFullFieldDescription(0, name, descriptor) : 143 ": method '" + ClassUtil.externalFullMethodDescription(clazz.getName(), 0, name, descriptor)) + 144 "' can't be mapped to '" + newName + 145 "' because it would conflict with " + 146 (isField ? 147 "field '" : 148 "method '" ) + previousName + 149 "', which is already being mapped to '" + newName + "'"); 150 } 151 152 // Clear the conflicting name. 153 MemberObfuscator.setNewMemberName(member, null); 154 155 // Assign a new name. 156 member.accept(clazz, memberObfuscator); 157 } 158 } 159} 160