/*
* Copyright 2003-2011 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 jetbrains.mps.lang.dataFlow.framework;
import jetbrains.mps.lang.dataFlow.framework.instructions.*;
import jetbrains.mps.lang.dataFlow.framework.analyzers.ReachabilityAnalyzer;
import jetbrains.mps.lang.dataFlow.framework.analyzers.InitializedVariablesAnalyzer;
import jetbrains.mps.lang.dataFlow.framework.analyzers.LivenessAnalyzer;
import jetbrains.mps.lang.dataFlow.framework.analyzers.MayBeInitializedVariablesAnalyzer;
import java.util.*;
import java.util.Map.Entry;
public class Program {
protected final List<Instruction> myInstructions = new ArrayList<Instruction>();
protected final List<TryFinallyInfo> myTryFinallyInfo = new ArrayList<TryFinallyInfo>();
protected final Map<Object, Integer> myStarts = new HashMap<Object, Integer>();
protected final Map<Object, Integer> myEnds = new HashMap<Object, Integer>();
protected final Stack<Object> myCreationStack = new Stack<Object>();
protected List<Object> myVariables = new ArrayList<Object>();
protected boolean hasOuterJumps = false;
public List<Instruction> getInstructions() {
return Collections.unmodifiableList(myInstructions);
}
public List<ProgramState> getStates() {
List<ProgramState> result = new ArrayList<ProgramState>();
for (Instruction i : myInstructions) {
result.add(new ProgramState(i, true));
result.add(new ProgramState(i, false));
}
return result;
}
public Instruction get(int index) {
return myInstructions.get(index);
}
public int size() {
return myInstructions.size();
}
public int indexOf(Instruction i) {
return myInstructions.indexOf(i);
}
public Instruction getStart() {
return myInstructions.get(0);
}
public Instruction getEnd() {
return myInstructions.get(myInstructions.size() - 1);
}
public <E> AnalysisResult<E> analyze(DataFlowAnalyzer<E> analyzer) {
return new AnalyzerRunner<E>(this, analyzer).analyze();
}
public List<Object> getVariables() {
return new ArrayList<Object>(myVariables);
}
public int getVariablesCount() {
return myVariables.size();
}
public int getVariableIndex(Object var) {
return myVariables.indexOf(var);
}
public Object getVariable(int index) {
return myVariables.get(index);
}
protected void add(Instruction instruction) {
instruction.setProgram(this);
instruction.setSource(getCurrent());
instruction.setIndex(myInstructions.size());
myInstructions.add(instruction);
}
@Deprecated
public void insert(Instruction instruction, int position, boolean update) {
insert(instruction, position, update, true);
}
public void insert(Instruction instruction, int position, boolean update, boolean before) {
instruction.setProgram(this);
if (instruction.getSource() == null) {
instruction.setSource(myInstructions.get(position - 1).getSource());
}
instruction.setIndex(position);
for (Instruction i : myInstructions.subList(position, myInstructions.size())) {
i.setIndex(i.getIndex() + 1);
}
for (Entry<Object, Integer> e : myEnds.entrySet()) {
if (e.getValue() > position) {
e.setValue(e.getValue() + 1);
}
}
for (Entry<Object, Integer> e : myStarts.entrySet()) {
if (e.getValue() >= position) {
e.setValue(e.getValue() + 1);
}
}
myInstructions.add(position, instruction);
if (update) {
updateJumpsOnInsert(position, before);
}
}
protected void start(Object o) {
if (myCreationStack.contains(o)) {
throw new RuntimeException("Cycle!");
}
myCreationStack.push(o);
myStarts.put(o, getCurrentPosition());
}
protected void end(Object o) {
if (myCreationStack.isEmpty() || myCreationStack.peek() != o) {
throw new RuntimeException("Unexpected end");
}
myCreationStack.pop();
myEnds.put(o, getCurrentPosition());
}
protected Object getCurrent() {
if (myCreationStack.isEmpty()) {
return null;
}
return myCreationStack.peek();
}
protected int getCurrentPosition() {
return myInstructions.size();
}
public int getStart(Object o) {
if (!myStarts.containsKey(o)) {
throw new DataflowBuilderException("Can't find a start of node " + o);
}
return myStarts.get(o);
}
public int getEnd(Object o) {
if (!myEnds.containsKey(o)) {
throw new DataflowBuilderException("Can't find an end of node " + o);
}
return myEnds.get(o);
}
public List<Instruction> getInstructionsFor(Object o) {
if (myStarts.containsKey(o)) {
int start = getStart(o);
int end = getEnd(o);
if (start <= end) {
return new ArrayList<Instruction>(myInstructions.subList(start, end));
}
}
return new ArrayList<Instruction>();
}
protected void init() {
add(new EndInstruction());
collectVariables();
buildBlockInfos();
buildInstructionCaches();
sanityCheck();
}
protected void buildInstructionCaches() {
for (Instruction i : myInstructions) {
i.buildCaches();
}
}
protected void collectVariables() {
Set<Object> result = new LinkedHashSet<Object>();
for (Instruction i : myInstructions) {
if (i instanceof ReadInstruction) {
result.add(((ReadInstruction) i).getVariable());
}
if (i instanceof WriteInstruction) {
result.add(((WriteInstruction) i).getVariable());
}
}
myVariables = new ArrayList<Object>(result);
}
protected void buildBlockInfos() {
Stack<TryFinallyInfo> stack = new Stack<TryFinallyInfo>();
for (Instruction i : myInstructions) {
if (i instanceof TryInstruction) {
TryFinallyInfo info = new TryFinallyInfo();
info.myTry = (TryInstruction) i;
myTryFinallyInfo.add(info);
if (!stack.isEmpty()) {
TryFinallyInfo parent = stack.peek();
info.myParent = parent;
parent.myChildren.add(info);
}
stack.push(info);
}
if (i instanceof FinallyInstruction) {
if (stack.isEmpty() || stack.peek().myFinally != null) {
throw new IllegalStateException("unexpected finally");
}
stack.peek().myFinally = (FinallyInstruction) i;
}
if (i instanceof EndTryInstruction) {
if (stack.isEmpty() ||
stack.peek().myEndTry != null) {
throw new IllegalStateException("unexpected endtry");
}
stack.peek().myEndTry = (EndTryInstruction) i;
stack.pop();
}
}
if (!stack.isEmpty()) {
throw new IllegalStateException("incomplete try blocks");
}
}
public Set<Instruction> getUnreachableInstructions() {
AnalysisResult<Boolean> analysisResult = analyze(new ReachabilityAnalyzer());
Set<Instruction> result = new HashSet<Instruction>();
for (Instruction i : myInstructions) {
if (!analysisResult.get(i)) {
result.add(i);
}
}
return result;
}
public Set<Instruction> getExpectedReturns() {
Set<Instruction> result = new HashSet<Instruction>();
AnalysisResult<Boolean> analysisResult = analyze(new ReachabilityAnalyzer());
ProgramState endWithoutReturn = new ProgramState(getEnd(), false);
if (analysisResult.get(endWithoutReturn)) {
for (ProgramState pred : endWithoutReturn.pred()) {
if (analysisResult.get(pred)) {
result.add(pred.getInstruction());
}
}
}
return result;
}
public Set<ReadInstruction> getUninitializedReads() {
AnalysisResult<VarSet> analysisResult = analyze(new InitializedVariablesAnalyzer());
Set<ReadInstruction> result = new HashSet<ReadInstruction>();
for (Instruction i : myInstructions) {
if (i instanceof ReadInstruction) {
ReadInstruction read = (ReadInstruction) i;
VarSet initializedVars = analysisResult.get(read);
if (!initializedVars.contains(read.getVariableIndex())) {
result.add(read);
}
}
}
return result;
}
public boolean isInitializedRewritten(WriteInstruction instruction) {
AnalysisResult<VarSet> analysisResult = analyze(new MayBeInitializedVariablesAnalyzer(instruction));
VarSet initializedVars = analysisResult.get(instruction);
return initializedVars.contains(instruction.getVariableIndex());
}
public Set<WriteInstruction> getUnusedAssignments() {
AnalysisResult<VarSet> analysisResult = analyze(new LivenessAnalyzer());
Set<WriteInstruction> retModeTrue = new HashSet<WriteInstruction>();
Set<WriteInstruction> retModeFalse = new HashSet<WriteInstruction>();
for (ProgramState s : analysisResult.getStates()) {
if (s.getInstruction() instanceof WriteInstruction) {
WriteInstruction write = (WriteInstruction) s.getInstruction();
VarSet liveAfter = new VarSet(this);
for (ProgramState succ : s.succ()) {
liveAfter.addAll(analysisResult.get(succ));
}
if (!liveAfter.contains(write.getVariableIndex())) {
if (s.isReturnMode()) {
retModeTrue.add(write);
} else {
retModeFalse.add(write);
}
}
}
}
retModeFalse.retainAll(retModeTrue);
return retModeFalse;
}
public String toString() {
return toString(false);
}
public String toString(boolean showSource) {
StringBuilder result = new StringBuilder();
for (Instruction instruction : myInstructions) {
result.append(instruction);
if (instruction.getSource() != null && showSource) {
result.append(" ").append(instruction.getSource());
}
result.append("\n");
}
return result.toString();
}
public List<TryFinallyInfo> getBlockInfos() {
return Collections.unmodifiableList(myTryFinallyInfo);
}
public ProgramState getState(int n) {
return new ProgramState(myInstructions.get(n >> 1), (n & 1) == 0);
}
protected void sanityCheck() {
for (Instruction i : myInstructions) {
sanityCheck(new ProgramState(i, true));
sanityCheck(new ProgramState(i, false));
}
}
protected void sanityCheck(ProgramState ps) {
for (ProgramState pred : ps.pred()) {
if (!pred.succ().contains(ps)) {
ps.pred();
pred.succ();
throw new RuntimeException("\n" + this.toString());
}
}
for (ProgramState succ : ps.succ()) {
if (!succ.pred().contains(ps)) {
ps.succ();
succ.pred();
throw new RuntimeException("\n" + this.toString());
}
}
}
public class TryFinallyInfo {
protected TryInstruction myTry;
protected FinallyInstruction myFinally;
protected EndTryInstruction myEndTry;
protected TryFinallyInfo myParent;
protected List<TryFinallyInfo> myChildren = new ArrayList<TryFinallyInfo>();
public TryInstruction getTry() {
return myTry;
}
public FinallyInstruction getFinally() {
return myFinally;
}
public EndTryInstruction getEndTry() {
return myEndTry;
}
public TryFinallyInfo getParent() {
return myParent;
}
public List<TryFinallyInfo> getChildren() {
return Collections.unmodifiableList(myChildren);
}
}
public boolean contains(Object o) {
Instruction i = null;
myInstructions.indexOf(i);
return myStarts.containsKey(o);
}
public boolean hasOuterJumps() {
return hasOuterJumps;
}
public void setHasOuterJumps(boolean hasOuterJumps) {
this.hasOuterJumps = hasOuterJumps;
}
public void updateJumpsOnInsert(int position, boolean before) {
for (Instruction i : myInstructions) {
if (i instanceof IfJumpInstruction) {
IfJumpInstruction ifJump = ((IfJumpInstruction) i);
int jumpTo = ifJump.getJumpTo();
if (jumpTo > position) {
ifJump.setJumpTo(jumpTo + 1);
} else if (jumpTo == position) {
if (before) {
ifJump.updateJumps(jumpTo + 1);
} else {
ifJump.setJumpTo(jumpTo + 1);
}
}
} else if (i instanceof JumpInstruction) {
JumpInstruction jump = ((JumpInstruction) i);
int jumpTo = jump.getJumpTo();
if (jumpTo > position) {
jump.setJumpTo(jumpTo + 1);
} else if (jumpTo == position) {
jump.updateJumps(jumpTo + 1);
if (before) {
jump.updateJumps(jumpTo + 1);
} else {
jump.setJumpTo(jumpTo + 1);
}
}
}
}
}
public Program copy() {
Program program = new Program();
for (Instruction i : myInstructions) {
program.add(i);
}
return program;
}
}