| /******************************************************************************* |
| * Copyright (c) 2000, 2009 IBM Corporation and others. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for |
| * o inline call that is used in a field initializer |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137) |
| * Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] Missing return value, while extracting code out of a loop - https://bugs.eclipse.org/bugs/show_bug.cgi?id=213519 |
| *******************************************************************************/ |
| package org.eclipse.dltk.internal.javascript.corext.refactoring.code.flow; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.dltk.internal.javascript.corext.refactoring.util.Selection; |
| import org.eclipse.dltk.javascript.core.dom.ConditionalExpression; |
| import org.eclipse.dltk.javascript.core.dom.DoStatement; |
| import org.eclipse.dltk.javascript.core.dom.Expression; |
| import org.eclipse.dltk.javascript.core.dom.ForEachInStatement; |
| import org.eclipse.dltk.javascript.core.dom.ForInStatement; |
| import org.eclipse.dltk.javascript.core.dom.ForStatement; |
| import org.eclipse.dltk.javascript.core.dom.IfStatement; |
| import org.eclipse.dltk.javascript.core.dom.IterationStatement; |
| import org.eclipse.dltk.javascript.core.dom.Node; |
| import org.eclipse.dltk.javascript.core.dom.ReturnStatement; |
| import org.eclipse.dltk.javascript.core.dom.Statement; |
| import org.eclipse.dltk.javascript.core.dom.SwitchStatement; |
| import org.eclipse.dltk.javascript.core.dom.WhileStatement; |
| import org.eclipse.jface.text.IRegion; |
| |
| public class InputFlowAnalyzer extends FlowAnalyzer { |
| private static class LoopReentranceVisitor extends FlowAnalyzer { |
| private Selection fSelection; |
| private Node fLoopNode; |
| public LoopReentranceVisitor(FlowContext context, Selection selection, Node loopNode) { |
| super(context); |
| fSelection= selection; |
| fLoopNode= loopNode; |
| } |
| protected boolean isTraverseNeeded(Node node) { |
| return true; |
| } |
| 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 Node getLoopNode() { |
| return fLoopNode; |
| } |
| public void process(Node node) { |
| try { |
| //fFlowContext.setLoopReentranceMode(true); |
| traverse(node); |
| } finally { |
| //fFlowContext.setLoopReentranceMode(false); |
| } |
| } |
| public Boolean caseDoStatement(DoStatement node) { |
| 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); |
| return true; |
| } |
| |
| public Boolean caseForInStatement(ForInStatement node) { |
| FlowInfo paramInfo= getFlowInfo(node.getItem()); |
| FlowInfo expressionInfo= getFlowInfo(node.getCollection()); |
| FlowInfo actionInfo= getFlowInfo(node.getBody()); |
| processForIn(node, paramInfo, expressionInfo, actionInfo); |
| return true; |
| } |
| |
| public Boolean caseForEachInStatement(ForEachInStatement node) { |
| FlowInfo paramInfo= getFlowInfo(node.getItem()); |
| FlowInfo expressionInfo= getFlowInfo(node.getCollection()); |
| FlowInfo actionInfo= getFlowInfo(node.getBody()); |
| processForIn(node, paramInfo, expressionInfo, actionInfo); |
| return true; |
| } |
| |
| private void processForIn(Node node, FlowInfo paramInfo, |
| FlowInfo expressionInfo, FlowInfo actionInfo) { |
| ForInFlowInfo forInfo= createForIn(); |
| 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 Boolean caseForStatement(ForStatement node) { |
| FlowInfo initInfo= createSequential(node.getInitialization()); |
| FlowInfo conditionInfo= getFlowInfo(node.getCondition()); |
| FlowInfo incrementInfo= createSequential(node.getIncrement()); |
| 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); |
| return true; |
| } |
| } |
| |
| 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(Node node) { |
| //Assert.isTrue(!(node instanceof AbstractTypeDeclaration)); |
| this.traverse(node); |
| return getFlowInfo(node); |
| } |
| |
| protected boolean isTraverseNeeded(Node node) { |
| return node.getEnd() > fSelection.getExclusiveEnd(); |
| } |
| |
| 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.getBegin() >= fSelection.getInclusiveEnd(); |
| } |
| |
| protected void traverse(Node node) { |
| if (isTraverseNeeded(node) && (node instanceof IterationStatement)) { |
| createLoopReentranceVisitor(node); |
| } |
| super.traverse(node); |
| } |
| |
| private void createLoopReentranceVisitor(Node node) { |
| if (fLoopReentranceVisitor == null && fDoLoopReentrance && fSelection.coveredBy(node)) |
| fLoopReentranceVisitor= new LoopReentranceVisitor(fFlowContext, fSelection, node); |
| } |
| |
| public Boolean caseConditionalExpression(ConditionalExpression node) { |
| Expression thenPart= node.getConsequent(); |
| Expression elsePart= node.getAlternative(); |
| if ((thenPart != null && fSelection.coveredBy(thenPart)) || |
| (elsePart != null && fSelection.coveredBy(elsePart))) { |
| GenericSequentialFlowInfo info= createSequential(); |
| setFlowInfo(node, info); |
| endVisitConditional(info, node.getPredicate(), new Node[] {thenPart, elsePart}); |
| } else { |
| super.caseConditionalExpression(node); |
| } |
| return true; |
| } |
| |
| public Boolean caseDoStatement(DoStatement node) { |
| super.caseDoStatement(node); |
| handleLoopReentrance(node); |
| return true; |
| } |
| |
| public Boolean caseIfStatement(IfStatement node) { |
| Statement thenPart= node.getConsequent(); |
| Statement elsePart= node.getAlternative(); |
| if ((thenPart != null && fSelection.coveredBy(thenPart)) || |
| (elsePart != null && fSelection.coveredBy(elsePart))) { |
| GenericSequentialFlowInfo info= createSequential(); |
| setFlowInfo(node, info); |
| endVisitConditional(info, node.getPredicate(), new Node[] {thenPart, elsePart}); |
| } else { |
| super.caseIfStatement(node); |
| } |
| return true; |
| } |
| |
| public Boolean caseForInStatement(ForInStatement node) { |
| super.caseForInStatement(node); |
| handleLoopReentrance(node); |
| return true; |
| } |
| |
| public Boolean caseForEachInStatement(ForEachInStatement node) { |
| super.caseForEachInStatement(node); |
| handleLoopReentrance(node); |
| return true; |
| } |
| |
| public Boolean caseForStatement(ForStatement node) { |
| super.caseForStatement(node); |
| handleLoopReentrance(node); |
| return true; |
| } |
| |
| public Boolean caseSwitchStatement(SwitchStatement node) { |
| 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.getSelector()), fFlowContext); |
| info.merge(data.getInfo(i), fFlowContext); |
| info.removeLabel(null); |
| return true; |
| } |
| } |
| super.caseSwitchStatement(node, data); |
| return true; |
| } |
| |
| public Boolean caseWhileStatement(WhileStatement node) { |
| super.caseWhileStatement(node); |
| handleLoopReentrance(node); |
| return true; |
| } |
| |
| private void endVisitConditional(GenericSequentialFlowInfo info, Node condition, Node[] branches) { |
| info.merge(getFlowInfo(condition), fFlowContext); |
| for (int i= 0; i < branches.length; i++) { |
| Node branch= branches[i]; |
| if (branch != null && fSelection.coveredBy(branch)) { |
| info.merge(getFlowInfo(branch), fFlowContext); |
| break; |
| } |
| } |
| } |
| |
| private void handleLoopReentrance(Node node) { |
| if (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); |
| } |
| } |