/******************************************************************************* * 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; } }