• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.*;
28 
29 /**
30  * This MemberVisitor obfuscates all class members that it visits.
31  * It uses names from the given name factory. At the same time, it avoids names
32  * from the given descriptor map.
33  * <p>
34  * The class members must have been linked before applying this visitor.
35  *
36  * @see MethodLinker
37  *
38  * @author Eric Lafortune
39  */
40 public class MemberObfuscator
41 extends      SimplifiedVisitor
42 implements   MemberVisitor
43 {
44     private final boolean        allowAggressiveOverloading;
45     private final NameFactory    nameFactory;
46     private final Map            descriptorMap;
47 
48 
49     /**
50      * Creates a new MemberObfuscator.
51      * @param allowAggressiveOverloading a flag that specifies whether class
52      *                                   members can be overloaded aggressively.
53      * @param nameFactory                the factory that can produce
54      *                                   obfuscated member names.
55      * @param descriptorMap              the map of descriptors to
56      *                                   [new name - old name] maps.
57      */
MemberObfuscator(boolean allowAggressiveOverloading, NameFactory nameFactory, Map descriptorMap)58     public MemberObfuscator(boolean        allowAggressiveOverloading,
59                             NameFactory    nameFactory,
60                             Map            descriptorMap)
61     {
62         this.allowAggressiveOverloading = allowAggressiveOverloading;
63         this.nameFactory                = nameFactory;
64         this.descriptorMap              = descriptorMap;
65     }
66 
67 
68     // Implementations for MemberVisitor.
69 
visitAnyMember(Clazz clazz, Member member)70     public void visitAnyMember(Clazz clazz, Member member)
71     {
72         // Special cases: <clinit> and <init> are always kept unchanged.
73         // We can ignore them here.
74         String name = member.getName(clazz);
75         if (ClassUtil.isInitializer(name))
76         {
77             return;
78         }
79 
80         // Get the member's descriptor.
81         String descriptor = member.getDescriptor(clazz);
82 
83         // Check whether we're allowed to do aggressive overloading
84         if (!allowAggressiveOverloading)
85         {
86             // Trim the return argument from the descriptor if not.
87             // Works for fields and methods alike.
88             descriptor = descriptor.substring(0, descriptor.indexOf(')')+1);
89         }
90 
91         // Get the name map, creating a new one if necessary.
92         Map nameMap = retrieveNameMap(descriptorMap, descriptor);
93 
94         // Get the member's new name.
95         String newName = newMemberName(member);
96 
97         // Assign a new one, if necessary.
98         if (newName == null)
99         {
100             // Find an acceptable new name.
101             nameFactory.reset();
102 
103             do
104             {
105                 newName = nameFactory.nextName();
106             }
107             while (nameMap.containsKey(newName));
108 
109             // Remember not to use the new name again in this name space.
110             nameMap.put(newName, name);
111 
112             // Assign the new name.
113             setNewMemberName(member, newName);
114         }
115     }
116 
117 
118     // Small utility methods.
119 
120     /**
121      * Gets the name map, based on the given map and a given descriptor.
122      * A new empty map is created if necessary.
123      * @param descriptorMap the map of descriptors to [new name - old name] maps.
124      * @param descriptor    the class member descriptor.
125      * @return the corresponding name map.
126      */
retrieveNameMap(Map descriptorMap, String descriptor)127     static Map retrieveNameMap(Map descriptorMap, String descriptor)
128     {
129         // See if we can find the nested map with this descriptor key.
130         Map nameMap = (Map)descriptorMap.get(descriptor);
131 
132         // Create a new one if not.
133         if (nameMap == null)
134         {
135             nameMap = new HashMap();
136             descriptorMap.put(descriptor, nameMap);
137         }
138 
139         return nameMap;
140     }
141 
142 
143     /**
144      * Assigns a fixed new name to the given class member.
145      * @param member the class member.
146      * @param name   the new name.
147      */
setFixedNewMemberName(Member member, String name)148     static void setFixedNewMemberName(Member member, String name)
149     {
150         VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member);
151 
152         if (!(lastVisitorAccepter instanceof LibraryMember) &&
153             !(lastVisitorAccepter instanceof MyFixedName))
154         {
155             lastVisitorAccepter.setVisitorInfo(new MyFixedName(name));
156         }
157         else
158         {
159             lastVisitorAccepter.setVisitorInfo(name);
160         }
161     }
162 
163 
164     /**
165      * Assigns a new name to the given class member.
166      * @param member the class member.
167      * @param name   the new name.
168      */
setNewMemberName(Member member, String name)169     static void setNewMemberName(Member member, String name)
170     {
171         MethodLinker.lastVisitorAccepter(member).setVisitorInfo(name);
172     }
173 
174 
175     /**
176      * Returns whether the new name of the given class member is fixed.
177      * @param member the class member.
178      * @return whether its new name is fixed.
179      */
hasFixedNewMemberName(Member member)180     static boolean hasFixedNewMemberName(Member member)
181     {
182         VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member);
183 
184         return lastVisitorAccepter instanceof LibraryMember ||
185                lastVisitorAccepter instanceof MyFixedName;
186     }
187 
188 
189     /**
190      * Retrieves the new name of the given class member.
191      * @param member the class member.
192      * @return the class member's new name, or <code>null</code> if it doesn't
193      *         have one yet.
194      */
newMemberName(Member member)195     static String newMemberName(Member member)
196     {
197         return (String)MethodLinker.lastVisitorAccepter(member).getVisitorInfo();
198     }
199 
200 
201     /**
202      * This VisitorAccepter can be used to wrap a name string, to indicate that
203      * the name is fixed.
204      */
205     private static class MyFixedName implements VisitorAccepter
206     {
207         private String newName;
208 
209 
MyFixedName(String newName)210         public MyFixedName(String newName)
211         {
212             this.newName = newName;
213         }
214 
215 
216         // Implementations for VisitorAccepter.
217 
getVisitorInfo()218         public Object getVisitorInfo()
219         {
220             return newName;
221         }
222 
223 
setVisitorInfo(Object visitorInfo)224         public void setVisitorInfo(Object visitorInfo)
225         {
226             newName = (String)visitorInfo;
227         }
228     }
229 }
230