/*
* Copyright 2011 Jon S Akhtar (Sylvanaar)
*
* 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.sylvanaar.idea.Lua.lang.psi.controlFlow;
import com.intellij.openapi.diagnostic.Logger;
import gnu.trove.TIntHashSet;
import gnu.trove.TObjectIntHashMap;
import java.util.ArrayList;
import java.util.List;
public class ControlFlowUtil {
private static final Logger LOG = Logger.getInstance("Lua.ControlFlowUtil");
public static int[] postorder(Instruction[] flow) {
int[] result = new int[flow.length];
boolean[] visited = new boolean[flow.length];
for (int i = 0; i < result.length; i++)
visited[i] = false;
int N = flow.length;
for (int i = 0; i < flow.length; i++) { //graph might not be connected
if (!visited[i])
N = doVisitForPostorder(flow[i], N, result, visited);
}
LOG.assertTrue(N == 0);
return result;
}
private static int doVisitForPostorder(Instruction curr, int currN, int[] postorder, boolean[] visited) {
visited[curr.num()] = true;
for (Instruction succ : curr.allSucc()) {
if (!visited[succ.num()]) {
currN = doVisitForPostorder(succ, currN, postorder, visited);
}
}
postorder[curr.num()] = --currN;
return currN;
}
public static ReadWriteVariableInstruction[] getReadsWithoutPriorWrites(Instruction[] flow) {
List<ReadWriteVariableInstruction> result = new ArrayList<ReadWriteVariableInstruction>();
TObjectIntHashMap<String> namesIndex = buildNamesIndex(flow);
TIntHashSet[] definitelyAssigned = new TIntHashSet[flow.length];
int[] postorder = postorder(flow);
int[] invpostorder = invPostorder(postorder);
findReadsBeforeWrites(flow, definitelyAssigned, result, namesIndex, postorder, invpostorder);
return result.toArray(new ReadWriteVariableInstruction[result.size()]);
}
private static int[] invPostorder(int[] postorder) {
int[] result = new int[postorder.length];
for (int i = 0; i < postorder.length; i++) {
result[postorder[i]] = i;
}
return result;
}
private static TObjectIntHashMap<String> buildNamesIndex(Instruction[] flow) {
TObjectIntHashMap<String> namesIndex = new TObjectIntHashMap<String>();
int idx = 0;
for (Instruction instruction : flow) {
if (instruction instanceof ReadWriteVariableInstruction) {
String name = ((ReadWriteVariableInstruction) instruction).getVariableName();
if (!namesIndex.contains(name)) {
namesIndex.put(name, idx++);
}
}
}
return namesIndex;
}
private static void findReadsBeforeWrites(Instruction[] flow,
TIntHashSet[] definitelyAssigned,
List<ReadWriteVariableInstruction> result,
TObjectIntHashMap<String> namesIndex,
int[] postorder,
int[] invpostorder) {
//skip instructions that are not reachable from the start
int start = 0;
while (invpostorder[start] != 0)
start++;
for (int i = start; i < flow.length; i++) {
int j = invpostorder[i];
Instruction curr = flow[j];
if (curr instanceof ReadWriteVariableInstruction) {
ReadWriteVariableInstruction readWriteInsn = (ReadWriteVariableInstruction) curr;
int idx = namesIndex.get(readWriteInsn.getVariableName());
TIntHashSet vars = definitelyAssigned[j];
if (readWriteInsn.isGlobal()) {
if (!readWriteInsn.isWrite()) {
if (vars == null || !vars.contains(idx)) {
result.add(readWriteInsn);
}
} else {
if (vars == null) {
vars = new TIntHashSet();
definitelyAssigned[j] = vars;
}
vars.add(idx);
}
}
}
for (Instruction succ : curr.allSucc()) {
if (postorder[succ.num()] > postorder[curr.num()]) {
TIntHashSet currDefinitelyAssigned = definitelyAssigned[curr.num()];
TIntHashSet succDefinitelyAssigned = definitelyAssigned[succ.num()];
if (currDefinitelyAssigned != null) {
int[] currArray = currDefinitelyAssigned.toArray();
if (succDefinitelyAssigned == null) {
succDefinitelyAssigned = new TIntHashSet();
succDefinitelyAssigned.addAll(currArray);
definitelyAssigned[succ.num()] = succDefinitelyAssigned;
} else {
succDefinitelyAssigned.retainAll(currArray);
}
} else {
if (succDefinitelyAssigned != null) {
succDefinitelyAssigned.clear();
} else {
succDefinitelyAssigned = new TIntHashSet();
definitelyAssigned[succ.num()] = succDefinitelyAssigned;
}
}
}
}
}
}
}