| /* |
| * Copyright 2000-2010 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.codeInsight.controlflow; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.util.Function; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.IntStack; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.Arrays; |
| |
| /** |
| * @author oleg |
| */ |
| public class ControlFlowUtil { |
| private static final Logger LOG = Logger.getInstance(ControlFlowUtil.class.getName()); |
| |
| private ControlFlowUtil() { |
| } |
| |
| public static int[] postOrder(Instruction[] flow) { |
| final int length = flow.length; |
| int[] result = new int[length]; |
| boolean[] visited = new boolean[length]; |
| Arrays.fill(visited, false); |
| final IntStack stack = new IntStack(length); |
| |
| int N = 0; |
| for (int i = 0; i < length; i++) { //graph might not be connected |
| if (!visited[i]) { |
| visited[i] = true; |
| stack.clear(); |
| stack.push(i); |
| |
| while (!stack.empty()) { |
| final int num = stack.pop(); |
| result[N++] = num; |
| for (Instruction succ : flow[num].allSucc()) { |
| final int succNum = succ.num(); |
| if (!visited[succNum]) { |
| visited[succNum] = true; |
| stack.push(succNum); |
| } |
| } |
| } |
| } |
| } |
| LOG.assertTrue(N == length); |
| return result; |
| } |
| |
| public static int findInstructionNumberByElement(final Instruction[] flow, final PsiElement element){ |
| for (int i=0;i<flow.length;i++) { |
| // Check if canceled |
| ProgressManager.checkCanceled(); |
| |
| if (element == flow[i].getElement()){ |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Process control flow graph in depth first order |
| */ |
| public static boolean process(final Instruction[] flow, final int start, final Processor<Instruction> processor){ |
| final int length = flow.length; |
| boolean[] visited = new boolean[length]; |
| Arrays.fill(visited, false); |
| |
| final IntStack stack = new IntStack(length); |
| stack.push(start); |
| |
| while (!stack.empty()) { |
| ProgressManager.checkCanceled(); |
| final int num = stack.pop(); |
| final Instruction instruction = flow[num]; |
| if (!processor.process(instruction)){ |
| return false; |
| } |
| for (Instruction succ : instruction.allSucc()) { |
| final int succNum = succ.num(); |
| if (!visited[succNum]) { |
| visited[succNum] = true; |
| stack.push(succNum); |
| } |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Iterates over write instructions in CFG with reversed order |
| */ |
| public static void iteratePrev(final int startInstruction, |
| @NotNull final Instruction[] instructions, |
| @NotNull final Function<Instruction, Operation> closure) { |
| final IntStack stack = new IntStack(instructions.length); |
| final boolean[] visited = new boolean[instructions.length]; |
| |
| stack.push(startInstruction); |
| while (!stack.empty()) { |
| ProgressManager.checkCanceled(); |
| final int num = stack.pop(); |
| final Instruction instr = instructions[num]; |
| final Operation nextOperation = closure.fun(instr); |
| // Just ignore previous instructions for current node and move further |
| if (nextOperation == Operation.CONTINUE) { |
| continue; |
| } |
| // STOP iteration |
| if (nextOperation == Operation.BREAK) { |
| break; |
| } |
| // If we are here, we should process previous nodes in natural way |
| assert nextOperation == Operation.NEXT; |
| for (Instruction pred : instr.allPred()) { |
| final int predNum = pred.num(); |
| if (!visited[predNum]) { |
| visited[predNum] = true; |
| stack.push(predNum); |
| } |
| } |
| } |
| } |
| |
| public static enum Operation { |
| /** |
| * CONTINUE is used to ignore previous elements processing for the node, however it doesn't stop the iteration process |
| */ |
| CONTINUE, |
| /** |
| * BREAK is used to stop iteration process |
| */ |
| BREAK, |
| /** |
| * NEXT is used to indicate that iteration should be continued in natural way |
| */ |
| NEXT |
| } |
| } |