MemberObfuscator.java revision 9f606f95f03a75961498803e24bee6799a7c0885
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.*;
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 */
40public class MemberObfuscator
41extends      SimplifiedVisitor
42implements   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     */
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
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     */
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     */
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     */
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     */
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     */
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
211        public MyFixedName(String newName)
212        {
213            this.newName = newName;
214        }
215
216
217        // Implementations for VisitorAccepter.
218
219        public Object getVisitorInfo()
220        {
221            return newName;
222        }
223
224
225        public void setVisitorInfo(Object visitorInfo)
226        {
227            newName = (String)visitorInfo;
228        }
229    }
230}
231