WALA/com.ibm.wala.cast.js/source/com/ibm/wala/cast/js/ipa/callgraph/correlations/extraction/ExtractionPos.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;
}
}