1156 lines
36 KiB
Java
1156 lines
36 KiB
Java
/******************************************************************************
|
|
* Copyright (c) 2002 - 2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*****************************************************************************/
|
|
package com.ibm.wala.cast.ipa.callgraph;
|
|
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.Set;
|
|
|
|
import com.ibm.wala.analysis.reflection.FactoryBypassInterpreter;
|
|
import com.ibm.wala.analysis.typeInference.TypeInference;
|
|
import com.ibm.wala.cast.analysis.typeInference.AstTypeInference;
|
|
import com.ibm.wala.cast.ipa.callgraph.AstCallGraph.AstCGNode;
|
|
import com.ibm.wala.cast.ipa.callgraph.ScopeMappingInstanceKeys.ScopeMappingInstanceKey;
|
|
import com.ibm.wala.cast.ir.ssa.*;
|
|
import com.ibm.wala.cast.ir.ssa.AstIRFactory.AstIR;
|
|
import com.ibm.wala.cast.ir.ssa.AstLexicalAccess.Access;
|
|
import com.ibm.wala.cast.ir.translator.AstTranslator;
|
|
import com.ibm.wala.cast.loader.AstMethod;
|
|
import com.ibm.wala.cast.loader.AstMethod.LexicalInformation;
|
|
import com.ibm.wala.classLoader.CallSiteReference;
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.fixedpoint.impl.AbstractOperator;
|
|
import com.ibm.wala.fixedpoint.impl.UnaryOperator;
|
|
import com.ibm.wala.fixpoint.IVariable;
|
|
import com.ibm.wala.fixpoint.IntSetVariable;
|
|
import com.ibm.wala.ipa.callgraph.*;
|
|
import com.ibm.wala.ipa.callgraph.impl.ExplicitCallGraph;
|
|
import com.ibm.wala.ipa.callgraph.propagation.*;
|
|
import com.ibm.wala.ipa.callgraph.propagation.cfa.DefaultSSAInterpreter;
|
|
import com.ibm.wala.ipa.callgraph.propagation.cfa.DelegatingSSAContextInterpreter;
|
|
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
|
import com.ibm.wala.ssa.*;
|
|
import com.ibm.wala.ssa.SSACFG.BasicBlock;
|
|
import com.ibm.wala.util.Function;
|
|
import com.ibm.wala.util.collections.EmptyIterator;
|
|
import com.ibm.wala.util.collections.Pair;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
import com.ibm.wala.util.debug.Trace;
|
|
import com.ibm.wala.util.graph.traverse.NumberedDFSDiscoverTimeIterator;
|
|
import com.ibm.wala.util.intset.IntSetAction;
|
|
import com.ibm.wala.util.intset.IntSetUtil;
|
|
import com.ibm.wala.util.intset.MutableIntSet;
|
|
import com.ibm.wala.util.warnings.WarningSet;
|
|
|
|
public abstract class AstSSAPropagationCallGraphBuilder
|
|
extends SSAPropagationCallGraphBuilder
|
|
{
|
|
|
|
public static final boolean DEBUG_TYPE_INFERENCE = false;
|
|
public static final boolean DEBUG_PROPERTIES = false;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// language specialization interface
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected abstract boolean useObjectCatalog();
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// overall control
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected
|
|
AstSSAPropagationCallGraphBuilder(ClassHierarchy cha,
|
|
WarningSet warnings,
|
|
AnalysisOptions options,
|
|
PointerKeyFactory pointerKeyFactory)
|
|
{
|
|
super(cha, warnings, options, pointerKeyFactory, options.usePreTransitiveSolver());
|
|
}
|
|
|
|
public SSAContextInterpreter
|
|
makeDefaultContextInterpreters(SSAContextInterpreter appContextInterpreter,
|
|
AnalysisOptions options,
|
|
ClassHierarchy cha,
|
|
ReflectionSpecification reflect,
|
|
WarningSet warnings)
|
|
{
|
|
SSAContextInterpreter c = new DefaultSSAInterpreter(options, cha, warnings);
|
|
c = new DelegatingSSAContextInterpreter(new AstContextInsensitiveSSAContextInterpreter(options, cha), c);
|
|
|
|
c = new DelegatingSSAContextInterpreter(new FactoryBypassInterpreter(options, cha, reflect, warnings), c);
|
|
|
|
if (appContextInterpreter == null)
|
|
return c;
|
|
else
|
|
return new DelegatingSSAContextInterpreter(appContextInterpreter, c);
|
|
}
|
|
|
|
public PointerKey getPointerKeyForObjectCatalog(InstanceKey I) {
|
|
return ((AstPointerKeyFactory)pointerKeyFactory)
|
|
.getPointerKeyForObjectCatalog(I);
|
|
}
|
|
|
|
public Iterator
|
|
getPointerKeysForReflectedFieldRead(InstanceKey I, InstanceKey F)
|
|
{
|
|
return ((AstPointerKeyFactory)pointerKeyFactory)
|
|
.getPointerKeysForReflectedFieldRead(I, F);
|
|
}
|
|
|
|
public Iterator
|
|
getPointerKeysForReflectedFieldWrite(InstanceKey I, InstanceKey F)
|
|
{
|
|
return ((AstPointerKeyFactory)pointerKeyFactory)
|
|
.getPointerKeysForReflectedFieldWrite(I, F);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// specialized pointer analysis
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected class AstPointerFlowGraph extends PointerFlowGraph {
|
|
|
|
protected class AstPointerFlowVisitor
|
|
extends InstructionVisitor
|
|
implements AstInstructionVisitor
|
|
{
|
|
protected AstPointerFlowVisitor(CGNode node, IR ir, BasicBlock bb) {
|
|
super(node, ir, bb);
|
|
}
|
|
|
|
public void visitAstLexicalRead(AstLexicalRead instruction) {
|
|
|
|
}
|
|
|
|
public void visitAstLexicalWrite(AstLexicalWrite instruction) {
|
|
|
|
}
|
|
|
|
public void visitAstGlobalRead(AstGlobalRead instruction) {
|
|
|
|
}
|
|
|
|
public void visitAstGlobalWrite(AstGlobalWrite instruction) {
|
|
|
|
}
|
|
|
|
public void visitNonExceptingThrow(NonExceptingThrowInstruction inst) {
|
|
|
|
}
|
|
|
|
public void visitAssert(AstAssertInstruction instruction) {
|
|
|
|
}
|
|
|
|
public void visitEachElementGet(EachElementGetInstruction inst) {
|
|
|
|
}
|
|
|
|
public void visitEachElementHasNext(EachElementHasNextInstruction inst) {
|
|
|
|
}
|
|
}
|
|
|
|
protected AstPointerFlowGraph(PointerAnalysis pa, CallGraph cg) {
|
|
super(pa,cg);
|
|
}
|
|
|
|
protected InstructionVisitor makeInstructionVisitor(CGNode node, IR ir, BasicBlock bb) {
|
|
return new AstPointerFlowVisitor(node,ir, bb);
|
|
}
|
|
}
|
|
|
|
public PointerFlowGraphFactory getPointerFlowGraphFactory() {
|
|
return new PointerFlowGraphFactory() {
|
|
public PointerFlowGraph make(PointerAnalysis pa, CallGraph cg) {
|
|
return new AstPointerFlowGraph(pa,cg);
|
|
}
|
|
};
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// top-level node constraint generation
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
protected ExplicitCallGraph createEmptyCallGraph(ClassHierarchy cha, AnalysisOptions options) {
|
|
return new AstCallGraph(cha, options, getWarnings());
|
|
}
|
|
|
|
protected TypeInference makeTypeInference(IR ir, ClassHierarchy cha) {
|
|
TypeInference ti = new AstTypeInference(ir, cha, false);
|
|
ti.solve();
|
|
|
|
if (DEBUG_TYPE_INFERENCE) {
|
|
Trace.println("IR of " + ir.getMethod());
|
|
Trace.println( ir );
|
|
Trace.println("TypeInference of " + ir.getMethod());
|
|
for(int i = 0; i < ir.getSymbolTable().getMaxValueNumber(); i++) {
|
|
if (ti.isUndefined(i)) {
|
|
Trace.println(" value " + i + " is undefined");
|
|
} else {
|
|
Trace.println(" value " + i + " has type " + ti.getType(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
return ti;
|
|
}
|
|
|
|
protected class AstInterestingVisitor
|
|
extends InterestingVisitor
|
|
implements AstInstructionVisitor
|
|
{
|
|
protected AstInterestingVisitor(int vn) {
|
|
super(vn);
|
|
}
|
|
|
|
public void visitAstLexicalRead(AstLexicalRead instruction) {
|
|
bingo = true;
|
|
}
|
|
|
|
public void visitAstLexicalWrite(AstLexicalWrite instruction) {
|
|
bingo = true;
|
|
}
|
|
|
|
public void visitAstGlobalRead(AstGlobalRead instruction) {
|
|
bingo = true;
|
|
}
|
|
|
|
public void visitAstGlobalWrite(AstGlobalWrite instruction) {
|
|
bingo = true;
|
|
}
|
|
|
|
public void visitNonExceptingThrow(NonExceptingThrowInstruction inst) {
|
|
bingo = true;
|
|
}
|
|
|
|
public void visitAssert(AstAssertInstruction instruction) {
|
|
bingo = true;
|
|
}
|
|
|
|
public void visitEachElementGet(EachElementGetInstruction inst) {
|
|
bingo = true;
|
|
}
|
|
|
|
public void visitEachElementHasNext(EachElementHasNextInstruction inst) {
|
|
|
|
}
|
|
}
|
|
|
|
protected InterestingVisitor makeInterestingVisitor(int vn) {
|
|
return new AstInterestingVisitor(vn);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// lexical scoping handling
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
private abstract class LexicalOperator extends UnaryOperator {
|
|
private final AstCGNode node;
|
|
private final Access[] accesses;
|
|
private final boolean isLoad;
|
|
|
|
private LexicalOperator(AstCGNode node,
|
|
Access[] accesses,
|
|
boolean isLoad)
|
|
{
|
|
this.node = node;
|
|
this.isLoad = isLoad;
|
|
this.accesses = accesses;
|
|
}
|
|
|
|
private void doLexicalPointerKeys() {
|
|
for(int i = 0; i < accesses.length; i++) {
|
|
final String name = accesses[i].variableName;
|
|
final String definer = accesses[i].variableDefiner;
|
|
final int vn = accesses[i].valueNumber;
|
|
|
|
if (AstTranslator.DEBUG_LEXICAL)
|
|
Trace.println("looking up lexical parent " + definer);
|
|
|
|
for(Iterator DS = getLexicalDefiners(node, definer).iterator();
|
|
DS.hasNext(); )
|
|
{
|
|
final CGNode D = (CGNode) DS.next();
|
|
|
|
Iterator PS = new NumberedDFSDiscoverTimeIterator(callGraph, node) {
|
|
/**
|
|
*
|
|
*/
|
|
private static final long serialVersionUID = 4546217460630659884L;
|
|
|
|
protected void visitEdge(Object callee, Object caller) {
|
|
CGNode from = (CGNode) caller;
|
|
CGNode to = (CGNode) callee;
|
|
|
|
for(Iterator SS = from.getPossibleSites(to); SS.hasNext(); ) {
|
|
CallSiteReference site = (CallSiteReference)SS.next();
|
|
|
|
PointerKey V =
|
|
isLoad?
|
|
getLocalReadKey(from, site, name, definer, D):
|
|
getLocalWriteKey(from, site, name, definer, D);
|
|
|
|
if (V != null) {
|
|
action(V, vn);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected Iterator getConnected(Object n) {
|
|
if (n.equals(D) )
|
|
return EmptyIterator.instance();
|
|
else
|
|
return G.getPredNodes(n);
|
|
}
|
|
};
|
|
|
|
while (PS.hasNext()) { PS.next(); }
|
|
}
|
|
}
|
|
}
|
|
|
|
public byte evaluate(IVariable lhs, IVariable rhs) {
|
|
doLexicalPointerKeys();
|
|
return NOT_CHANGED;
|
|
}
|
|
|
|
abstract void action(PointerKey lexicalKey, int vn);
|
|
|
|
public String toString() { return "lexical op"; }
|
|
|
|
public boolean equals(Object o) { return o==this; }
|
|
|
|
public int hashCode() { return System.identityHashCode(this); }
|
|
}
|
|
|
|
private Set getLexicalDefiners(final CGNode opNode, final String definer) {
|
|
if (definer == null) {
|
|
return Collections.singleton(callGraph.getFakeRootNode());
|
|
|
|
} else {
|
|
final Set result = new HashSet();
|
|
PointerKey F = getPointerKeyForLocal(opNode, 1);
|
|
|
|
IR ir = getCFAContextInterpreter().getIR(opNode, getWarnings());
|
|
SymbolTable symtab = ir.getSymbolTable();
|
|
DefUse du = getOptions().getSSACache().findOrCreateDU(ir, opNode.getContext());
|
|
if (contentsAreInvariant(symtab, du, 1)) {
|
|
system.recordImplicitPointsToSet(F);
|
|
final InstanceKey[] functionKeys =
|
|
getInvariantContents(symtab, du, opNode, 1, this);
|
|
for(int f = 0; f < functionKeys.length; f++) {
|
|
system.findOrCreateIndexForInstanceKey( functionKeys[f] );
|
|
ScopeMappingInstanceKey K = (ScopeMappingInstanceKey)functionKeys[f];
|
|
result.add( K.getDefiningNode( definer ) );
|
|
}
|
|
} else {
|
|
PointsToSetVariable FV = system.findOrCreatePointsToSet( F );
|
|
if (FV.getValue() != null) {
|
|
FV.getValue().foreach(new IntSetAction() {
|
|
public void act(int ptr) {
|
|
InstanceKey iKey = system.getInstanceKey(ptr);
|
|
if (iKey instanceof ScopeMappingInstanceKey) {
|
|
ScopeMappingInstanceKey K = (ScopeMappingInstanceKey) iKey;
|
|
result.add( K.getDefiningNode( definer ) );
|
|
} else {
|
|
Assertions.UNREACHABLE("unexpected instance key " + iKey);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private boolean isEqual(Object a, Object b) {
|
|
if (a == null) return b==null; else return a.equals(b);
|
|
}
|
|
|
|
private Set discoveredUpwardFunargs = new HashSet();
|
|
|
|
private void addUpwardFunargConstraints(PointerKey lhs,
|
|
String name,
|
|
String definer,
|
|
CGNode definingNode)
|
|
{
|
|
discoveredUpwardFunargs.add( lhs );
|
|
|
|
LexicalInformation LI = ((AstMethod)definingNode.getMethod()).lexicalInfo;
|
|
Pair[] names = LI.getExposedNames();
|
|
for(int i = 0; i < names.length; i++) {
|
|
if (name.equals(names[i].fst) && definer.equals(names[i].snd)) {
|
|
int vn = LI.getExitExposedUses()[ i ];
|
|
if (vn > 0) {
|
|
IR ir = getCFAContextInterpreter().getIR(definingNode, getWarnings());
|
|
DefUse du = getCFAContextInterpreter().getDU(definingNode, getWarnings());
|
|
SymbolTable st = ir.getSymbolTable();
|
|
|
|
PointerKey rhs = getPointerKeyForLocal(definingNode, vn);
|
|
|
|
if (contentsAreInvariant(st, du, vn)) {
|
|
system.recordImplicitPointsToSet(rhs);
|
|
final InstanceKey[] objs = getInvariantContents(st, du, definingNode, vn, this);
|
|
for(int f = 0; f < objs.length; f++) {
|
|
system.findOrCreateIndexForInstanceKey( objs[f] );
|
|
system.newConstraint(lhs, objs[f]);
|
|
}
|
|
} else {
|
|
system.newConstraint(lhs, assignOperator, rhs);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
private PointerKey handleRootLexicalReference(String name,
|
|
String definer,
|
|
final CGNode definingNode)
|
|
{
|
|
// global variable
|
|
if (definer == null) {
|
|
return new AstGlobalPointerKey(name);
|
|
|
|
// upward funarg
|
|
} else {
|
|
class UpwardFunargPointerKey extends AstGlobalPointerKey {
|
|
UpwardFunargPointerKey(String name) { super(name); }
|
|
|
|
public CGNode getDefiningNode() { return definingNode; }
|
|
|
|
public boolean equals(Object x) {
|
|
return
|
|
(x instanceof UpwardFunargPointerKey) &&
|
|
super.equals(x) &&
|
|
definingNode.equals(((UpwardFunargPointerKey)x).getDefiningNode());
|
|
}
|
|
|
|
public int hashCode() {
|
|
return super.hashCode()*definingNode.hashCode();
|
|
}
|
|
|
|
public String toString() {
|
|
return "[upward:" + getName() + ":" + definingNode + "]";
|
|
}
|
|
}
|
|
|
|
PointerKey result = new UpwardFunargPointerKey(name);
|
|
|
|
if (! discoveredUpwardFunargs.contains( result )) {
|
|
addUpwardFunargConstraints(result, name, definer, definingNode);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private PointerKey getLocalReadKey(CGNode n,
|
|
CallSiteReference callSite,
|
|
String name,
|
|
String definer,
|
|
CGNode definingNode)
|
|
{
|
|
IMethod M = n.getMethod();
|
|
if (n == callGraph.getFakeRootNode()) {
|
|
return handleRootLexicalReference(name, definer, definingNode);
|
|
}
|
|
|
|
else if (M instanceof AstMethod) {
|
|
AstIR ir = (AstIR)getCFAContextInterpreter().getIR(n, getWarnings());
|
|
int pc = callSite.getProgramCounter();
|
|
LexicalInformation L = ((AstMethod)M).lexicalInfo;
|
|
|
|
// some people have no lexical uses at all
|
|
if (L == null) {
|
|
return null;
|
|
}
|
|
|
|
AbstractLexicalInvoke I =
|
|
(AbstractLexicalInvoke) ir.getInstructions()[pc];
|
|
|
|
// find existing explicit lexical use
|
|
for(int i = I.getNumberOfParameters(); i < I.getNumberOfUses(); i++) {
|
|
Access A = I.getLexicalUse(i);
|
|
if (A.variableName.equals(name)&&isEqual(A.variableDefiner,definer)) {
|
|
return getPointerKeyForLocal(n, A.valueNumber);
|
|
}
|
|
}
|
|
|
|
// make new lexical use
|
|
int values[] = L.getExposedUses(pc);
|
|
Pair names[] = L.getExposedNames();
|
|
if (names != null && names.length > 0) {
|
|
for(int i = 0; i < names.length; i++) {
|
|
if (name.equals(names[i].fst) && isEqual(definer,names[i].snd)) {
|
|
if (values[i] == -1) return null;
|
|
|
|
I.addLexicalUse(new Access(name, definer, values[i]));
|
|
|
|
if (SSAConversion.DEBUG_UNDO)
|
|
Trace.println("copy use #" + (-i - 1) + " to use #" + (I.getNumberOfUses()-1) + " at inst " + pc);
|
|
|
|
SSAConversion.copyUse(ir, pc, -i - 1, pc, I.getNumberOfUses()-1);
|
|
|
|
return getPointerKeyForLocal(n, values[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private PointerKey getLocalWriteKey(CGNode n,
|
|
CallSiteReference callSite,
|
|
String name,
|
|
String definer,
|
|
CGNode definingNode)
|
|
{
|
|
IMethod M = n.getMethod();
|
|
if (n == callGraph.getFakeRootNode()) {
|
|
return handleRootLexicalReference(name, definer, definingNode);
|
|
}
|
|
|
|
else if (M instanceof AstMethod) {
|
|
AstMethod AstM = (AstMethod)M;
|
|
LexicalInformation L = AstM.lexicalInfo;
|
|
|
|
// some people have no lexical uses at all
|
|
if (L == null) return null;
|
|
|
|
AstIR ir = (AstIR)getCFAContextInterpreter().getIR(n, getWarnings());
|
|
int pc = callSite.getProgramCounter();
|
|
AbstractLexicalInvoke I =
|
|
(AbstractLexicalInvoke) ir.getInstructions()[pc];
|
|
|
|
// find existing explicit lexical def
|
|
for(int i = 2; i < I.getNumberOfDefs(); i++) {
|
|
Access A = I.getLexicalDef(i);
|
|
if (A.variableName.equals(name)&&isEqual(A.variableDefiner,definer)) {
|
|
return getPointerKeyForLocal(n, A.valueNumber);
|
|
}
|
|
}
|
|
|
|
// make new lexical def
|
|
int values[] = L.getExposedUses(pc);
|
|
Pair names[] = L.getExposedNames();
|
|
if (names != null && names.length > 0) {
|
|
for(int i = 0; i < names.length; i++) {
|
|
if (name.equals(names[i].fst) && isEqual(definer,names[i].snd)) {
|
|
if (values[i] == -1) return null;
|
|
|
|
// if values[i] was altered by copy propagation, we must undo
|
|
// that to ensure we do not bash the wrong value number in the
|
|
// the next steps.
|
|
SSAConversion.undoCopyPropagation(ir, pc, -i - 1);
|
|
// possibly new instruction due to renames, so get it again
|
|
I = (AbstractLexicalInvoke) ir.getInstructions()[pc];
|
|
|
|
I.addLexicalDef(new Access(name, definer, values[i]));
|
|
|
|
if (SSAConversion.DEBUG_UNDO)
|
|
Trace.println("new def of " + values[i] + " at inst " + pc + ": " + I);
|
|
|
|
// new def has broken SSA form for values[i], so fix for that value
|
|
MutableIntSet vs = IntSetUtil.make();
|
|
vs.add( values[i] );
|
|
SSAConversion.convert(AstM, ir, getOptions().getSSAOptions());
|
|
|
|
// now redo analysis
|
|
// TODO: only values[i] uses need to be re-done.
|
|
getOptions().getSSACache().invalidateDU(M, n.getContext());
|
|
// addConstraintsFromChangedNode(n);
|
|
markChanged( n );
|
|
|
|
// get SSA-renamed def from call site instruction
|
|
return getLocalWriteKey(n, callSite, name, definer, definingNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// property manipulation handling
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
private interface ReflectedFieldAction {
|
|
void action(PointerKey fieldKey);
|
|
void dump(PointerKey fieldKey, boolean constObj, boolean constProp);
|
|
}
|
|
|
|
private void newFieldOperation(CGNode opNode, final int objVn, final int fieldsVn, final boolean isLoadOperation, final ReflectedFieldAction action) {
|
|
IR ir = getCFAContextInterpreter().getIR(opNode, getWarnings());
|
|
SymbolTable symtab = ir.getSymbolTable();
|
|
DefUse du = getCFAContextInterpreter().getDU(opNode, getWarnings());
|
|
PointerKey objKey = getPointerKeyForLocal(opNode, objVn);
|
|
final PointerKey fieldKey = getPointerKeyForLocal(opNode, fieldsVn);
|
|
|
|
// log field access
|
|
if (DEBUG_PROPERTIES) {
|
|
if (isLoadOperation)
|
|
Trace.print("adding read of " + objKey + "." + fieldKey + ":");
|
|
else
|
|
Trace.print("adding write of " + objKey + "." + fieldKey + ":");
|
|
|
|
if (contentsAreInvariant(symtab, du, objVn)) {
|
|
Trace.print(" constant obj:");
|
|
InstanceKey[] x=getInvariantContents(symtab, du, opNode, objVn, this);
|
|
for(int i = 0 ; i < x.length; i++) {
|
|
Trace.print( x[i].toString() + " " );
|
|
}
|
|
} else {
|
|
Trace.print(" obj:" + system.findOrCreatePointsToSet(objKey));
|
|
}
|
|
|
|
if (contentsAreInvariant(symtab, du, fieldsVn)) {
|
|
Trace.print(" constant prop:");
|
|
InstanceKey[] x=getInvariantContents(symtab, du, opNode, fieldsVn, this);
|
|
for(int i = 0 ; i < x.length; i++) {
|
|
Trace.print( x[i].toString() + " " );
|
|
}
|
|
} else {
|
|
Trace.print(" props:" + system.findOrCreatePointsToSet(fieldKey));
|
|
}
|
|
|
|
Trace.print("\n");
|
|
}
|
|
|
|
// make sure instance keys get mapped for PointerAnalysisImpl
|
|
if (contentsAreInvariant(symtab, du, objVn)) {
|
|
InstanceKey[] x=getInvariantContents(symtab, du, opNode, objVn, this);
|
|
for(int i = 0; i < x.length; i++) {
|
|
system.findOrCreateIndexForInstanceKey(x[i]);
|
|
}
|
|
}
|
|
if (contentsAreInvariant(symtab, du, fieldsVn)) {
|
|
InstanceKey[] x=getInvariantContents(symtab, du, opNode, fieldsVn, this);
|
|
for(int i = 0; i < x.length; i++) {
|
|
system.findOrCreateIndexForInstanceKey(x[i]);
|
|
}
|
|
}
|
|
|
|
// process field access
|
|
if (contentsAreInvariant(symtab, du, objVn)) {
|
|
system.recordImplicitPointsToSet(objKey);
|
|
final InstanceKey[] objKeys = getInvariantContents(symtab, du, opNode, objVn, this);
|
|
|
|
if (contentsAreInvariant(symtab, du, fieldsVn)) {
|
|
system.recordImplicitPointsToSet(fieldKey);
|
|
InstanceKey[] fieldsKeys = getInvariantContents(symtab, du, opNode, fieldsVn, this);
|
|
|
|
for(int o = 0; o < objKeys.length; o++) {
|
|
PointerKey objCatalog = getPointerKeyForObjectCatalog(objKeys[o]);
|
|
for(int f = 0; f < fieldsKeys.length; f++) {
|
|
if (isLoadOperation) {
|
|
for(Iterator keys =
|
|
getPointerKeysForReflectedFieldRead(objKeys[o], fieldsKeys[f]);
|
|
keys.hasNext(); )
|
|
{
|
|
PointerKey key = (PointerKey)keys.next();
|
|
if (DEBUG_PROPERTIES) action.dump( key, true, true );
|
|
action.action( key );
|
|
}
|
|
} else {
|
|
system.newConstraint(objCatalog, fieldsKeys[f]);
|
|
for(Iterator keys =
|
|
getPointerKeysForReflectedFieldWrite(objKeys[o],fieldsKeys[f]);
|
|
keys.hasNext(); )
|
|
{
|
|
PointerKey key = (PointerKey)keys.next();
|
|
if (DEBUG_PROPERTIES) action.dump( key, true, true );
|
|
action.action( key );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if (! isLoadOperation) {
|
|
for(int o = 0; o < objKeys.length; o++) {
|
|
PointerKey objCatalog = getPointerKeyForObjectCatalog(objKeys[o]);
|
|
system.newConstraint(objCatalog, assignOperator, fieldKey);
|
|
}
|
|
}
|
|
|
|
system.newSideEffect(
|
|
new UnaryOperator() {
|
|
public byte evaluate(IVariable lhs, IVariable rhs) {
|
|
final IntSetVariable fields = (IntSetVariable) rhs;
|
|
if (fields.getValue() != null) {
|
|
fields.getValue().foreach(
|
|
new IntSetAction() {
|
|
public void act(int fptr) {
|
|
InstanceKey field = system.getInstanceKey(fptr);
|
|
for(int o = 0; o < objKeys.length; o++) {
|
|
for(Iterator keys =
|
|
isLoadOperation?
|
|
getPointerKeysForReflectedFieldRead(objKeys[o],field):
|
|
getPointerKeysForReflectedFieldWrite(objKeys[o],field);
|
|
keys.hasNext(); )
|
|
{
|
|
PointerKey key = (PointerKey)keys.next();
|
|
if (DEBUG_PROPERTIES) action.dump(key, false, true);
|
|
action.action( key );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
return NOT_CHANGED;
|
|
}
|
|
public int hashCode() {
|
|
return System.identityHashCode(this);
|
|
}
|
|
public boolean equals(Object o) {
|
|
return o==this;
|
|
}
|
|
public String toString() {
|
|
return "field op" + objVn + ", " + fieldsVn;
|
|
}
|
|
},
|
|
fieldKey);
|
|
}
|
|
|
|
} else {
|
|
if (contentsAreInvariant(symtab, du, fieldsVn)) {
|
|
system.recordImplicitPointsToSet(fieldKey);
|
|
final InstanceKey[] fieldsKeys = getInvariantContents(symtab, du, opNode, fieldsVn, this);
|
|
|
|
system.newSideEffect(
|
|
new UnaryOperator() {
|
|
public byte evaluate(IVariable lhs, IVariable rhs) {
|
|
final IntSetVariable objects = (IntSetVariable) rhs;
|
|
if (objects.getValue() != null) {
|
|
objects.getValue().foreach(
|
|
new IntSetAction() {
|
|
public void act(int optr) {
|
|
InstanceKey object = system.getInstanceKey(optr);
|
|
PointerKey objCatalog =
|
|
getPointerKeyForObjectCatalog(object);
|
|
for(int f = 0; f < fieldsKeys.length; f++) {
|
|
if (isLoadOperation) {
|
|
for(Iterator keys =
|
|
getPointerKeysForReflectedFieldRead(object, fieldsKeys[f]);
|
|
keys.hasNext(); )
|
|
{
|
|
PointerKey key = (PointerKey)keys.next();
|
|
if (DEBUG_PROPERTIES) action.dump(key, true, false);
|
|
action.action( key );
|
|
}
|
|
} else {
|
|
system.newConstraint(objCatalog, fieldsKeys[f]);
|
|
for(Iterator keys =
|
|
getPointerKeysForReflectedFieldWrite(object,fieldsKeys[f]);
|
|
keys.hasNext(); )
|
|
{
|
|
PointerKey key = (PointerKey)keys.next();
|
|
if (DEBUG_PROPERTIES) action.dump(key, true, false);
|
|
action.action( key );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
return NOT_CHANGED;
|
|
}
|
|
public int hashCode() {
|
|
return System.identityHashCode(this);
|
|
}
|
|
public boolean equals(Object o) {
|
|
return o==this;
|
|
}
|
|
public String toString() {
|
|
return "field op" + objVn + ", " + fieldsVn;
|
|
}
|
|
},
|
|
objKey);
|
|
|
|
} else {
|
|
system.newSideEffect(
|
|
new AbstractOperator() {
|
|
public byte evaluate(IVariable lhs, final IVariable[] rhs) {
|
|
final IntSetVariable receivers = (IntSetVariable) rhs[0];
|
|
final IntSetVariable fields = (IntSetVariable) rhs[1];
|
|
if (receivers.getValue() != null && fields.getValue() != null) {
|
|
receivers.getValue().foreach(
|
|
new IntSetAction() {
|
|
public void act(int rptr) {
|
|
final InstanceKey receiver = system.getInstanceKey(rptr);
|
|
|
|
if (! isLoadOperation) {
|
|
PointerKey cat =
|
|
getPointerKeyForObjectCatalog(receiver);
|
|
system.newConstraint(cat, assignOperator, fieldKey);
|
|
}
|
|
|
|
fields.getValue().foreach(
|
|
new IntSetAction() {
|
|
public void act(int fptr) {
|
|
InstanceKey field = system.getInstanceKey(fptr);
|
|
for(Iterator keys =
|
|
isLoadOperation?
|
|
getPointerKeysForReflectedFieldRead(receiver, field):
|
|
getPointerKeysForReflectedFieldWrite(receiver, field);
|
|
keys.hasNext(); )
|
|
{
|
|
PointerKey key = (PointerKey)keys.next();
|
|
if (DEBUG_PROPERTIES)
|
|
action.dump(key, false, false);
|
|
action.action( key );
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
return NOT_CHANGED;
|
|
}
|
|
|
|
public String toString() { return "field op"; }
|
|
|
|
public boolean equals(Object o) { return o==this; }
|
|
|
|
public int hashCode() { return System.identityHashCode(this); }
|
|
},
|
|
objKey,
|
|
fieldKey);
|
|
}
|
|
}
|
|
|
|
if (DEBUG_PROPERTIES) {
|
|
Trace.println("finished\n");
|
|
}
|
|
}
|
|
|
|
protected void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, int rhsVn) {
|
|
IR ir = getCFAContextInterpreter().getIR(opNode, getWarnings());
|
|
SymbolTable symtab = ir.getSymbolTable();
|
|
DefUse du = getCFAContextInterpreter().getDU(opNode, getWarnings());
|
|
PointerKey rhsKey = getPointerKeyForLocal(opNode, rhsVn);
|
|
if (contentsAreInvariant(symtab, du, rhsVn)) {
|
|
system.recordImplicitPointsToSet(rhsKey);
|
|
newFieldWrite(opNode, objVn, fieldsVn, getInvariantContents(symtab, du, opNode, rhsVn, this));
|
|
} else {
|
|
newFieldWrite(opNode, objVn, fieldsVn, rhsKey);
|
|
}
|
|
}
|
|
|
|
protected void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, final InstanceKey[] rhsFixedValues) {
|
|
try {
|
|
|
|
newFieldOperation(opNode, objVn, fieldsVn, false, new ReflectedFieldAction() {
|
|
public void dump(PointerKey fieldKey, boolean constObj, boolean constProp) {
|
|
Trace.println("writing fixed rvals to " + fieldKey + " " + constObj + ", " + constProp);
|
|
for(int i = 0; i < rhsFixedValues.length; i++) {
|
|
Trace.println("writing " + rhsFixedValues[i]);
|
|
}
|
|
}
|
|
public void action(PointerKey fieldKey) {
|
|
for(int i = 0; i < rhsFixedValues.length; i++) {
|
|
system.findOrCreateIndexForInstanceKey(rhsFixedValues[i]);
|
|
system.newConstraint(fieldKey, rhsFixedValues[i]);
|
|
}
|
|
}
|
|
});
|
|
|
|
} catch (RuntimeException e) {
|
|
System.err.println("error: " + e);
|
|
System.err.println( getCFAContextInterpreter().getIR(opNode, getWarnings()) );
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
protected void newFieldWrite(CGNode opNode, int objVn, int fieldsVn, final PointerKey rhs) {
|
|
newFieldOperation(opNode, objVn, fieldsVn, false, new ReflectedFieldAction() {
|
|
public void dump(PointerKey fieldKey, boolean constObj, boolean constProp) {
|
|
Trace.println("write " + rhs + " to " + fieldKey + " " + constObj + ", " + constProp);
|
|
}
|
|
public void action(PointerKey fieldKey) {
|
|
system.newConstraint(fieldKey, assignOperator, rhs);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected void newFieldRead(CGNode opNode, int objVn, int fieldsVn, int lhsVn) {
|
|
newFieldRead(opNode, objVn, fieldsVn, getPointerKeyForLocal(opNode, lhsVn));
|
|
}
|
|
|
|
protected void newFieldRead(CGNode opNode, int objVn, int fieldsVn, final PointerKey lhs) {
|
|
newFieldOperation(opNode, objVn, fieldsVn, true, new ReflectedFieldAction() {
|
|
public void dump(PointerKey fieldKey, boolean constObj, boolean constProp) {
|
|
Trace.println("read " + lhs + " from " + fieldKey + " " + constObj + ", " + constProp);
|
|
}
|
|
public void action(PointerKey fieldKey) {
|
|
system.newConstraint(lhs, assignOperator, fieldKey);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IR visitor specialization for Ast-specific IR types
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected class AstConstraintVisitor
|
|
extends ConstraintVisitor
|
|
implements AstInstructionVisitor
|
|
{
|
|
|
|
public AstConstraintVisitor(ExplicitCallGraph.ExplicitNode node, IR ir, ExplicitCallGraph callGraph, DefUse du) {
|
|
super(node, ir, callGraph, du);
|
|
}
|
|
|
|
public void visitNonExceptingThrow(NonExceptingThrowInstruction inst) {
|
|
// no-op: exceptions handled elsewhere
|
|
// (see comment in SSAPropagationCallGraphBuilder)
|
|
}
|
|
|
|
private void visitLexical(final LexicalOperator op) {
|
|
final PointerKey function = getPointerKeyForLocal(node, 1);
|
|
if (contentsAreInvariant(symbolTable, du, 1)) {
|
|
op.doLexicalPointerKeys();
|
|
} else {
|
|
system.newSideEffect(op, function);
|
|
}
|
|
|
|
((AstCGNode)node).addCallback(new Function() {
|
|
public Object apply(Object ignore) {
|
|
op.doLexicalPointerKeys();
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
public void visitAstLexicalRead(AstLexicalRead instruction) {
|
|
visitLexical(new LexicalOperator((AstCGNode)node, instruction.getAccesses(), true) {
|
|
void action(PointerKey lexicalKey, int vn) {
|
|
PointerKey lval = getPointerKeyForLocal(node, vn);
|
|
if (lexicalKey instanceof LocalPointerKey) {
|
|
CGNode lnode = ((LocalPointerKey)lexicalKey).getNode();
|
|
int lvn = ((LocalPointerKey)lexicalKey).getValueNumber();
|
|
IR lir = getCFAContextInterpreter().getIR(lnode, getWarnings());
|
|
SymbolTable lsymtab = lir.getSymbolTable();
|
|
DefUse ldu = getOptions().getSSACache().findOrCreateDU(lir, lnode.getContext());
|
|
if (contentsAreInvariant(lsymtab, ldu, lvn)) {
|
|
InstanceKey[] ik = getInvariantContents(lsymtab, ldu, lnode, lvn, AstSSAPropagationCallGraphBuilder.this);
|
|
system.recordImplicitPointsToSet(lexicalKey);
|
|
for(int i = 0; i < ik.length; i++) {
|
|
system.findOrCreateIndexForInstanceKey(ik[i]);
|
|
system.newConstraint(lval, ik[i]);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
system.newConstraint(lval, assignOperator, lexicalKey);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void visitAstLexicalWrite(AstLexicalWrite instruction) {
|
|
visitLexical(new LexicalOperator((AstCGNode)node, instruction.getAccesses(), false) {
|
|
void action(PointerKey lexicalKey, int vn) {
|
|
PointerKey rval = getPointerKeyForLocal(node,vn);
|
|
if (contentsAreInvariant(symbolTable, du, vn)) {
|
|
InstanceKey[] ik = getInvariantContents(symbolTable, du, node, vn, AstSSAPropagationCallGraphBuilder.this);
|
|
system.recordImplicitPointsToSet(rval);
|
|
for(int i = 0; i < ik.length; i++) {
|
|
system.findOrCreateIndexForInstanceKey(ik[i]);
|
|
system.newConstraint(lexicalKey, ik[i]);
|
|
}
|
|
} else {
|
|
system.newConstraint(lexicalKey, assignOperator, rval);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void visitAstGlobalRead(AstGlobalRead instruction) {
|
|
visitGetInternal(instruction.getDef(),
|
|
-1,
|
|
true,
|
|
instruction.getDeclaredField());
|
|
}
|
|
|
|
public void visitAstGlobalWrite(AstGlobalWrite instruction) {
|
|
visitPutInternal(instruction.getVal(),
|
|
-1,
|
|
true,
|
|
instruction.getDeclaredField());
|
|
}
|
|
|
|
public void visitPut(SSAPutInstruction inst) {
|
|
super.visitPut(inst);
|
|
|
|
if (inst.isStatic() || !useObjectCatalog()) return;
|
|
|
|
SymbolTable symtab = ir.getSymbolTable();
|
|
|
|
int objVn = inst.getRef();
|
|
String fieldName = inst.getDeclaredField().getName().toString();
|
|
int fieldNameVn = symtab.getConstant( fieldName );
|
|
|
|
final PointerKey objKey = getPointerKeyForLocal(node, objVn);
|
|
|
|
final InstanceKey[] fieldNameKeys =
|
|
getInvariantContents(symtab, du, node, fieldNameVn, AstSSAPropagationCallGraphBuilder.this);
|
|
Assertions._assert( fieldNameKeys.length == 1 );
|
|
|
|
if (contentsAreInvariant(symtab, du, objVn)) {
|
|
system.recordImplicitPointsToSet(objKey);
|
|
final InstanceKey[] objKeys =
|
|
getInvariantContents(symtab, du, node, objVn, AstSSAPropagationCallGraphBuilder.this);
|
|
|
|
for(int i = 0; i < objKeys.length; i++) {
|
|
PointerKey objCatalog = getPointerKeyForObjectCatalog(objKeys[i]);
|
|
system.newConstraint(objCatalog, fieldNameKeys[0]);
|
|
}
|
|
|
|
} else {
|
|
system.newSideEffect(
|
|
new UnaryOperator() {
|
|
public byte evaluate(IVariable lhs, IVariable rhs) {
|
|
final IntSetVariable objects = (IntSetVariable) rhs;
|
|
if (objects.getValue() != null) {
|
|
objects.getValue().foreach(
|
|
new IntSetAction() {
|
|
public void act(int optr) {
|
|
InstanceKey object = system.getInstanceKey(optr);
|
|
PointerKey cat = getPointerKeyForObjectCatalog(object);
|
|
system.newConstraint(cat, fieldNameKeys[0]);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
return NOT_CHANGED;
|
|
}
|
|
public int hashCode() {
|
|
return System.identityHashCode(this);
|
|
}
|
|
public boolean equals(Object o) {
|
|
return o==this;
|
|
}
|
|
public String toString() {
|
|
return "field name record: " + objKey;
|
|
}
|
|
},
|
|
objKey);
|
|
}
|
|
}
|
|
|
|
public void visitAssert(AstAssertInstruction instruction) {
|
|
|
|
}
|
|
|
|
public void visitEachElementHasNext(EachElementHasNextInstruction inst) {
|
|
|
|
}
|
|
|
|
public void visitEachElementGet(EachElementGetInstruction inst) {
|
|
int lval = inst.getDef(0);
|
|
final PointerKey lk = getPointerKeyForLocal(node, lval);
|
|
|
|
int rval = inst.getUse(0);
|
|
final PointerKey rk = getPointerKeyForLocal(node, rval);
|
|
|
|
if (contentsAreInvariant(symbolTable, du, rval)) {
|
|
InstanceKey objects[] = getInvariantContents(symbolTable, du, node, rval, AstSSAPropagationCallGraphBuilder.this);
|
|
for(int i = 0; i < objects.length; i++) {
|
|
PointerKey catalog = getPointerKeyForObjectCatalog(objects[i]);
|
|
system.newConstraint(lk, assignOperator, catalog);
|
|
}
|
|
}
|
|
|
|
else {
|
|
system.newSideEffect(
|
|
new UnaryOperator() {
|
|
public byte evaluate(IVariable lhs, IVariable rhs) {
|
|
final IntSetVariable objects = (IntSetVariable) rhs;
|
|
if (objects.getValue() != null) {
|
|
objects.getValue().foreach(
|
|
new IntSetAction() {
|
|
public void act(int optr) {
|
|
InstanceKey object = system.getInstanceKey(optr);
|
|
PointerKey objCatalog =
|
|
getPointerKeyForObjectCatalog(object);
|
|
system.newConstraint(lk, assignOperator, objCatalog);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
return NOT_CHANGED;
|
|
}
|
|
public int hashCode() {
|
|
return System.identityHashCode(this);
|
|
}
|
|
public boolean equals(Object o) {
|
|
return o==this;
|
|
}
|
|
public String toString() {
|
|
return "get catalog op" + rk;
|
|
}
|
|
},
|
|
rk);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
protected ConstraintVisitor makeVisitor(ExplicitCallGraph.ExplicitNode node,
|
|
IR ir,
|
|
DefUse du,
|
|
ExplicitCallGraph callGraph)
|
|
{
|
|
return new AstConstraintVisitor(node, ir, callGraph, du);
|
|
}
|
|
}
|
|
|