/* Soot - a J*va Optimization Framework
* Copyright (C) 2002 Ondrej Lhotak
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package soot.jimple.spark.builder;
import soot.jimple.spark.pag.*;
import soot.jimple.*;
import soot.*;
import soot.toolkits.scalar.Pair;
import soot.shimple.*;
/** Class implementing builder parameters (this decides
* what kinds of nodes should be built for each kind of Soot value).
* @author Ondrej Lhotak
*/
public class MethodNodeFactory extends AbstractShimpleValueSwitch {
public MethodNodeFactory( PAG pag, MethodPAG mpag ) {
this.pag = pag;
this.mpag = mpag;
setCurrentMethod( mpag.getMethod() );
}
/** Sets the method for which a graph is currently being built. */
private void setCurrentMethod( SootMethod m ) {
method = m;
if( !m.isStatic() ) {
SootClass c = m.getDeclaringClass();
if( c == null ) {
throw new RuntimeException( "Method "+m+" has no declaring class" );
}
caseThis();
}
for( int i = 0; i < m.getParameterCount(); i++ ) {
if( m.getParameterType(i) instanceof RefLikeType ) {
caseParm( i );
}
}
Type retType = m.getReturnType();
if( retType instanceof RefLikeType ) {
caseRet();
}
}
public Node getNode( Value v ) {
v.apply( this );
return getNode();
}
/** Adds the edges required for this statement to the graph. */
final public void handleStmt( Stmt s ) {
if( s.containsInvokeExpr() ) {
return;
}
s.apply( new AbstractStmtSwitch() {
final public void caseAssignStmt(AssignStmt as) {
Value l = as.getLeftOp();
Value r = as.getRightOp();
if( !( l.getType() instanceof RefLikeType ) ) return;
l.apply( MethodNodeFactory.this );
Node dest = getNode();
r.apply( MethodNodeFactory.this );
Node src = getNode();
if( l instanceof InstanceFieldRef ) {
((InstanceFieldRef) l).getBase().apply( MethodNodeFactory.this );
pag.addDereference( (VarNode) getNode() );
}
if( r instanceof InstanceFieldRef ) {
((InstanceFieldRef) r).getBase().apply( MethodNodeFactory.this );
pag.addDereference( (VarNode) getNode() );
}
if( r instanceof StaticFieldRef ) {
StaticFieldRef sfr = (StaticFieldRef) r;
SootFieldRef s = sfr.getFieldRef();
if (pag.getOpts().empties_as_allocs()) {
if (s.declaringClass().getName().equals("java.util.Collections")) {
if (s.name().equals("EMPTY_SET")) {
src = pag.makeAllocNode( RefType.v("java.util.HashSet"),
RefType.v("java.util.HashSet"), method );
} else if (s.name().equals("EMPTY_MAP")) {
src = pag.makeAllocNode( RefType.v("java.util.HashMap"),
RefType.v("java.util.HashMap"), method );
} else if (s.name().equals("EMPTY_LIST")) {
src = pag.makeAllocNode( RefType.v("java.util.LinkedList"),
RefType.v("java.util.LinkedList"), method );
}
} else if (s.declaringClass().getName().equals("java.util.Hashtable")) {
if (s.name().equals("emptyIterator")) {
src = pag.makeAllocNode( RefType.v("java.util.Hashtable$EmptyIterator"),
RefType.v("java.util.Hashtable$EmptyIterator"), method );
} else if (s.name().equals("emptyEnumerator")) {
src = pag.makeAllocNode( RefType.v("java.util.Hashtable$EmptyEnumerator"),
RefType.v("java.util.Hashtable$EmptyEnumerator"), method );
}
}
}
}
mpag.addInternalEdge( src, dest );
}
final public void caseReturnStmt(ReturnStmt rs) {
if( !( rs.getOp().getType() instanceof RefLikeType ) ) return;
rs.getOp().apply( MethodNodeFactory.this );
Node retNode = getNode();
mpag.addInternalEdge( retNode, caseRet() );
}
final public void caseIdentityStmt(IdentityStmt is) {
if( !( is.getLeftOp().getType() instanceof RefLikeType ) ) return;
is.getLeftOp().apply( MethodNodeFactory.this );
Node dest = getNode();
is.getRightOp().apply( MethodNodeFactory.this );
Node src = getNode();
mpag.addInternalEdge( src, dest );
}
final public void caseThrowStmt(ThrowStmt ts) {
ts.getOp().apply( MethodNodeFactory.this );
mpag.addOutEdge( getNode(), pag.nodeFactory().caseThrow() );
}
} );
}
final public Node getNode() {
return (Node) getResult();
}
final public Node caseThis() {
VarNode ret = pag.makeLocalVarNode(
new Pair( method, PointsToAnalysis.THIS_NODE ),
method.getDeclaringClass().getType(), method );
ret.setInterProcTarget();
return ret;
}
final public Node caseParm( int index ) {
VarNode ret = pag.makeLocalVarNode(
new Pair( method, new Integer( index ) ),
method.getParameterType( index ), method );
ret.setInterProcTarget();
return ret;
}
final public void casePhiExpr(PhiExpr e) {
Pair phiPair = new Pair( e, PointsToAnalysis.PHI_NODE );
Node phiNode = pag.makeLocalVarNode( phiPair, e.getType(), method );
for (Value op : e.getValues()) {
op.apply( MethodNodeFactory.this );
Node opNode = getNode();
mpag.addInternalEdge( opNode, phiNode );
}
setResult( phiNode );
}
final public Node caseRet() {
VarNode ret = pag.makeLocalVarNode(
Parm.v( method, PointsToAnalysis.RETURN_NODE ),
method.getReturnType(), method );
ret.setInterProcSource();
return ret;
}
final public Node caseArray( VarNode base ) {
return pag.makeFieldRefNode( base, ArrayElement.v() );
}
/* End of public methods. */
/* End of package methods. */
// OK, these ones are public, but they really shouldn't be; it's just
// that Java requires them to be, because they override those other
// public methods.
final public void caseArrayRef( ArrayRef ar ) {
caseLocal( (Local) ar.getBase() );
setResult( caseArray( (VarNode) getNode() ) );
}
final public void caseCastExpr( CastExpr ce ) {
Pair castPair = new Pair( ce, PointsToAnalysis.CAST_NODE );
ce.getOp().apply( this );
Node opNode = getNode();
Node castNode = pag.makeLocalVarNode( castPair, ce.getCastType(), method );
mpag.addInternalEdge( opNode, castNode );
setResult( castNode );
}
final public void caseCaughtExceptionRef( CaughtExceptionRef cer ) {
setResult( pag.nodeFactory().caseThrow() );
}
final public void caseInstanceFieldRef( InstanceFieldRef ifr ) {
if( pag.getOpts().field_based() || pag.getOpts().vta() ) {
setResult( pag.makeGlobalVarNode(
ifr.getField(),
ifr.getField().getType() ) );
} else {
setResult( pag.makeLocalFieldRefNode(
ifr.getBase(),
ifr.getBase().getType(),
ifr.getField(),
method ) );
}
}
final public void caseLocal( Local l ) {
setResult( pag.makeLocalVarNode( l, l.getType(), method ) );
}
final public void caseNewArrayExpr( NewArrayExpr nae ) {
setResult( pag.makeAllocNode( nae, nae.getType(), method ) );
}
private boolean isStringBuffer(Type t) {
if(!(t instanceof RefType)) return false;
RefType rt = (RefType) t;
String s = rt.toString();
if(s.equals("java.lang.StringBuffer")) return true;
if(s.equals("java.lang.StringBuilder")) return true;
return false;
}
final public void caseNewExpr( NewExpr ne ) {
if( pag.getOpts().merge_stringbuffer()
&& isStringBuffer(ne.getType())) {
setResult( pag.makeAllocNode( ne.getType(), ne.getType(), null ) );
} else {
setResult( pag.makeAllocNode( ne, ne.getType(), method ) );
}
}
final public void caseNewMultiArrayExpr( NewMultiArrayExpr nmae ) {
ArrayType type = (ArrayType) nmae.getType();
AllocNode prevAn = pag.makeAllocNode(
new Pair( nmae, new Integer( type.numDimensions ) ), type, method );
VarNode prevVn = pag.makeLocalVarNode( prevAn, prevAn.getType(), method );
mpag.addInternalEdge( prevAn, prevVn );
setResult( prevAn );
while( true ) {
Type t = type.getElementType();
if( !( t instanceof ArrayType ) ) break;
type = (ArrayType) t;
AllocNode an = pag.makeAllocNode(
new Pair( nmae, new Integer( type.numDimensions ) ), type, method );
VarNode vn = pag.makeLocalVarNode( an, an.getType(), method );
mpag.addInternalEdge( an, vn );
mpag.addInternalEdge( vn, pag.makeFieldRefNode( prevVn, ArrayElement.v() ) );
prevAn = an;
prevVn = vn;
}
}
final public void caseParameterRef( ParameterRef pr ) {
setResult( caseParm( pr.getIndex() ) );
}
final public void caseStaticFieldRef( StaticFieldRef sfr ) {
setResult( pag.makeGlobalVarNode(
sfr.getField(),
sfr.getField().getType() ) );
}
final public void caseStringConstant( StringConstant sc ) {
AllocNode stringConstant;
if( pag.getOpts().string_constants()
|| Scene.v().containsClass(sc.value)
|| ( sc.value.length() > 0 && sc.value.charAt(0) == '[' ) ) {
stringConstant = pag.makeStringConstantNode( sc.value );
} else {
stringConstant = pag.makeAllocNode(
PointsToAnalysis.STRING_NODE,
RefType.v( "java.lang.String" ), null );
}
VarNode stringConstantLocal = pag.makeGlobalVarNode(
stringConstant,
RefType.v( "java.lang.String" ) );
pag.addEdge( stringConstant, stringConstantLocal );
setResult( stringConstantLocal );
}
final public void caseThisRef( ThisRef tr ) {
setResult( caseThis() );
}
final public void caseNullConstant( NullConstant nr ) {
setResult( null );
}
final public void caseClassConstant( ClassConstant cc ) {
AllocNode classConstant = pag.makeClassConstantNode(cc);
VarNode classConstantLocal = pag.makeGlobalVarNode(
classConstant,
RefType.v( "java.lang.Class" ) );
pag.addEdge(classConstant, classConstantLocal);
setResult(classConstantLocal);
}
final public void defaultCase( Object v ) {
throw new RuntimeException( "failed to handle "+v );
}
protected PAG pag;
protected MethodPAG mpag;
protected SootMethod method;
}