/*
* @(#)ClassEntry.java 1.27 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 dependenceAnalyzer;
import util.*;
import java.util.*;
import components.*;
import consts.Const;
/*
* Class ClassEntry is for use with Member-level dependence analysis.
* It is not only a part of the dependence relations but also
* a container for MemberDependenceNodes, and thus
* a part of the naming mechanism.
*
* This is where class members are parsed, and thus
* their dependences discovered and exposed.
*/
public class ClassEntry extends DependenceNode implements MemberArcTypes {
/*
* public parts.
*/
public boolean signatureFlag = false;
public ClassEntry superClass(){ return superClassEntry; }
public Enumeration subclasses(){
return (subclassSet == null ) ? EmptyEnumeration.instance : subclassSet.elements();
}
public Enumeration members(){
return (memberSet == null ) ? EmptyEnumeration.instance : memberSet.elements();
}
public Enumeration interfaces(){
return (interfaceSet == null ) ? EmptyEnumeration.instance : interfaceSet.elements();
}
public MemberDependenceNode lookupMember( MemberName mn ){
if ( memberSet == null ) return null;
return (MemberDependenceNode)(memberSet.get( mn ));
}
private MemberName t = new MemberName( this, "" );
public MemberDependenceNode lookupMember( String mname ){
// ... won't work multithreaded!...
t.name = mname.intern();
return lookupMember( t );
}
public MemberDependenceNode lookupAddMember( MemberName mn, int mflags ){
MemberDependenceNode mnode = lookupMember( mn );
if ( mnode == null ){
mnode = new MemberDependenceNode( mn, mflags );
if ( memberSet == null )
memberSet = new Hashtable();
memberSet.put( mn, mnode );
} else if ( mflags != 0 ){
mnode.flags = mflags;
}
if ( (this.flags&EXCLUDED) != 0 ){
// class is excluded, and so are all members.
mnode.flags |= MemberDependenceNode.EXCLUDED;
}
return mnode;
}
public ClassEntry( String name ){
super( name );
className = name;
nodeState = UNANALYZED;
}
public boolean analyzeClass( ClassDictionary cd, boolean f ){
signatureFlag = f;
return (analyzeClass(cd));
}
public boolean analyzeClass( ClassDictionary cd ){
if ( nodeState != UNANALYZED ) return(nodeState!=ERROR); // already done.
nodeState = ANALYZED; // do this early to prevent excess recursion
cdict = cd; // share the dictionary with the rest of the code.
ClassInfo ci = cdict.findClassInfo( className );
if ( ci == null ){
// cannot find class info to analyze.
// this is not good.
nodeState = ERROR;
} else {
nodeState = parseClassInfo( ci );
}
return (nodeState!=ERROR);
}
/*
* A class which is marked as an interface does not have to be
* a Java TM Interface. It merely has to define a contract between
* two software subsystems. java.applet.Applet is a good example of
* an interface.
* An interface is always EXCLUDED.
* An interface has additional, imputed, members, which
* are inhereted from superclasses and interfaces, and which
* form part of the contract. makeInterface creates these after
* inspecting all superclasses and Java interfaces. This is so that
* our override-based dependence analysis works.
* makeInterface returns true on success, false on any adversary
* Adversary includes:
* inability to find class file
* class is not EXCLUDED
* inability to find superclasses or interfaces
*/
public boolean makeInterface( ClassDictionary cd ){
cdict = cd;
if ( nodeState == UNANALYZED )
analyzeClass( cd );
if ( nodeState == ERROR )
return false;
if ( ( flags & EXCLUDED ) == 0 ) return false;
if ( ( flags & DEFINES_INTERFACE ) != 0 ) return true; // done!
if ( ! imputeMembers( superClassEntry )) return false;
if ( interfaceSet != null ){
Enumeration ilist = interfaceSet.elements();
while ( ilist.hasMoreElements() ){
if ( ! imputeMembers( (ClassEntry)(ilist.nextElement()) ) )
return false;
}
}
flags |= DEFINES_INTERFACE;
return true;
}
/*
* Flags. This is pretty much up to the user, except for this one
* which will govern how things work.
*/
public static final int EXCLUDED = MemberDependenceNode.EXCLUDED;
public static final int DEFINES_INTERFACE = 2;
public static final int HAS_OVERRIDING = 4;
/*
* Implementation parts.
*/
private String className;
private ClassEntry superClassEntry;
private Vector subclassSet;
private Dictionary memberSet;
private Vector interfaceSet = new Vector();
//
// I feel rather silly having this here, as there is
// no requirement that a ClassEntry be associated with only
// one dictionary. But the fact is, this variable is
// referenced in many many places in the analysis routines,
// and I'd otherwise have to keep passing it around...
private ClassDictionary cdict;
private int
parseClassInfo( ClassInfo ci ){
//
// This class cannot be understood without
// first understanding its super class
//
int resultState = ANALYZED;
if ( ci.superClass != null ){
ClassEntry sup = cdict.lookupAdd( ci.superClass.name.string );
if ( sup.nodeState == UNANALYZED )
sup.analyzeClass( cdict ); // recurse.
if ( sup.nodeState != ERROR ){
superClassEntry = sup;
sup.addSubclass( this );
}
}
// interfaces, if any.
if ( ci.interfaces != null ){
for ( int i = 0; i < ci.interfaces.length; i++ ){
ClassEntry sup = cdict.lookupAdd( ci.interfaces[i].name.string );
interfaceSet.addElement( sup );
sup.addSubclass( this );
if ( sup.nodeState == UNANALYZED )
sup.analyzeClass( cdict ); // recurse.
}
}
// Take Care the Inner class as well.
if ( ci.classAttributes != null ){
for ( int i = 0; i < ci.classAttributes.length; i++ ){
if (ci.classAttributes[i].name.string.equals("InnerClasses")) {
for (int j = 0; j < ci.innerClassAttr.getInnerClassCount();
j++) {
String name = ci.innerClassAttr.getFullName(j);
if (name.equals("ICA-ReferingToSelf"))
continue;
ClassEntry inner = cdict.lookupAdd( name );
if ( inner.nodeState == UNANALYZED )
// if -signatureFlag is set, get api dependency as well.
inner.analyzeClass( cdict, signatureFlag); // recurse.
}
}
}
}
MemberDependenceNode mnode;
// all the methods.
if ( ci.methods != null ){
for ( int i = 0; i < ci.methods.length; i++ ){
mnode = overrideAnalysis( parseMethodInfo( ci.methods[i] ) );
if ( mnode == null ){
resultState = ERROR; // bad news.
} else {
mnode.nodeState = DependenceNode.ANALYZED;
}
}
}
// and the fields.
if ( ci.fields != null ){
for ( int i = 0; i < ci.fields.length; i++ ){
mnode = parseFieldInfo( ci.fields[i] );
if ( mnode == null ){
resultState = ERROR; // bad news.
} else {
mnode.nodeState = DependenceNode.ANALYZED;
}
}
}
return resultState;
}
private MemberDependenceNode overrideAnalysis( MemberDependenceNode mn ){
if ( mn == null ) return null; // must have been some error
if ( (mn.flags&MemberDependenceNode.NO_OVERRIDING) != 0 )
return mn; // these don't override.
MemberName nm = (MemberName)((MemberName)(mn.name())).clone(); // make a dirty copy.
int anyOverriding = 0;
if ( superClassEntry != null )
anyOverriding += superClassEntry.overrideCheckUp( mn, nm );
Enumeration ife = interfaceSet.elements();
while( ife.hasMoreElements() ){
ClassEntry iface = (ClassEntry)(ife.nextElement() );
anyOverriding += iface.overrideCheckUp( mn, nm );
}
if ( anyOverriding != 0 ){
this.flags |= HAS_OVERRIDING;
}
return mn;
}
private int overrideCheckUp( MemberDependenceNode mn,
MemberName nameCopy ){
if ( this.nodeState != ANALYZED ) return 0; // error or unanalyzed interface
int nOverrides = 0;
nameCopy.classEntry = this;
MemberDependenceNode target = lookupMember( nameCopy );
if ( target != null ){
// have a case of overriding.
// various sets may not exist yet, so be careful.
if ( target.memberOverriddenBy == null ){
target.memberOverriddenBy = new util.Set();
}
target.memberOverriddenBy.addElement( mn );
target.flags |= MemberDependenceNode.OVERRIDDEN;
if ( mn.memberOverrides == null ){
mn.memberOverrides = new util.Set();
}
mn.memberOverrides.addElement( target );
nOverrides += 1;
}
if ( superClassEntry != null )
nOverrides += superClassEntry.overrideCheckUp( mn, nameCopy );
Enumeration ife = interfaceSet.elements();
while( ife.hasMoreElements() ){
ClassEntry iface = (ClassEntry)(ife.nextElement() );
nOverrides += iface.overrideCheckUp( mn, nameCopy );
}
return nOverrides;
}
private static int getUnsignedShort( byte code[], int w ){
return (( (int)code[w] &0xff ) << 8 ) | ( (int)code[w+1] &0xff );
}
private ClassEntry conditionalClassReference( MemberDependenceNode referring, ClassConstant ref, int referenceType ){
String classname = ref.name.string;
if ( className.equals( classname ) ){
return this;
}
ClassEntry cer = cdict.lookupAdd( classname );
DependenceArc de = new DependenceArc( referring, cer, referenceType );
referring.nodeDependsOn.addElement( de );
cer.nodeDependedOn.addElement( de );
return cer;
}
private void addCrossReference( MemberDependenceNode referring, FMIrefConstant fref, int arctype ){
ClassEntry targetClass = conditionalClassReference( referring, fref.clas, ARC_CLASS );
String refname;
if ( arctype == ARC_CALLS ){
// for methods, name includes signature.
refname = fref.sig.name.string+fref.sig.type.string;
} else {
// for data, name is just name.
refname = fref.sig.name.string;
}
MemberDependenceNode targetNode =
targetClass.lookupAddMember(
new MemberName( targetClass, refname ),
0
);
DependenceArc de = new DependenceArc( referring, targetNode, arctype );
referring.nodeDependsOn.addElement( de );
targetNode.nodeDependedOn.addElement( de );
}
private ClassEntry javaLangString;
private MemberDependenceNode stringInit;
private void makeString( MemberDependenceNode referring ){
if ( javaLangString == null ){
javaLangString = cdict.lookupAdd( "java/lang/String" );
stringInit = javaLangString.lookupAddMember(
new MemberName( javaLangString, "<init>([C)V" ),
0
);
}
DependenceArc da = new DependenceArc( referring, stringInit, ARC_CALLS );
referring.nodeDependsOn.addElement( da );
stringInit.nodeDependedOn.addElement( da );
}
private void doSignatureDepend(String str, MemberDependenceNode referring) {
ParamSignatureParser csi;
csi = new ParamSignatureParser(str, referring);
try {
csi.iterate();
} catch ( util.DataFormatException e ){
e.printStackTrace();
}
}
private MemberDependenceNode
parseMethodInfo( MethodInfo mi ){
MemberDependenceNode md = parseClassMemberInfo( mi, MemberDependenceNode.METHOD );
if ( (md.flags&MemberDependenceNode.EXCLUDED ) != 0 ){
return md; // we have all we need.
}
/* Should set an option -listSignatureDepend to turn this on. how?? */
if (signatureFlag)
doSignatureDepend(mi.type.string, md);
if ( mi.exceptionTable != null ){
for ( int j = 0; j < mi.exceptionTable.length; j++ ){
ExceptionEntry ee = mi.exceptionTable[j];
if ( ee.catchType != null )
conditionalClassReference( md, ee.catchType, ARC_EXCEPTION );
}
}
if ( mi.exceptionsThrown != null ){
for ( int j = 0; j < mi.exceptionsThrown.length; j++ ){
conditionalClassReference( md, mi.exceptionsThrown[j], ARC_EXCEPTION );
}
}
doCode:
if ( mi.code != null ){
byte code[] = mi.code;
try {
int locs[] = mi.getLdcInstructions();
ConstantObject cpool[] =
mi.parent.getConstantPool().getConstants();
for ( int j = 0; j < locs.length; j++ ){
ConstantObject o = cpool[ (int)code[locs[j]+1]&0xff ];
if ( o instanceof StringConstant ){
makeString( md );
} else if ( o instanceof ClassConstant ){
conditionalClassReference( md,
(ClassConstant) o, ARC_CLASS );
}
}
locs = mi.getWideConstantRefInstructions();
for ( int j = 0; j < locs.length; j++ ){
FMIrefConstant fref;
switch ( (int)code[locs[j] ] & 0xff ){
case Const.opc_anewarray:
case Const.opc_checkcast:
case Const.opc_multianewarray:
case Const.opc_new:
case Const.opc_instanceof: {
conditionalClassReference( md,
(ClassConstant) cpool[ getUnsignedShort( code, locs[j]+1 ) ], ARC_CLASS );
break;
}
case Const.opc_getfield:
case Const.opc_getstatic:
fref = (FMIrefConstant) cpool[ getUnsignedShort( code, locs[j]+1 ) ];
addCrossReference( md, fref, ARC_READS );
break;
case Const.opc_putfield:
case Const.opc_putstatic:{
fref = (FMIrefConstant) cpool[ getUnsignedShort( code, locs[j]+1 ) ];
addCrossReference( md, fref, ARC_WRITES );
break;
}
case Const.opc_ldc_w: {
ConstantObject o = cpool[ getUnsignedShort( code, locs[j]+1 ) ];
if ( o instanceof StringConstant ){
makeString( md );
} else if ( o instanceof ClassConstant ){
conditionalClassReference( md,
(ClassConstant) o, ARC_CLASS );
}
break;
}
case Const.opc_invokeinterface:
case Const.opc_invokevirtual:
case Const.opc_invokespecial:
case Const.opc_invokestatic:
fref = (FMIrefConstant) cpool[ getUnsignedShort( code, locs[j]+1 ) ];
addCrossReference( md, fref, ARC_CALLS );
break;
}
}
} catch ( Exception e ){
System.out.println(this);
e.printStackTrace();
break doCode;
}
}
return md;
}
private MemberDependenceNode parseFieldInfo( FieldInfo fi ){
return parseClassMemberInfo( fi, MemberDependenceNode.FIELD );
}
private MemberDependenceNode parseClassMemberInfo( ClassMemberInfo mi, int flagval ){
boolean isStatic = ( mi.isStaticMember() )
|| ( mi.name.string.charAt(0) == '<' );
if ( isStatic )
flagval |= MemberDependenceNode.STATIC;
if ( mi.isPrivateMember() )
flagval |= MemberDependenceNode.PRIVATE;
if ( mi.name.string.equals("<init>") )
flagval |= MemberDependenceNode.INIT;
if ( mi.name.string.equals("<clinit>") )
flagval |= MemberDependenceNode.CLINIT;
if ( (mi.access&Const.ACC_NATIVE) != 0 )
flagval |= MemberDependenceNode.NATIVE;
String memberName= mi.name.string;
if ( (flagval&MemberDependenceNode.METHOD ) != 0 )
memberName += mi.type.string;
return lookupAddMember(
new MemberName( this, memberName ),
flagval );
}
private boolean imputeMembers( ClassEntry other ){
if ( other == null ) return true; // vacuous
if ( other.nodeState == UNANALYZED )
other.analyzeClass( cdict );
if ( other.nodeState == ERROR ) return false;
if ( other.memberSet != null ){
Enumeration omembers = other.memberSet.elements();
while ( omembers.hasMoreElements() ){
MemberDependenceNode mn = (MemberDependenceNode)(omembers.nextElement());
if ( (mn.flags&MemberDependenceNode.NO_OVERRIDING) != 0 )
continue; // never mind. Not part of interface.
MemberName tname = (MemberName)(((MemberName)(mn.name())).clone());
tname.classEntry = this;
MemberDependenceNode ourmn = lookupAddMember( tname, 0 );
if ( (ourmn.flags&~EXCLUDED) == 0 ){
// no flags set. That means that we just inserted it.
ourmn.flags |= mn.flags | MemberDependenceNode.IMPUTED;
}
}
}
if ( ! imputeMembers( other.superClassEntry ) ) return false;
if ( other.interfaceSet != null ){
Enumeration ilist = other.interfaceSet.elements();
while ( ilist.hasMoreElements() ){
if ( ! imputeMembers( (ClassEntry)(ilist.nextElement()) ) )
return false;
}
}
return true;
}
private void addSubclass( ClassEntry newSub ){
if ( subclassSet == null ){
subclassSet = new Vector();
}
subclassSet.addElement( newSub );
}
private boolean hasSubclass( ClassEntry other ){
if ( subclassSet == null ) return false;
Enumeration subs = subclassSet.elements();
while ( subs.hasMoreElements() ){
if ( subs.nextElement() == other )
return true;
}
subs = subclassSet.elements();
while ( subs.hasMoreElements() ){
if ( ((ClassEntry)(subs.nextElement())).hasSubclass( other ) )
return true;
}
return false;
}
class ParamSignatureParser extends util.SignatureIterator {
String params[];
MemberDependenceNode ref;
public ParamSignatureParser( String sig, MemberDependenceNode r ){
super(sig);
ref = r;
}
public void do_array(int arrayDepth, int subTypeStart, int subTypeEnd) {
ClassEntry cer;
DependenceArc da;
if (subTypeStart == subTypeEnd)
return;
cer = cdict.lookupAdd(sig.substring(subTypeStart+1, subTypeEnd));
da = new DependenceArc(ref, cer, ARC_CLASS);
ref.nodeDependsOn.addElement(da);
cer.nodeDependedOn.addElement(da);
}
public void do_object( int subTypeStart, int subTypeEnd ) {
ClassEntry cer;
DependenceArc da;
cer = cdict.lookupAdd(sig.substring(subTypeStart+1, subTypeEnd));
da = new DependenceArc(ref, cer, ARC_CLASS);
ref.nodeDependsOn.addElement(da);
cer.nodeDependedOn.addElement(da);
}
}
}