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 */ 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 (ClassUtil.isInitializer(name)) 85 { 86 return; 87 } 88 89 visitMember(programClass, programMethod, false); 90 } 91 92 93 public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} 94 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} 95 96 97 /** 98 * Obfuscates the given class member. 99 * @param clazz the class of the given member. 100 * @param member the class member to be obfuscated. 101 * @param isField specifies whether the class member is a field. 102 */ 103 private void visitMember(Clazz clazz, 104 Member member, 105 boolean isField) 106 { 107 // Get the member's name and descriptor. 108 String name = member.getName(clazz); 109 String descriptor = member.getDescriptor(clazz); 110 111 // Check whether we're allowed to overload aggressively. 112 if (!allowAggressiveOverloading) 113 { 114 // Trim the return argument from the descriptor if not. 115 // Works for fields and methods alike. 116 descriptor = descriptor.substring(0, descriptor.indexOf(')')+1); 117 } 118 119 // Get the name map. 120 Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor); 121 122 // Get the member's new name. 123 String newName = MemberObfuscator.newMemberName(member); 124 125 // Get the expected old name for this new name. 126 String previousName = (String)nameMap.get(newName); 127 if (previousName != null && 128 !name.equals(previousName)) 129 { 130 // There's a conflict! A member (with a given old name) in a 131 // first namespace has received the same new name as this 132 // member (with a different old name) in a second name space, 133 // and now these two have to live together in this name space. 134 if (MemberObfuscator.hasFixedNewMemberName(member) && 135 warningPrinter != null) 136 { 137 descriptor = member.getDescriptor(clazz); 138 warningPrinter.print(clazz.getName(), 139 "Warning: " + ClassUtil.externalClassName(clazz.getName()) + 140 (isField ? 141 ": field '" + ClassUtil.externalFullFieldDescription(0, name, descriptor) : 142 ": method '" + ClassUtil.externalFullMethodDescription(clazz.getName(), 0, name, descriptor)) + 143 "' can't be mapped to '" + newName + 144 "' because it would conflict with " + 145 (isField ? 146 "field '" : 147 "method '" ) + previousName + 148 "', which is already being mapped to '" + newName + "'"); 149 } 150 151 // Clear the conflicting name. 152 MemberObfuscator.setNewMemberName(member, null); 153 154 // Assign a new name. 155 member.accept(clazz, memberObfuscator); 156 } 157 } 158} 159