/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * 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 * Zend Technologies - adapt for PHP refactoring *******************************************************************************/ package org.eclipse.php.refactoring.core.code.flow; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.text.IRegion; import org.eclipse.php.core.ast.nodes.*; import org.eclipse.php.internal.core.corext.dom.Selection; public class InputFlowAnalyzer extends FlowAnalyzer { private static class LoopReentranceVisitor extends FlowAnalyzer { private Selection fSelection; private ASTNode fLoopNode; public LoopReentranceVisitor(FlowContext context, Selection selection, ASTNode loopNode) { super(context); fSelection = selection; fLoopNode = loopNode; } protected boolean traverseNode(ASTNode node) { return true; // end <= fSelection.end || // fSelection.enclosedBy(start, end); } protected boolean createReturnFlowInfo(ReturnStatement node) { // Make sure that the whole return statement is selected or located // before the selection. return node.getEnd() <= fSelection.getExclusiveEnd(); } protected ASTNode getLoopNode() { return fLoopNode; } public void process(ASTNode node) { try { fFlowContext.setLoopReentranceMode(true); node.accept(this); } finally { fFlowContext.setLoopReentranceMode(false); } } public void endVisit(DoStatement node) { if (skipNode(node)) return; DoWhileFlowInfo info = createDoWhile(); setFlowInfo(node, info); info.mergeAction(getFlowInfo(node.getBody()), fFlowContext); // No need to merge the condition. It was already considered by the // InputFlowAnalyzer. info.removeLabel(null); } // public void endVisit(EnhancedForStatement node) { // if (skipNode(node)) // return; // FlowInfo paramInfo= getFlowInfo(node.getParameter()); // FlowInfo expressionInfo= getFlowInfo(node.getExpression()); // FlowInfo actionInfo= getFlowInfo(node.getBody()); // EnhancedForFlowInfo forInfo= createEnhancedFor(); // setFlowInfo(node, forInfo); // // If the for statement is the outermost loop then we only have to // consider // // the action. The parameter and expression are only evaluated once. // if (node == fLoopNode) { // forInfo.mergeAction(actionInfo, fFlowContext); // } else { // // Inner for loops are evaluated in the sequence expression, // parameter, // // action. // forInfo.mergeExpression(expressionInfo, fFlowContext); // forInfo.mergeParameter(paramInfo, fFlowContext); // forInfo.mergeAction(actionInfo, fFlowContext); // } // forInfo.removeLabel(null); // } public void endVisit(ForStatement node) { if (skipNode(node)) return; FlowInfo initInfo = createSequential(node.initializers()); FlowInfo conditionInfo = createSequential(node.conditions()); FlowInfo incrementInfo = createSequential(node.updaters()); FlowInfo actionInfo = getFlowInfo(node.getBody()); ForFlowInfo forInfo = createFor(); setFlowInfo(node, forInfo); // the for statement is the outermost loop. In this case we only // have // to consider the increment, condition and action. if (node == fLoopNode) { forInfo.mergeIncrement(incrementInfo, fFlowContext); forInfo.mergeCondition(conditionInfo, fFlowContext); forInfo.mergeAction(actionInfo, fFlowContext); } else { // we have to merge two different cases. One if we reenter the // for statement // immediatelly (that means we have to consider increments, // condition and action) // and the other case if we reenter the for in the next loop of // the outer loop. Then we have to consider initializations, // condtion and action. // For a conditional flow info that means: // (initializations | increments) & condition & action. GenericConditionalFlowInfo initIncr = new GenericConditionalFlowInfo(); initIncr.merge(initInfo, fFlowContext); initIncr.merge(incrementInfo, fFlowContext); forInfo.mergeAccessModeSequential(initIncr, fFlowContext); forInfo.mergeCondition(conditionInfo, fFlowContext); forInfo.mergeAction(actionInfo, fFlowContext); } forInfo.removeLabel(null); } } private Selection fSelection; private boolean fDoLoopReentrance; private LoopReentranceVisitor fLoopReentranceVisitor; public InputFlowAnalyzer(FlowContext context, Selection selection, boolean doLoopReentrance) { super(context); fSelection = selection; Assert.isNotNull(fSelection); fDoLoopReentrance = doLoopReentrance; } public FlowInfo perform(ASTNode node) { // Assert.isTrue(!(node instanceof AbstractTypeDeclaration)); node.accept(this); return getFlowInfo(node); } protected boolean traverseNode(ASTNode node) { return node.getEnd() > fSelection.getInclusiveEnd(); } protected boolean createReturnFlowInfo(ReturnStatement node) { // Make sure that the whole return statement is located after the // selection. There can be cases like // return i + [x + 10] * 10; In this case we must not create a return // info node. return node.getStart() >= fSelection.getInclusiveEnd(); } public boolean visit(DoStatement node) { createLoopReentranceVisitor(node); return super.visit(node); } // public boolean visit(EnhancedForStatement node) { // createLoopReentranceVisitor(node); // return super.visit(node); // } public boolean visit(ForStatement node) { createLoopReentranceVisitor(node); return super.visit(node); } public boolean visit(WhileStatement node) { createLoopReentranceVisitor(node); return super.visit(node); } private void createLoopReentranceVisitor(ASTNode node) { if (fLoopReentranceVisitor == null && fDoLoopReentrance) fLoopReentranceVisitor = new LoopReentranceVisitor(fFlowContext, fSelection, node); } public void endVisit(ConditionalExpression node) { if (skipNode(node)) return; Expression thenPart = node.getIfTrue(); Expression elsePart = node.getIfFalse(); if ((thenPart != null && fSelection.coveredBy(thenPart)) || (elsePart != null && fSelection.coveredBy(elsePart))) { GenericSequentialFlowInfo info = createSequential(); setFlowInfo(node, info); endVisitConditional(info, node.getCondition(), new ASTNode[] { thenPart, elsePart }); } else { super.endVisit(node); } } public void endVisit(DoStatement node) { super.endVisit(node); handleLoopReentrance(node); } public void endVisit(IfStatement node) { if (skipNode(node)) return; Statement thenPart = node.getTrueStatement(); Statement elsePart = node.getFalseStatement(); if ((thenPart != null && fSelection.coveredBy(thenPart)) || (elsePart != null && fSelection.coveredBy(elsePart))) { GenericSequentialFlowInfo info = createSequential(); setFlowInfo(node, info); endVisitConditional(info, node.getCondition(), new ASTNode[] { thenPart, elsePart }); } else { super.endVisit(node); } } // public void endVisit(EnhancedForStatement node) { // super.endVisit(node); // handleLoopReentrance(node); // } public void endVisit(ForStatement node) { super.endVisit(node); handleLoopReentrance(node); } public void endVisit(SwitchStatement node) { if (skipNode(node)) return; SwitchData data = createSwitchData(node); IRegion[] ranges = data.getRanges(); for (int i = 0; i < ranges.length; i++) { IRegion range = ranges[i]; if (fSelection.coveredBy(range)) { GenericSequentialFlowInfo info = createSequential(); setFlowInfo(node, info); info.merge(getFlowInfo(node.getExpression()), fFlowContext); info.merge(data.getInfo(i), fFlowContext); info.removeLabel(null); return; } } super.endVisit(node, data); } public void endVisit(WhileStatement node) { super.endVisit(node); handleLoopReentrance(node); } private void endVisitConditional(GenericSequentialFlowInfo info, ASTNode condition, ASTNode[] branches) { info.merge(getFlowInfo(condition), fFlowContext); for (int i = 0; i < branches.length; i++) { ASTNode branch = branches[i]; if (branch != null && fSelection.coveredBy(branch)) { info.merge(getFlowInfo(branch), fFlowContext); break; } } } private void handleLoopReentrance(ASTNode node) { if (!fSelection.coveredBy(node) || fLoopReentranceVisitor == null || fLoopReentranceVisitor.getLoopNode() != node) return; fLoopReentranceVisitor.process(node); GenericSequentialFlowInfo info = createSequential(); info.merge(getFlowInfo(node), fFlowContext); info.merge(fLoopReentranceVisitor.getFlowInfo(node), fFlowContext); setFlowInfo(node, info); } }