227 lines
6.2 KiB
Java
227 lines
6.2 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.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import com.ibm.wala.cast.tree.CAstEntity;
|
|
import com.ibm.wala.cast.tree.CAstNode;
|
|
import com.ibm.wala.util.collections.HashSetFactory;
|
|
import com.ibm.wala.util.collections.Pair;
|
|
|
|
/**
|
|
* A special {@link ChildPos} representing the position of a node which is the body of a for-in loop.
|
|
*
|
|
* <p>This also stores some additional data obtained while rewriting the loop body, such as whether
|
|
* <code>return</code> statements were encountered.</p>
|
|
*
|
|
* @author mschaefer
|
|
*
|
|
*/
|
|
public class ExtractionPos extends NodePos {
|
|
private final CAstNode parent;
|
|
private final ExtractionRegion region;
|
|
private final NodePos parent_pos;
|
|
private boolean contains_return;
|
|
private boolean contains_this;
|
|
private final Set<Pair<String, CAstNode>> goto_targets = HashSetFactory.make();
|
|
private boolean contains_outer_goto;
|
|
private final Set<ExtractionPos> nested_loops = HashSetFactory.make();
|
|
private CAstEntity extracted_entity;
|
|
private CAstNode callsite;
|
|
|
|
public ExtractionPos(CAstNode parent, ExtractionRegion region, NodePos parent_pos) {
|
|
this.parent = parent;
|
|
this.region = region;
|
|
this.parent_pos = parent_pos;
|
|
}
|
|
|
|
public CAstNode getParent() {
|
|
return parent;
|
|
}
|
|
|
|
public int getStart() {
|
|
return region.getStart();
|
|
}
|
|
|
|
public int getEnd() {
|
|
return region.getEnd();
|
|
}
|
|
|
|
public ExtractionRegion getRegion() {
|
|
return region;
|
|
}
|
|
|
|
public boolean contains(CAstNode node) {
|
|
for(int i=getStart();i<getEnd();++i)
|
|
if(NodePos.inSubtree(node, parent.getChild(i)))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
public List<String> getParameters() {
|
|
return region.getParameters();
|
|
}
|
|
|
|
public void addGotoTarget(String label, CAstNode node) {
|
|
// check whether this target lies beyond an enclosing for-in loop
|
|
ExtractionPos outer = getEnclosingExtractionPos(parent_pos);
|
|
if(outer != null && !outer.contains(node)) {
|
|
// the goto needs to be handled by the outer loop
|
|
outer.addGotoTarget(label, node);
|
|
// but we need to remember to pass it on
|
|
contains_outer_goto = true;
|
|
} else {
|
|
// this goto is our responsibility
|
|
goto_targets.add(Pair.make(label, node));
|
|
}
|
|
}
|
|
|
|
public boolean containsReturn() {
|
|
return contains_return;
|
|
}
|
|
|
|
public void addReturn() {
|
|
this.contains_return = true;
|
|
}
|
|
|
|
public Set<Pair<String, CAstNode>> getGotoTargets() {
|
|
return Collections.unmodifiableSet(goto_targets);
|
|
}
|
|
|
|
public void addThis() {
|
|
contains_this = true;
|
|
}
|
|
|
|
public boolean containsThis() {
|
|
return contains_this;
|
|
}
|
|
|
|
public boolean containsGoto() {
|
|
return !getGotoTargets().isEmpty();
|
|
}
|
|
|
|
public boolean containsOuterGoto() {
|
|
return contains_outer_goto;
|
|
}
|
|
|
|
public boolean containsJump() {
|
|
return containsGoto() || containsReturn() || containsOuterGoto();
|
|
}
|
|
|
|
public String getThisParmName() {
|
|
return "thi$";
|
|
}
|
|
|
|
public void addNestedPos(ExtractionPos loop) {
|
|
nested_loops.add(loop);
|
|
}
|
|
|
|
public Iterator<ExtractionPos> getNestedLoops() {
|
|
return nested_loops.iterator();
|
|
}
|
|
|
|
public void setExtractedEntity(CAstEntity entity) {
|
|
assert this.extracted_entity == null : "Cannot reset extracted entity.";
|
|
extracted_entity = entity;
|
|
}
|
|
|
|
public CAstEntity getExtractedEntity() {
|
|
assert extracted_entity != null : "Extracted entity not set.";
|
|
return extracted_entity;
|
|
}
|
|
|
|
public void setCallSite(CAstNode callsite) {
|
|
assert this.callsite == null : "Cannot reset call site.";
|
|
this.callsite = callsite;
|
|
}
|
|
|
|
public CAstNode getCallSite() {
|
|
assert callsite != null : "Call site not set.";
|
|
return callsite;
|
|
}
|
|
|
|
@Override
|
|
public <A> A accept(PosSwitch<A> ps) {
|
|
return ps.caseForInLoopBodyPos(this);
|
|
}
|
|
|
|
// return the outermost enclosing extraction position around 'pos' within the same function; "null" if there is none
|
|
public static ExtractionPos getOutermostEnclosingExtractionPos(NodePos pos) {
|
|
return pos.accept(new PosSwitch<ExtractionPos>() {
|
|
@Override
|
|
public ExtractionPos caseRootPos(RootPos pos) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public ExtractionPos caseChildPos(ChildPos pos) {
|
|
int kind = pos.getParent().getKind();
|
|
if(kind == CAstNode.FUNCTION_STMT || kind == CAstNode.FUNCTION_EXPR)
|
|
return null;
|
|
return getOutermostEnclosingExtractionPos(pos.getParentPos());
|
|
}
|
|
|
|
@Override
|
|
public ExtractionPos caseForInLoopBodyPos(ExtractionPos pos) {
|
|
ExtractionPos outer = getEnclosingExtractionPos(pos.getParentPos());
|
|
return outer == null ? pos : outer;
|
|
}
|
|
|
|
@Override
|
|
public ExtractionPos caseLabelPos(LabelPos pos) {
|
|
return getOutermostEnclosingExtractionPos(pos.getParentPos());
|
|
}
|
|
});
|
|
}
|
|
|
|
// return the innermost enclosing extraction position around 'pos' within the same function; "null" if there is none
|
|
public static ExtractionPos getEnclosingExtractionPos(NodePos pos) {
|
|
return pos.accept(new PosSwitch<ExtractionPos>() {
|
|
@Override
|
|
public ExtractionPos caseRootPos(RootPos pos) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public ExtractionPos caseChildPos(ChildPos pos) {
|
|
int kind = pos.getParent().getKind();
|
|
if(kind == CAstNode.FUNCTION_STMT || kind == CAstNode.FUNCTION_EXPR)
|
|
return null;
|
|
return getEnclosingExtractionPos(pos.getParentPos());
|
|
}
|
|
|
|
@Override
|
|
public ExtractionPos caseForInLoopBodyPos(ExtractionPos pos) {
|
|
return pos;
|
|
}
|
|
|
|
@Override
|
|
public ExtractionPos caseLabelPos(LabelPos pos) {
|
|
return getEnclosingExtractionPos(pos.getParentPos());
|
|
}
|
|
});
|
|
}
|
|
|
|
// is this the outermost for-in loop within its enclosing function?
|
|
public boolean isOutermost() {
|
|
return getEnclosingExtractionPos(parent_pos) == null;
|
|
}
|
|
|
|
public NodePos getParentPos() {
|
|
return parent_pos;
|
|
}
|
|
}
|