282 lines
12 KiB
Java
282 lines
12 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2011 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.js.ipa.callgraph.correlations.extraction;
|
|
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import com.ibm.wala.cast.js.ipa.callgraph.correlations.Correlation;
|
|
import com.ibm.wala.cast.js.ipa.callgraph.correlations.CorrelationSummary;
|
|
import com.ibm.wala.cast.js.ipa.callgraph.correlations.EscapeCorrelation;
|
|
import com.ibm.wala.cast.js.ipa.callgraph.correlations.ReadWriteCorrelation;
|
|
import com.ibm.wala.cast.loader.AstMethod;
|
|
import com.ibm.wala.cast.tree.CAstControlFlowMap;
|
|
import com.ibm.wala.cast.tree.CAstEntity;
|
|
import com.ibm.wala.cast.tree.CAstNode;
|
|
import com.ibm.wala.cast.tree.CAstSourcePositionMap;
|
|
import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.collections.HashSetFactory;
|
|
import com.ibm.wala.util.collections.Pair;
|
|
|
|
/**
|
|
* An {@link ExtractionPolicy} that specifies that correlated pairs should be extracted.
|
|
*
|
|
* In principle, extracting an arbitrary correlated pair can be very difficult. We restrict
|
|
* our attention to the case where both read and write occur within the same block of
|
|
* statements, with the read preceding the write. In practice, most correlations are of
|
|
* this form.
|
|
*
|
|
* TODO: The code for finding the correlated instructions is broken since Rhino only gives
|
|
* us line number positions. Consequently, it fails to find the relevant instructions every
|
|
* once in a while.
|
|
*
|
|
* @author mschaefer
|
|
*
|
|
*/
|
|
public class CorrelatedPairExtractionPolicy extends ExtractionPolicy {
|
|
private static final boolean DEBUG = true;
|
|
private final Map<CAstNode, List<ExtractionRegion>> region_map = HashMapFactory.make();
|
|
|
|
private CorrelatedPairExtractionPolicy() {}
|
|
|
|
private static void findNodesAtPos(int kind, Position pos, CAstSourcePositionMap spmap, ChildPos nodep, Set<ChildPos> res) {
|
|
CAstNode node = nodep.getChild();
|
|
if(node == null)
|
|
return;
|
|
Position ndpos = spmap.getPosition(node);
|
|
if(ndpos != null) {
|
|
// if we are in the wrong file or past the position pos, abort search
|
|
if(!ndpos.getURL().equals(pos.getURL()))
|
|
return;
|
|
|
|
if(pos.getLastLine() >= 0 && ndpos.getFirstLine() > pos.getLastLine())
|
|
return;
|
|
|
|
//if(node.getKind() == kind && ndpos.getFirstLine() == pos.getFirstLine() && ndpos.getLastLine() == pos.getLastLine())
|
|
if(node.getKind() == kind && ndpos.getFirstOffset() == pos.getFirstOffset() && ndpos.getLastOffset() == pos.getLastOffset())
|
|
res.add(nodep);
|
|
}
|
|
for(int i=0;i<node.getChildCount();++i)
|
|
findNodesAtPos(kind, pos, spmap, nodep.getChildPos(i), res);
|
|
}
|
|
|
|
private static Set<ChildPos> findNodesAtPos(int kind, Position pos, CAstEntity entity) {
|
|
Set<ChildPos> res = HashSetFactory.make();
|
|
CAstSourcePositionMap spmap = entity.getSourceMap();
|
|
CAstNode ast = entity.getAST();
|
|
for(int i=0;i<ast.getChildCount();++i)
|
|
findNodesAtPos(kind, pos, spmap, new ChildPos(ast, i, new RootPos()), res);
|
|
return res;
|
|
}
|
|
|
|
// create an ExtractRegion for the given correlation
|
|
private boolean addCorrelation(CAstEntity entity, Correlation corr, CorrelationSummary correlations) {
|
|
Position startPos = corr.getStartPosition(correlations.getPositions()),
|
|
endPos = corr.getEndPosition(correlations.getPositions());
|
|
|
|
// TODO: enable these assertions; currently we're getting getLastLine() == -1 a lot
|
|
assert startPos.getFirstLine() != -1;
|
|
//assert startPos.getLastLine() != -1;
|
|
assert endPos.getFirstLine() != -1;
|
|
//assert endPos.getLastLine() != -1;
|
|
|
|
Set<ChildPos> startNodes = null,
|
|
endNodes = null;
|
|
if(!entity.getPosition().getURL().equals(startPos.getURL()))
|
|
return true;
|
|
startNodes = findNodesAtPos(CAstNode.OBJECT_REF, startPos, entity);
|
|
if(corr instanceof ReadWriteCorrelation) {
|
|
endNodes = findNodesAtPos(CAstNode.ASSIGN, endPos, entity);
|
|
} else if(corr instanceof EscapeCorrelation) {
|
|
int arity = ((EscapeCorrelation)corr).getNumberOfArguments();
|
|
endNodes = findNodesAtPos(CAstNode.CALL, endPos, entity);
|
|
for(Iterator<ChildPos> iter=endNodes.iterator();iter.hasNext();) {
|
|
CAstNode candidate = iter.next().getChild();
|
|
// need to deduct three here: one for the function expression, one for "do"/"ctor", and one for the receiver expression
|
|
if(candidate.getChildCount() - 3 != arity)
|
|
iter.remove();
|
|
}
|
|
} else {
|
|
throw new IllegalArgumentException("Unknown correlation type.");
|
|
}
|
|
if(startNodes.isEmpty() || endNodes.isEmpty()) {
|
|
if(DEBUG)
|
|
System.err.println("Couldn't find any " + (startNodes.isEmpty()? endNodes.isEmpty()? "boundary": "start": "end") + " nodes for correlation " + corr.pp(correlations.getPositions()));
|
|
return true;
|
|
}
|
|
ChildPos startNode, endNode;
|
|
filterNames(startNodes, corr.getIndexName());
|
|
filterNames(endNodes, corr.getIndexName());
|
|
Iterator<ChildPos> iter = startNodes.iterator();
|
|
if(startNodes.size() == 2 && endNodes.equals(startNodes)) {
|
|
startNode = iter.next();
|
|
endNode = iter.next();
|
|
} else if(startNodes.size() > 1 || startNodes.size() == 0) {
|
|
if(DEBUG)
|
|
System.err.println("Couldn't find unique start node for correlation " + corr.pp(correlations.getPositions()));
|
|
return false;
|
|
} else if(endNodes.size() > 1 || endNodes.size() == 0) {
|
|
if(DEBUG)
|
|
System.err.println("Couldn't find unique end node for correlation " + corr.pp(correlations.getPositions()));
|
|
return false;
|
|
} else {
|
|
startNode = startNodes.iterator().next();
|
|
endNode = endNodes.iterator().next();
|
|
}
|
|
|
|
List<String> locals = corr.getFlownThroughLocals().size() == 1 ? Collections.singletonList(corr.getFlownThroughLocals().iterator().next())
|
|
: Collections.<String>emptyList();
|
|
Pair<CAstNode, ? extends ExtractionRegion> region_info = findClosestContainingBlock(entity, startNode, endNode, corr.getIndexName(), locals);
|
|
if(region_info == null) {
|
|
if(DEBUG)
|
|
System.err.println("Couldn't find enclosing block for correlation " + corr.pp(correlations.getPositions()));
|
|
return false;
|
|
}
|
|
|
|
List<ExtractionRegion> regions = region_map.get(region_info.fst);
|
|
if(regions == null)
|
|
region_map.put(region_info.fst, regions = new LinkedList<>());
|
|
for(int i=0;i<regions.size();++i) {
|
|
ExtractionRegion region2 = regions.get(i);
|
|
if(region2.getEnd() <= region_info.snd.getStart())
|
|
continue;
|
|
if(region2.getStart() < region_info.snd.getEnd()) {
|
|
if(region_info.snd.getParameters().equals(region2.getParameters())) {
|
|
region2.setStart(Math.min(region_info.snd.getStart(), region2.getStart()));
|
|
region2.setEnd(Math.max(region_info.snd.getEnd(), region2.getEnd()));
|
|
if(DEBUG)
|
|
System.err.println("Successfully processed correlation " + corr.pp(correlations.getPositions()));
|
|
return true;
|
|
}
|
|
if(DEBUG)
|
|
System.err.println("Overlapping regions.");
|
|
return false;
|
|
}
|
|
regions.add(i, region_info.snd);
|
|
if(DEBUG)
|
|
System.out.println("Successfully processed correlation " + corr.pp(correlations.getPositions()));
|
|
return true;
|
|
}
|
|
if(DEBUG)
|
|
System.out.println("Successfully processed correlation " + corr.pp(correlations.getPositions()));
|
|
regions.add(region_info.snd);
|
|
return true;
|
|
}
|
|
|
|
private static void filterNames(Set<ChildPos> nodes, String indexName) {
|
|
for(Iterator<ChildPos> iter=nodes.iterator();iter.hasNext();) {
|
|
CAstNode node = iter.next().getChild();
|
|
if(node.getKind() == CAstNode.OBJECT_REF) {
|
|
CAstNode index = node.getChild(1);
|
|
if(index.getKind() != CAstNode.VAR || !index.getChild(0).getValue().equals(indexName)) {
|
|
iter.remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Pair<CAstNode, ? extends ExtractionRegion> findClosestContainingBlock(CAstEntity entity, ChildPos startNode, ChildPos endNode, String parmName, List<String> locals) {
|
|
ChildPos pos = startNode;
|
|
CAstNode block = null;
|
|
int start = -1, end = 0;
|
|
int start_inner = -1, end_inner = -1;
|
|
|
|
do {
|
|
if(pos != startNode && pos.getParentPos() instanceof ChildPos)
|
|
pos = (ChildPos)pos.getParentPos();
|
|
// find the next closest block around the node at position "pos"
|
|
while(pos.getParent().getKind() != CAstNode.BLOCK_STMT) {
|
|
if(pos.getParentPos() instanceof ChildPos)
|
|
pos = (ChildPos)pos.getParentPos();
|
|
else
|
|
return null;
|
|
}
|
|
block = pos.getParent();
|
|
start = pos.getIndex();
|
|
end = getCoveringChildIndex(block, start, endNode.getChild()) + 1;
|
|
} while(end == 0);
|
|
|
|
// expand region to include forward goto targets
|
|
CAstControlFlowMap cfg = entity.getControlFlow();
|
|
for(int i=start;i<end;++i) {
|
|
CAstNode stmt = block.getChild(i);
|
|
for(Object targetLabel : cfg.getTargetLabels(stmt)) {
|
|
CAstNode target = cfg.getTarget(stmt, targetLabel);
|
|
int targetIndex = getCoveringChildIndex(block, start, target);
|
|
if(targetIndex >= end)
|
|
end = targetIndex + 1;
|
|
}
|
|
}
|
|
|
|
// special hack to handle "var p = ..., x = y[p];", where startNode = "y[p]"
|
|
if(block.getChild(0).getKind() == CAstNode.BLOCK_STMT && start == 0) {
|
|
for(start_inner=0;start_inner<block.getChild(0).getChildCount();++start_inner)
|
|
if(NodePos.inSubtree(startNode.getChild(), block.getChild(0).getChild(start_inner)))
|
|
return Pair.make(block, new TwoLevelExtractionRegion(start, end, start_inner, end_inner, Collections.singletonList(parmName), locals));
|
|
}
|
|
// special hack to handle the case where we're extracting the body of a local scope
|
|
else if(block.getChild(start).getKind() == CAstNode.LOCAL_SCOPE && end == start+1) {
|
|
return Pair.make(block, new TwoLevelExtractionRegion(start, end, 0, -1, Collections.singletonList(parmName), locals));
|
|
}
|
|
|
|
return Pair.make(block, new ExtractionRegion(start, end, Collections.singletonList(parmName), locals));
|
|
}
|
|
|
|
private static int getCoveringChildIndex(CAstNode parent, int start, CAstNode child) {
|
|
for(int i=start;i<parent.getChildCount();++i)
|
|
if(NodePos.inSubtree(child, parent.getChild(i)))
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
private static CorrelatedPairExtractionPolicy addCorrelations(CAstEntity entity, Map<Position, CorrelationSummary> summaries, CorrelatedPairExtractionPolicy policy) {
|
|
// add correlations for this entity
|
|
if(entity.getAST() != null && summaries.containsKey(entity.getPosition())) {
|
|
CorrelationSummary correlations = summaries.get(entity.getPosition());
|
|
for(Correlation corr : correlations.getCorrelations())
|
|
policy.addCorrelation(entity, corr, correlations);
|
|
}
|
|
// recursively add correlations for scoped entities
|
|
Map<CAstNode, Collection<CAstEntity>> allScopedEntities = entity.getAllScopedEntities();
|
|
for(Collection<CAstEntity> scopedEntities : allScopedEntities.values())
|
|
for(CAstEntity scopedEntity : scopedEntities)
|
|
if(addCorrelations(scopedEntity, summaries, policy) == null)
|
|
return null;
|
|
return policy;
|
|
}
|
|
|
|
public static CorrelatedPairExtractionPolicy make(CAstEntity entity, Map<IMethod, CorrelationSummary> summaries) {
|
|
CorrelatedPairExtractionPolicy policy = new CorrelatedPairExtractionPolicy();
|
|
Map<Position, CorrelationSummary> summary_map = HashMapFactory.make();
|
|
for(Map.Entry<IMethod, CorrelationSummary> e : summaries.entrySet()) {
|
|
if(e.getKey() instanceof AstMethod) {
|
|
Position pos = ((AstMethod)e.getKey()).getSourcePosition();
|
|
if(pos != null)
|
|
summary_map.put(pos, e.getValue());
|
|
}
|
|
}
|
|
return addCorrelations(entity, summary_map, policy);
|
|
}
|
|
|
|
@Override
|
|
public List<ExtractionRegion> extract(CAstNode node) {
|
|
return region_map.get(node);
|
|
}
|
|
}
|