/* * @(#)DepgenUtil.java 1.21 06/10/10 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package util; import dependenceAnalyzer.*; import java.util.Enumeration; import java.util.Vector; /* * Dependence-generation utility functions. * Specific to the interfaces defined in dependenceAnalyzer, * and to the external (String) representation of some of those things. * Actually, this the the framework for building any member- * dependence-analysis-based stuff. */ public class DepgenUtil extends LinkerUtil implements dependenceAnalyzer.MemberArcTypes { protected ClassFileFinder finder = new ClassFileFinder(); protected ClassnameFilter filter = new ClassnameFilter(); protected ClassnameFilter wholeClass; protected ClassnameFilter wholeData; protected MemberDependenceAnalyzer analyzer = new MemberDependenceAnalyzer( finder, filter ); protected MemberName stringToName( String nameAndType ){ int nameDivision = methodOff( nameAndType ); String classname = nameAndType.substring( 0, nameDivision ); String membername = nameAndType.substring( nameDivision+1 ); return new MemberName( analyzer.classByName( sanitizeClassname( classname ) ), sanitizeClassname( membername ) // may include signature. ); } protected MemberDependenceNode stringToNode( String nameAndType ){ return (MemberDependenceNode)(analyzer.addNodeByName( stringToName( nameAndType ) )); } /* * Similar to stringToName, but permits us * to name constructors (in context) without the bother of * the magic name. Thus * "java.lang.String([C)" is, in context, a synonym for * the more perfectly general "java.lang.String.<init>([C)" * We will also use the default signature if none is found. */ protected MemberName stringToConstructorName( String classAndType ){ int sigDivision = sigOff( classAndType ); String classname; String signature; if ( sigDivision == -1 ){ classname = classAndType; signature = constructorSig; } else { classname = classAndType.substring( 0, sigDivision ); signature = classAndType.substring( sigDivision ); } return new MemberName( analyzer.classByName( sanitizeClassname( classname ) ), constructorName+sanitizeClassname( signature ) ); } protected MemberDependenceNode stringToConstructorNode( String classAndType ){ return (MemberDependenceNode)(analyzer.addNodeByName( stringToConstructorName( classAndType ) )); } /* * Start with a String, return a ClassEntry. Very simple. */ protected ClassEntry stringToClass( String classname ){ return analyzer.classByName( sanitizeClassname( classname ) ); } /* * To exclude a class or package named by the given string. * If it ends in *, we treat it as a package name. * otherwise, it is a class. */ protected void excludeClass( String xclassname ){ String classname = sanitizeClassname( xclassname ); filter.includeName( classname ); } /* * This is the guts of dependence-based analysis. * It is only slightly parameterized to allow * for multiple target sets. In reality, * there will most likely be only one. * * processNode is called after we have determined that the given * node has not yet be added to our target set, but should be. * It adds the node to targetList, then processes it, * looking for other things to be added to the worklist. * It is called by processNodeList, below. */ protected void processNode( DependenceNode node, int inclusionFlag, int initIncludedFlag, int requiredMask, Vector targetlist, Vector worklist ){ if ( node.state() == DependenceNode.UNANALYZED ) analyzer.analyzeDependences( node ); if ( node.state() == DependenceNode.ERROR ) return; // cannot cope. if ( (node.flags&MemberDependenceNode.EXCLUDED) != 0 ) return; // not wanted. targetlist.addElement( node ); node.flags |= inclusionFlag; if ( node instanceof ClassEntry ){ // if we require a class, what we really require is its <clinit>, if any. ClassEntry classNode = (ClassEntry)node; MemberDependenceNode clinit = classNode.lookupMember( staticInitializerName+staticInitializerSig ); if ( clinit != null ){ if ( ( clinit.flags&requiredMask) == 0 ) worklist.addElement( clinit ); // if we were clever, we'd assign node=clinit and fall through... } // we also need its superclass ClassEntry mySuper = classNode.superClass(); if ( mySuper!= null ){ if ( (mySuper.flags&requiredMask) == 0 ) worklist.addElement( mySuper ); } // // we also believe that we need its interfaces. // I personally believe this is an incorrect requirement // by the current JVM implementation, but what can you do? // Enumeration ilist = classNode.interfaces(); while ( ilist.hasMoreElements() ){ ClassEntry iface = (ClassEntry) (ilist.nextElement()); if ( (iface.flags&requiredMask) == 0 ) worklist.addElement( iface ); } // // finally (?), if the class is named as one for which we // want-all-members or want-all-data, then put all those // things on the worklist, as appropriate. String className = (String)( classNode.name() ); boolean wantAllMembers = wholeClass.accept( null, className ); boolean wantAllData = wantAllMembers || wholeData.accept( null, className ); if ( wantAllData ){ // ... which is the OR of two conditions... Enumeration members = classNode.members(); if ( members != null ){ while ( members.hasMoreElements() ){ MemberDependenceNode mnode = (MemberDependenceNode)(members.nextElement() ); if ( wantAllMembers || ((mnode.flags&MemberDependenceNode.FIELD) != 0 ) ){ // either we want absolutely everything, or // this is data ( and we implcitly want data ) // so, either way, we want mnode. if ( (mnode.flags&requiredMask) == 0 ){ worklist.addElement( mnode ); } } } } } return; } MemberDependenceNode mNode = (MemberDependenceNode)node; // the only other kind we recognize. if ( (mNode.flags&MemberDependenceNode.OVERRIDDEN) != 0 ){ /* * Any methods which override this one, and are members of classes having constructors * loaded, must themselves be loaded. */ Enumeration overriders = mNode.overriddenBy(); while ( overriders.hasMoreElements() ){ MemberDependenceNode t = (MemberDependenceNode)(overriders.nextElement()); if ( (t.flags&requiredMask) != 0 ) continue; // already loaded. if ( (((MemberName)(t.name())).classEntry.flags&initIncludedFlag) != 0 ){ // constructor is loaded but the overriding member is not. // put it on the list. worklist.addElement( t ); } } } else if ( (mNode.flags&MemberDependenceNode.INIT) != 0 ){ // this is a constructor. // if its our first, check for necessary and overriding methods. // then note that a constructor has been loaded ClassEntry mClass = ((MemberName)(mNode.name())).classEntry; if ( ((mClass.flags & initIncludedFlag ) == 0 ) && (mClass.flags&ClassEntry.HAS_OVERRIDING) != 0 ){ Enumeration mMembers = mClass.members(); while ( mMembers.hasMoreElements() ){ MemberDependenceNode t = (MemberDependenceNode)(mMembers.nextElement()); if ( (t.flags&(MemberDependenceNode.NO_OVERRIDING|MemberDependenceNode.EXCLUDED)) != 0 ) continue; // not a candidate Enumeration tover = t.overrides(); overriders: while ( tover.hasMoreElements() ){ MemberDependenceNode u = (MemberDependenceNode)(tover.nextElement()); if ( (u.flags&requiredMask) != 0 ){ worklist.addElement( t ); break overriders; // enough! } } } mClass.flags |= initIncludedFlag; } } Enumeration e = node.dependsOn(); while ( e.hasMoreElements() ){ DependenceNode t = ((DependenceArc)(e.nextElement())).to(); if ( (t.flags&requiredMask) == 0 ){ worklist.addElement( t ); } } } /* * The main entry point to dependence analysis. * The vector of dependence nodes is used as a starting point, * and they and everything they depend on are included in the * resulting vector. This is parameterized by flags used so * we can work with multiple sets at a time. In practice, this * will probably never happen. * * Parameters are: * worklist -- a Vector of DependenceNode's for which we need to * build the dependence graphs * inclusionFlag -- a 1-bit mask. This gets ORed into the flag word * of all nodes that are required (i.e. are in the * dependence set we're * calculating. (By using different bits, we allow you * to calculate multiple sets within the same graph.) * We use this to avoid infinitely recalculating dependences * for nodes we've already visited. * initIncludedFlag -- a 1-bit mask. This gets ORed into the flag * word of all classes for which at least one constructor * is required. Is used for part of the overriding * induced implicit-dependence calculation. * requiredMask -- a multi-bit mask, which will certainly include * inclusionFlag. Will also include, for instance, a * bit noting that a node is part of an interface. This * is used to test overridden members, to see if * overriders must be required. * verbosity -- a small integer value. Currently, there is only * one message, and it is only printed if (verbosity>1) * * Return value is: * a vector of all the nodes added to the required list by this * calculation. * * Caveats: * Note that we keep a lot of state in each node's flag word. * We let the caller determine which bits we use, so it is the caller's * responsibility to choose reasonable non-interfering sets. * This algorithm might not work if some flag bits are * unexpectedly set. If necessary, use clearUserFlags, below. */ protected Vector processList( Vector worklist, int inclusionFlag, int initIncludedFlag, int requiredMask, int verbosity ){ Vector target = new Vector(); int targetsize = 0; while ( worklist.size() != 0 ){ Enumeration working = worklist.elements(); Vector newlist = new Vector(); while ( working.hasMoreElements() ){ DependenceNode node = (DependenceNode)(working.nextElement()); if ( (node.flags&requiredMask) == 0 ){ processNode( node, inclusionFlag, initIncludedFlag, requiredMask, target, newlist ); } } int newsize = target.size(); if ( verbosity > 1 ){ System.out.println(Localizer.getString("depgenutil.iteration_added_new_elements", new Integer(newsize-targetsize))); } targetsize = newsize; worklist = newlist; } return target; } /* * If we are asking multiple, hypothetical questions about * dependence sets, we might want to re-use the same membership * flags. This requires clearing them before re-use. * clearUserFlags will iterate through all classes and members, * clearing the flags given as input. * This is NOT built into the operation of processList, as multiple * questions is NOT the expected normal behavior, and has some cost. */ protected void clearUserFlags( int flagset ){ int flagmask = ~ flagset; Enumeration classlist = analyzer.allClasses(); while ( classlist.hasMoreElements() ){ ClassEntry c = (ClassEntry) (classlist.nextElement()); c.flags &= flagmask; Enumeration memberlist = c.members(); if ( memberlist == null ) continue; while ( memberlist.hasMoreElements() ){ DependenceNode m = (DependenceNode)(memberlist.nextElement()); m.flags &= flagmask; } } } }