package udg.useDefAnalysis;
import java.util.Collection;
import java.util.EmptyStackException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import udg.ASTProvider;
import udg.useDefAnalysis.environments.ArgumentEnvironment;
import udg.useDefAnalysis.environments.ArrayIndexingEnvironment;
import udg.useDefAnalysis.environments.AssignmentEnvironment;
import udg.useDefAnalysis.environments.CallEnvironment;
import udg.useDefAnalysis.environments.DeclEnvironment;
import udg.useDefAnalysis.environments.IdentifierEnvironment;
import udg.useDefAnalysis.environments.IncDecEnvironment;
import udg.useDefAnalysis.environments.MemberAccessEnvironment;
import udg.useDefAnalysis.environments.PtrMemberAccessEnvironment;
import udg.useDefAnalysis.environments.UnaryOpEnvironment;
import udg.useDefAnalysis.environments.UseDefEnvironment;
import udg.useDefAnalysis.environments.UseEnvironment;
import udg.useDefGraph.UseOrDef;
/**
* The ASTDefUseAnalyzer determines symbol uses and definitions performed in a
* given AST. It is currently run on statement ASTs as the core step in the
* construction of the symbol graph (UDG).
* */
public class ASTDefUseAnalyzer
{
Stack<UseDefEnvironment> environmentStack = new Stack<UseDefEnvironment>();
HashSet<UseOrDef> useDefsOfBlock = new HashSet<UseOrDef>();
TaintSources taintSources = new TaintSources();
/**
* Analyze an AST to determine the symbols used and defined by each AST
* node.
*
* */
public Collection<UseOrDef> analyzeAST(ASTProvider astProvider)
{
reset();
traverseAST(astProvider);
return useDefsOfBlock;
}
/**
* Inform the ASTAnalyzer about (callee, argNum)-pairs that define their
* arguments. For example, 'recv' defines its first argument.
* */
public void addTaintSource(String callee, int argNum)
{
taintSources.add(callee, argNum);
}
private void reset()
{
environmentStack.clear();
useDefsOfBlock.clear();
}
private void traverseAST(ASTProvider astProvider)
{
UseDefEnvironment env = createUseDefEnvironment(astProvider);
env.setASTProvider(astProvider);
traverseASTChildren(astProvider, env);
}
private void traverseASTChildren(ASTProvider astProvider,
UseDefEnvironment env)
{
int numChildren = astProvider.getChildCount();
environmentStack.push(env);
for (int i = 0; i < numChildren; i++)
{
ASTProvider childProvider = astProvider.getChild(i);
traverseAST(childProvider);
Collection<UseOrDef> toEmit = env
.useOrDefsFromSymbols(childProvider);
emitUseOrDefs(toEmit);
}
environmentStack.pop();
reportUpstream(env);
}
/**
* Creates a UseDefEnvironment for a given AST node.
* */
private UseDefEnvironment createUseDefEnvironment(ASTProvider astProvider)
{
String nodeType = astProvider.getTypeAsString();
switch (nodeType)
{
case "AssignmentExpr":
return new AssignmentEnvironment();
case "IncDecOp":
return new IncDecEnvironment();
case "IdentifierDecl":
case "Parameter":
return new DeclEnvironment();
case "CallExpression":
return createCallEnvironment(astProvider);
case "Argument":
return createArgumentEnvironment(astProvider);
case "PtrMemberAccess":
return new PtrMemberAccessEnvironment();
case "MemberAccess":
return new MemberAccessEnvironment();
case "Condition":
case "ReturnStatement":
return new UseEnvironment();
case "ArrayIndexing":
return new ArrayIndexingEnvironment();
case "UnaryOp":
return new UnaryOpEnvironment();
case "Identifier":
return new IdentifierEnvironment();
default:
return new UseDefEnvironment();
}
}
private UseDefEnvironment createCallEnvironment(ASTProvider astProvider)
{
CallEnvironment callEnv = new CallEnvironment();
// inform calls of any arguments it might taint
String callee = astProvider.getChild(0).getEscapedCodeStr();
if (taintSources.isTaintSource(callee))
{
List<Integer> taintedArgs = taintSources
.getTaintedArgsForCallee(callee);
callEnv.setTaintedArgs(taintedArgs);
}
return callEnv;
}
private ArgumentEnvironment createArgumentEnvironment(
ASTProvider astProvider)
{
ArgumentEnvironment argEnv = new ArgumentEnvironment();
CallEnvironment callEnv = (CallEnvironment) environmentStack
.get(environmentStack.size() - 2);
if (callEnv.isArgumentTainted(astProvider.getChildNumber()))
argEnv.setIsTainted();
return argEnv;
}
/**
* Gets upstream symbols from environment and passes them to
* parent-environment by calling addChildSymbols on the parent. Asks
* parent-environment to generate useOrDefs and emit them.
* */
private void reportUpstream(UseDefEnvironment env)
{
LinkedList<String> symbols = env.upstreamSymbols();
ASTProvider astProvider = env.getASTProvider();
try
{
UseDefEnvironment parentEnv = environmentStack.peek();
parentEnv.addChildSymbols(symbols, astProvider);
}
catch (EmptyStackException ex)
{
// stack is empty, we've reached the root.
// Nothing to do.
}
}
private void emitUseOrDefs(Collection<UseOrDef> toEmit)
{
for (UseOrDef useOrDef : toEmit)
useDefsOfBlock.add(useOrDef);
}
}