Home | History | Annotate | Download | only in util
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 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.ACC_PRIVATE | ClassConstants.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 (ClassUtil.isInitializer(name))
     73         {
     74             return;
     75         }
     76 
     77         // See if we've already come across a method with the same name and
     78         // descriptor.
     79         String key = name + ' ' + descriptor;
     80         Member otherMember = (Member)memberMap.get(key);
     81 
     82         if (otherMember == null)
     83         {
     84             // Get the last method in the chain.
     85             Member thisLastMember = lastMember(member);
     86 
     87             // Store the new class method in the map.
     88             memberMap.put(key, thisLastMember);
     89         }
     90         else
     91         {
     92             // Link both members.
     93             link(member, otherMember);
     94         }
     95     }
     96 
     97 
     98     // Small utility methods.
     99 
    100     /**
    101      * Links the two given class members.
    102      */
    103     private static void link(Member member1, Member member2)
    104     {
    105         // Get the last methods in the both chains.
    106         Member lastMember1 = lastMember(member1);
    107         Member lastMember2 = lastMember(member2);
    108 
    109         // Check if both link chains aren't already ending in the same element.
    110         if (!lastMember1.equals(lastMember2))
    111         {
    112             // Merge the two chains, with the library members last.
    113             if (lastMember2 instanceof LibraryMember)
    114             {
    115                 lastMember1.setVisitorInfo(lastMember2);
    116             }
    117             else
    118             {
    119                 lastMember2.setVisitorInfo(lastMember1);
    120             }
    121         }
    122     }
    123 
    124 
    125     /**
    126      * Finds the last class member in the linked list of related class members.
    127      * @param member the given class member.
    128      * @return the last class member in the linked list.
    129      */
    130     public static Member lastMember(Member member)
    131     {
    132         Member lastMember = member;
    133         while (lastMember.getVisitorInfo() != null &&
    134                lastMember.getVisitorInfo() instanceof Member)
    135         {
    136             lastMember = (Member)lastMember.getVisitorInfo();
    137         }
    138 
    139         return lastMember;
    140     }
    141 
    142 
    143     /**
    144      * Finds the last visitor accepter in the linked list of visitors.
    145      * @param visitorAccepter the given method.
    146      * @return the last method in the linked list.
    147      */
    148     public static VisitorAccepter lastVisitorAccepter(VisitorAccepter visitorAccepter)
    149     {
    150         VisitorAccepter lastVisitorAccepter = visitorAccepter;
    151         while (lastVisitorAccepter.getVisitorInfo() != null &&
    152                lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter)
    153         {
    154             lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo();
    155         }
    156 
    157         return lastVisitorAccepter;
    158     }
    159 }
    160