• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2013 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.classfile.util;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.visitor.*;
25 
26 import java.util.*;
27 
28 /**
29  * This ClassVisitor links all corresponding non-private, non-static,
30  * non-initializer methods in the class hierarchies of all visited classes.
31  * Visited classes are typically all class files that are not being subclassed.
32  * Chains of links that have been created in previous invocations are merged
33  * with new chains of links, in order to create a consistent set of chains.
34  *
35  * @author Eric Lafortune
36  */
37 public class MethodLinker
38 extends      SimplifiedVisitor
39 implements   ClassVisitor,
40              MemberVisitor
41 {
42     // An object that is reset and reused every time.
43     // The map: [class member name+' '+descriptor - class member info]
44     private final Map memberMap = new HashMap();
45 
46 
47     // Implementations for ClassVisitor.
48 
visitAnyClass(Clazz clazz)49     public void visitAnyClass(Clazz clazz)
50     {
51         // Collect all non-private members in this class hierarchy.
52         clazz.hierarchyAccept(true, true, true, false,
53             new AllMethodVisitor(
54             new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC,
55             this)));
56 
57         // Clean up for the next class hierarchy.
58         memberMap.clear();
59     }
60 
61 
62     // Implementations for MemberVisitor.
63 
visitAnyMember(Clazz clazz, Member member)64     public void visitAnyMember(Clazz clazz, Member member)
65     {
66         // Get the class member's name and descriptor.
67         String name       = member.getName(clazz);
68         String descriptor = member.getDescriptor(clazz);
69 
70         // Special cases: <clinit> and <init> are always kept unchanged.
71         // We can ignore them here.
72         if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ||
73             name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
74         {
75             return;
76         }
77 
78         // See if we've already come across a method with the same name and
79         // descriptor.
80         String key = name + ' ' + descriptor;
81         Member otherMember = (Member)memberMap.get(key);
82 
83         if (otherMember == null)
84         {
85             // Get the last method in the chain.
86             Member thisLastMember = lastMember(member);
87 
88             // Store the new class method in the map.
89             memberMap.put(key, thisLastMember);
90         }
91         else
92         {
93             // Link both members.
94             link(member, otherMember);
95         }
96     }
97 
98 
99     // Small utility methods.
100 
101     /**
102      * Links the two given class members.
103      */
link(Member member1, Member member2)104     private static void link(Member member1, Member member2)
105     {
106         // Get the last methods in the both chains.
107         Member lastMember1 = lastMember(member1);
108         Member lastMember2 = lastMember(member2);
109 
110         // Check if both link chains aren't already ending in the same element.
111         if (!lastMember1.equals(lastMember2))
112         {
113             // Merge the two chains, with the library members last.
114             if (lastMember2 instanceof LibraryMember)
115             {
116                 lastMember1.setVisitorInfo(lastMember2);
117             }
118             else
119             {
120                 lastMember2.setVisitorInfo(lastMember1);
121             }
122         }
123     }
124 
125 
126     /**
127      * Finds the last class member in the linked list of related class members.
128      * @param member the given class member.
129      * @return the last class member in the linked list.
130      */
lastMember(Member member)131     public static Member lastMember(Member member)
132     {
133         Member lastMember = member;
134         while (lastMember.getVisitorInfo() != null &&
135                lastMember.getVisitorInfo() instanceof Member)
136         {
137             lastMember = (Member)lastMember.getVisitorInfo();
138         }
139 
140         return lastMember;
141     }
142 
143 
144     /**
145      * Finds the last visitor accepter in the linked list of visitors.
146      * @param visitorAccepter the given method.
147      * @return the last method in the linked list.
148      */
lastVisitorAccepter(VisitorAccepter visitorAccepter)149     public static VisitorAccepter lastVisitorAccepter(VisitorAccepter visitorAccepter)
150     {
151         VisitorAccepter lastVisitorAccepter = visitorAccepter;
152         while (lastVisitorAccepter.getVisitorInfo() != null &&
153                lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter)
154         {
155             lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo();
156         }
157 
158         return lastVisitorAccepter;
159     }
160 }
161