1
/* $Id: MemberInfoLinker.java,v 1.9 2004/11/20 15:41:24 eric Exp $
3
* ProGuard -- shrinking, optimization, and obfuscation of Java class files.
5
* Copyright (c) 2002-2004 Eric Lafortune (eric@graphics.cornell.edu)
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)
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
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
21
package proguard.obfuscate;
23
import proguard.classfile.*;
24
import proguard.classfile.visitor.*;
30
* This ClassFileVisitor links all methods that should get the same names
31
* in the name spaces of all visited class files. A class file's name space
32
* encompasses all of its subclasses and interfaces. It is typically a class file
33
* that is not being subclassed. Chains of links that have been created in
34
* previous invocations are merged with new chains of links, in order to create
35
* a consistent set of chains. Class initialization methods and constructors are
38
* @see MemberInfoObfuscator
40
* @author Eric Lafortune
42
public class MemberInfoLinker
43
implements ClassFileVisitor,
46
// An object that is reset and reused every time.
47
// The map: [class member name+descriptor - class member info]
48
private final Map methodInfoMap = new HashMap();
51
// Implementations for ClassFileVisitor.
53
public void visitProgramClassFile(ProgramClassFile programClassFile)
55
// Collect all members in this class's name space.
56
programClassFile.hierarchyAccept(true, true, true, false,
57
new AllMemberInfoVisitor(this));
59
// Clean up for obfuscation of the next name space.
60
methodInfoMap.clear();
64
public void visitLibraryClassFile(LibraryClassFile libraryClassFile)
69
// Implementations for MemberInfoVisitor.
71
public void visitProgramFieldInfo(ProgramClassFile programClassFile, ProgramFieldInfo programFieldInfo)
76
public void visitProgramMethodInfo(ProgramClassFile programClassFile, ProgramMethodInfo programMethodInfo)
78
visitMethodInfo(programClassFile, programMethodInfo);
82
public void visitLibraryFieldInfo(LibraryClassFile libraryClassFile, LibraryFieldInfo libraryFieldInfo)
87
public void visitLibraryMethodInfo(LibraryClassFile libraryClassFile, LibraryMethodInfo libraryMethodInfo)
89
visitMethodInfo(libraryClassFile, libraryMethodInfo);
94
* Links the given method into the chains of links. Class initialization
95
* methods and constructors are ignored.
96
* @param classFile the class file of the given method.
97
* @param methodInfo the method to be linked.
99
private void visitMethodInfo(ClassFile classFile, MethodInfo methodInfo)
101
// Private methods don't have to be linked.
102
if ((methodInfo.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) != 0)
107
// Get the method's original name and descriptor.
108
String name = methodInfo.getName(classFile);
109
String descriptor = methodInfo.getDescriptor(classFile);
111
// Special cases: <clinit> and <init> are always kept unchanged.
112
// We can ignore them here.
113
if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
114
name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
119
// Get the last method in the chain.
120
MemberInfo thisLastMemberInfo = lastMemberInfo(methodInfo);
122
// See if we've already come across a method with the same name and
124
String key = name + descriptor;
125
MethodInfo otherMethodInfo = (MethodInfo)methodInfoMap.get(key);
127
if (otherMethodInfo == null)
129
// Store the new class method info in the map.
130
methodInfoMap.put(key, thisLastMemberInfo);
134
// Get the last method in the other chain.
135
MemberInfo otherLastMemberInfo = lastMemberInfo(otherMethodInfo);
137
// Check if both link chains aren't already ending in the same element.
138
if (thisLastMemberInfo != otherLastMemberInfo)
140
// Merge the two chains, making sure LibraryMethodInfo elements,
141
// if any, are at the end of the resulting chain.
142
if (thisLastMemberInfo instanceof LibraryMethodInfo)
144
// This class method chain ends with a library class method.
145
// Link this chain to the end of the other one.
146
otherLastMemberInfo.setVisitorInfo(thisLastMemberInfo);
148
/* We can skip this test and go straight to the final case.
149
else if (otherLastVisitorAccepter instanceof LibraryMethodInfo)
151
// The other method chain ends with a library class method.
152
// Link the other chain to the end of this one.
153
thisLastVisitorAccepter.setVisitorInfo(otherLastVisitorAccepter);
158
// We have two non-library methods. Link their chains
159
// one way or another.
160
thisLastMemberInfo.setVisitorInfo(otherLastMemberInfo);
167
// Small utility methods.
170
* Finds the last class member in the linked list of class members.
171
* @param memberInfo the given class member.
172
* @return the last class member in the linked list.
174
static MemberInfo lastMemberInfo(MemberInfo memberInfo)
176
VisitorAccepter lastVisitorAccepter = memberInfo;
177
while (lastVisitorAccepter.getVisitorInfo() != null &&
178
lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter)
180
lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo();
183
return (MemberInfo)lastVisitorAccepter;