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.obfuscate;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.util.*;
25 import proguard.classfile.visitor.MemberVisitor;
26 
27 import 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  */
36 public 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      */
MemberNameConflictFixer(boolean allowAggressiveOverloading, Map descriptorMap, WarningPrinter warningPrinter, MemberObfuscator memberObfuscator)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 
visitProgramField(ProgramClass programClass, ProgramField programField)73     public void visitProgramField(ProgramClass programClass, ProgramField programField)
74     {
75         visitMember(programClass, programField, true);
76     }
77 
78 
visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)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 
visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)93     public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {}
visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)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      */
visitMember(Clazz clazz, Member member, boolean isField)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