blob: 3118ca348409c5740364939abc68d8146710c962 [file] [log] [blame]
/*
* 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
}
}