1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 Eric Lafortune (eric (at) 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 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 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 */ 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 */ 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 */ 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