summaryrefslogtreecommitdiff
path: root/src/proguard/obfuscate/MemberNameConflictFixer.java
blob: 00ac6f76240cd17582c05bf0b5461d3526724657 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * ProGuard -- shrinking, optimization, obfuscation, and preverification
 *             of Java bytecode.
 *
 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package proguard.obfuscate;

import proguard.classfile.*;
import proguard.classfile.util.*;
import proguard.classfile.visitor.MemberVisitor;

import java.util.Map;

/**
 * This MemberInfoVisitor solves obfuscation naming conflicts in all class
 * members that it visits. It avoids names from the given descriptor map,
 * delegating to the given obfuscator in order to get a new name if necessary.
 *
 * @author Eric Lafortune
 */
public class MemberNameConflictFixer implements MemberVisitor
{
    private final boolean          allowAggressiveOverloading;
    private final Map              descriptorMap;
    private final WarningPrinter   warningPrinter;
    private final MemberObfuscator memberObfuscator;


    /**
     * Creates a new MemberNameConflictFixer.
     * @param allowAggressiveOverloading a flag that specifies whether class
     *                                   members can be overloaded aggressively.
     * @param descriptorMap              the map of descriptors to
     *                                   [new name - old name] maps.
     * @param warningPrinter             an optional warning printer to which
     *                                   warnings about conflicting name
     *                                   mappings can be printed.
     * @param memberObfuscator           the obfuscator that can assign new
     *                                   names to members with conflicting
     *                                   names.
     */
    public MemberNameConflictFixer(boolean          allowAggressiveOverloading,
                                   Map              descriptorMap,
                                   WarningPrinter   warningPrinter,
                                   MemberObfuscator memberObfuscator)
    {
        this.allowAggressiveOverloading = allowAggressiveOverloading;
        this.descriptorMap              = descriptorMap;
        this.warningPrinter             = warningPrinter;
        this.memberObfuscator           = memberObfuscator;
    }




    // Implementations for MemberVisitor.

    public void visitProgramField(ProgramClass programClass, ProgramField programField)
    {
        visitMember(programClass, programField, true);
    }


    public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
    {
        // Special cases: <clinit> and <init> are always kept unchanged.
        // We can ignore them here.
        String name = programMethod.getName(programClass);
        if (ClassUtil.isInitializer(name))
        {
            return;
        }

        visitMember(programClass, programMethod, false);
    }


    public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {}
    public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {}


    /**
     * Obfuscates the given class member.
     * @param clazz   the class  of the given member.
     * @param member  the class member to be obfuscated.
     * @param isField specifies whether the class member is a field.
     */
    private void visitMember(Clazz   clazz,
                             Member  member,
                             boolean isField)
    {
        // Get the member's name and descriptor.
        String name       = member.getName(clazz);
        String descriptor = member.getDescriptor(clazz);

        // Check whether we're allowed to overload aggressively.
        if (!allowAggressiveOverloading)
        {
            // Trim the return argument from the descriptor if not.
            // Works for fields and methods alike.
            descriptor = descriptor.substring(0, descriptor.indexOf(')')+1);
        }

        // Get the name map.
        Map nameMap = MemberObfuscator.retrieveNameMap(descriptorMap, descriptor);

        // Get the member's new name.
        String newName = MemberObfuscator.newMemberName(member);

        // Get the expected old name for this new name.
        String previousName = (String)nameMap.get(newName);
        if (previousName != null &&
            !name.equals(previousName))
        {
            // There's a conflict! A member (with a given old name) in a
            // first namespace has received the same new name as this
            // member (with a different old name) in a second name space,
            // and now these two have to live together in this name space.
            if (MemberObfuscator.hasFixedNewMemberName(member) &&
                warningPrinter != null)
            {
                descriptor = member.getDescriptor(clazz);
                warningPrinter.print(clazz.getName(),
                                     "Warning: " + ClassUtil.externalClassName(clazz.getName()) +
                                                   (isField ?
                                                       ": field '" + ClassUtil.externalFullFieldDescription(0, name, descriptor) :
                                                       ": method '" + ClassUtil.externalFullMethodDescription(clazz.getName(), 0, name, descriptor)) +
                                     "' can't be mapped to '" + newName +
                                     "' because it would conflict with " +
                                     (isField ?
                                         "field '" :
                                         "method '" ) + previousName +
                                     "', which is already being mapped to '" + newName + "'");
            }

            // Clear the conflicting name.
            MemberObfuscator.setNewMemberName(member, null);

            // Assign a new name.
            member.accept(clazz, memberObfuscator);
        }
    }
}