• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
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 (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
76             name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
77         {
78             return;
79         }
80 
81         // Get the member's descriptor.
82         String descriptor = member.getDescriptor(clazz);
83 
84         // Check whether we're allowed to do aggressive overloading
85         if (!allowAggressiveOverloading)
86         {
87             // Trim the return argument from the descriptor if not.
88             // Works for fields and methods alike.
89             descriptor = descriptor.substring(0, descriptor.indexOf(')')+1);
90         }
91 
92         // Get the name map, creating a new one if necessary.
93         Map nameMap = retrieveNameMap(descriptorMap, descriptor);
94 
95         // Get the member's new name.
96         String newName = newMemberName(member);
97 
98         // Assign a new one, if necessary.
99         if (newName == null)
100         {
101             // Find an acceptable new name.
102             nameFactory.reset();
103 
104             do
105             {
106                 newName = nameFactory.nextName();
107             }
108             while (nameMap.containsKey(newName));
109 
110             // Remember not to use the new name again in this name space.
111             nameMap.put(newName, name);
112 
113             // Assign the new name.
114             setNewMemberName(member, newName);
115         }
116     }
117 
118 
119     // Small utility methods.
120 
121     /**
122      * Gets the name map, based on the given map and a given descriptor.
123      * A new empty map is created if necessary.
124      * @param descriptorMap the map of descriptors to [new name - old name] maps.
125      * @param descriptor    the class member descriptor.
126      * @return the corresponding name map.
127      */
retrieveNameMap(Map descriptorMap, String descriptor)128     static Map retrieveNameMap(Map descriptorMap, String descriptor)
129     {
130         // See if we can find the nested map with this descriptor key.
131         Map nameMap = (Map)descriptorMap.get(descriptor);
132 
133         // Create a new one if not.
134         if (nameMap == null)
135         {
136             nameMap = new HashMap();
137             descriptorMap.put(descriptor, nameMap);
138         }
139 
140         return nameMap;
141     }
142 
143 
144     /**
145      * Assigns a fixed new name to the given class member.
146      * @param member the class member.
147      * @param name   the new name.
148      */
setFixedNewMemberName(Member member, String name)149     static void setFixedNewMemberName(Member member, String name)
150     {
151         VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member);
152 
153         if (!(lastVisitorAccepter instanceof LibraryMember) &&
154             !(lastVisitorAccepter instanceof MyFixedName))
155         {
156             lastVisitorAccepter.setVisitorInfo(new MyFixedName(name));
157         }
158         else
159         {
160             lastVisitorAccepter.setVisitorInfo(name);
161         }
162     }
163 
164 
165     /**
166      * Assigns a new name to the given class member.
167      * @param member the class member.
168      * @param name   the new name.
169      */
setNewMemberName(Member member, String name)170     static void setNewMemberName(Member member, String name)
171     {
172         MethodLinker.lastVisitorAccepter(member).setVisitorInfo(name);
173     }
174 
175 
176     /**
177      * Returns whether the new name of the given class member is fixed.
178      * @param member the class member.
179      * @return whether its new name is fixed.
180      */
hasFixedNewMemberName(Member member)181     static boolean hasFixedNewMemberName(Member member)
182     {
183         VisitorAccepter lastVisitorAccepter = MethodLinker.lastVisitorAccepter(member);
184 
185         return lastVisitorAccepter instanceof LibraryMember ||
186                lastVisitorAccepter instanceof MyFixedName;
187     }
188 
189 
190     /**
191      * Retrieves the new name of the given class member.
192      * @param member the class member.
193      * @return the class member's new name, or <code>null</code> if it doesn't
194      *         have one yet.
195      */
newMemberName(Member member)196     static String newMemberName(Member member)
197     {
198         return (String)MethodLinker.lastVisitorAccepter(member).getVisitorInfo();
199     }
200 
201 
202     /**
203      * This VisitorAccepter can be used to wrap a name string, to indicate that
204      * the name is fixed.
205      */
206     private static class MyFixedName implements VisitorAccepter
207     {
208         private String newName;
209 
210 
MyFixedName(String newName)211         public MyFixedName(String newName)
212         {
213             this.newName = newName;
214         }
215 
216 
217         // Implementations for VisitorAccepter.
218 
getVisitorInfo()219         public Object getVisitorInfo()
220         {
221             return newName;
222         }
223 
224 
setVisitorInfo(Object visitorInfo)225         public void setVisitorInfo(Object visitorInfo)
226         {
227             newName = (String)visitorInfo;
228         }
229     }
230 }
231