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