//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.IO;
using Microsoft.Boogie;
using Microsoft.Boogie.GraphUtil;
using System.Diagnostics.Contracts;
using Microsoft.Basetypes;
using Microsoft.Boogie.VCExprAST;
namespace VC {
public partial class DCGen : ConditionGeneration {
private bool _print_time = CommandLineOptions.Clo.DoomStrategy!=-1;
#region Attributes
static private Dictionary/*!*/ m_BlockReachabilityMap;
Dictionary/*!*/ m_copiedBlocks = new Dictionary();
const string reachvarsuffix = "__ivebeenthere";
List/*!*/ m_doomedCmds = new List();
[ContractInvariantMethod]
void ObjectInvariant() {
}
#endregion
///
/// Constructor. Initializes the theorem prover.
///
public DCGen(Program program, string/*?*/ logFilePath, bool appendLogFile, List checkers)
: base(program, checkers) {
Contract.Requires(program != null);
this.appendLogFile = appendLogFile;
this.logFilePath = logFilePath;
m_BlockReachabilityMap = new Dictionary();
}
///
/// Debug method that prints a dot file of the
/// current set of blocks in impl to filename.
///
private void Impl2Dot(Implementation impl, string filename) {
Contract.Requires(impl != null);
Contract.Requires(filename != null);
List nodes = new List();
List edges = new List();
string nodestyle = "[shape=box];";
foreach (Block b in impl.Blocks) {
Contract.Assert(b != null);
nodes.Add(string.Format("\"{0}\" {1}", b.Label, nodestyle));
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null)
{
Contract.Assert(gc.labelTargets != null);
foreach (Block b_ in gc.labelTargets)
{
Contract.Assert(b_ != null);
edges.Add(String.Format("\"{0}\" -> \"{1}\";", b.Label, b_.Label));
}
}
//foreach (Block b_ in b.Predecessors)
//{
// edges.Add(String.Format("\"{0}\" -> \"{1}\";", b.Label, b_.Label));
//}
}
using (StreamWriter sw = new StreamWriter(filename)) {
sw.WriteLine(String.Format("digraph {0} {{", impl.Name));
// foreach (string! s in nodes) {
// sw.WriteLine(s);
// }
foreach (string s in edges) {
Contract.Assert(s != null);
sw.WriteLine(s);
}
sw.WriteLine("}}");
sw.Close();
}
}
private const string _copyPrefix = "CPY__";
private List m_UncheckableBlocks = null;
///
/// MSchaef:
/// - remove loops and add reach variables
/// - make it a passive program
/// - compute the wlp for each block
/// - check if |= (reach=false) => wlp.S.false holds for each reach
///
///
public override Outcome VerifyImplementation(Implementation impl, VerifierCallback callback) {
Contract.EnsuresOnThrow(true);
Console.WriteLine();
Console.WriteLine("Checking function {0}", impl.Name);
callback.OnProgress("doomdetector", 0, 0, 0);
bool restartTP = CommandLineOptions.Clo.DoomRestartTP ;
//Impl2Dot(impl, String.Format("c:/dot/{0}_orig.dot", impl.Name));
Transform4DoomedCheck(impl);
//Impl2Dot(impl, String.Format("c:/dot/{0}_fin.dot", impl.Name));
Checker checker = FindCheckerFor(1000);
Contract.Assert(checker != null);
int assertionCount;
DoomCheck dc = new DoomCheck(impl, this.exitBlock, checker, m_UncheckableBlocks, out assertionCount);
CumulativeAssertionCount += assertionCount;
//EmitImpl(impl, false);
int _totalchecks = 0;
ProverInterface.Outcome outcome;
dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback);
System.TimeSpan ts = new TimeSpan();
if (_print_time) Console.WriteLine("Total number of blocks {0}", impl.Blocks.Count);
List lb;
List lv = new List();
while (dc.GetNextBlock(out lb))
{
Contract.Assert(lb != null);
outcome = ProverInterface.Outcome.Undetermined;
Variable v = null;
lv.Clear();
foreach (Block b_ in lb)
{
if (!m_BlockReachabilityMap.TryGetValue(b_, out v))
{
// This should cause an error
continue;
}
//Console.Write("{0}, ",b_.Label);
lv.Add(v);
}
//Console.WriteLine();
Dictionary finalreachvars = m_GetPostconditionVariables(impl.Blocks,lb);
if (lv.Count < 1)
{
continue;
}
Contract.Assert(lv != null);
_totalchecks++;
if (!dc.CheckLabel(lv,finalreachvars, out outcome)) {
return Outcome.Inconclusive;
}
ts += dc.DEBUG_ProverTime.Elapsed;
if (restartTP)
{
checker.Close();
checker = FindCheckerFor(1000);
dc.RespawnChecker(impl, checker);
dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback);
}
}
checker.Close();
if (_print_time)
{
Console.WriteLine("Number of Checkes / #Blocks: {0} of {1}", _totalchecks, impl.Blocks.Count);
dc.__DEBUG_PrintStatistics();
Console.WriteLine("Total time for this method: {0}", ts.ToString());
}
#region Try to produce a counter example (brute force)
if (dc.DoomedSequences.Count > 0) {
int counter = 0;
List _all = new List();
foreach (List lb_ in dc.DoomedSequences)
{
foreach (Block b_ in lb_)
{
if (!_all.Contains(b_) && !m_UncheckableBlocks.Contains(b_))
{
_all.Add(b_); counter++;
if (!_print_time) Console.WriteLine(b_.Label);
}
}
}
if (_all.Count > 0)
{
Console.WriteLine("#Dead Blocks found: {0}: ", counter);
return Outcome.Errors;
}
}
#endregion
return Outcome.Correct;
}
private Dictionary m_GetPostconditionVariables(List allblock, List passBlock)
{
Dictionary r = new Dictionary();
foreach (Block b in allblock)
{
Variable v;
if (m_BlockReachabilityMap.TryGetValue(b, out v))
{
if (passBlock.Contains(b)) r[m_LastReachVarIncarnation[v]] = 1;
}
else
{
Console.WriteLine("there is no reachability variable for {0}", b.Label);
}
}
return r;
}
#if false
#region Error message construction
private void SearchCounterexample(Implementation impl, DoomErrorHandler errh, VerifierCallback callback) {
Contract.Requires(impl != null);
Contract.Requires(errh != null);
Contract.Requires(callback != null);
Contract.Requires(errh.m_Reachvar != null);
//if (errh.m_Reachvar==null) {
// Contract.Assert(false);throw new cce.UnreachableException();
//}
m_doomedCmds.Clear();
Dictionary> cmdbackup = new Dictionary>();
BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, 0, impl.Blocks.Count / 2 - 1);
BruteForceCESearch(errh.m_Reachvar, impl, callback, cmdbackup, impl.Blocks.Count / 2, impl.Blocks.Count - 1);
List causals = CollectCausalStatements(impl.Blocks[0]);
foreach (Cmd c in causals) {
Contract.Assert(c != null);
GenerateErrorMessage(c, causals);
}
#region Undo all modifications
foreach (KeyValuePair> kvp in cmdbackup) {
Contract.Assert(kvp.Key != null);
Contract.Assert(kvp.Value != null);
kvp.Key.Cmds = kvp.Value;
}
#endregion
}
#region Causal Statement Tree
private void GenerateErrorMessage(Cmd causalStatement, List causals) {
Contract.Requires(causalStatement != null);
Contract.Requires(cce.NonNullElements(causals));
AssumeCmd uc = causalStatement as AssumeCmd;
AssertCmd ac = causalStatement as AssertCmd;
ConsoleColor col = Console.ForegroundColor;
// Trivial case. Must be either assume or assert false
if (m_doomedCmds.Count == 1) {
Console.WriteLine("Found a trivial error:");
if (uc != null) {
Console.Write("Trivial false assumption: ");
Console.Write("({0};{1}):", uc.tok.line, uc.tok.col);
}
if (ac != null) {
Console.Write("Trivial false assertion: ");
Console.Write("({0};{1}):", ac.tok.line, ac.tok.col);
}
causalStatement.Emit(new TokenTextWriter("", Console.Out, false), 0);
} else {
// Safety error
if (ac != null) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Safety error:");
Console.ForegroundColor = col;
Console.Write("This assertion is violated: ");
Console.Write("({0};{1}):", ac.tok.line, ac.tok.col);
ac.Emit(new TokenTextWriter("", Console.Out, false), 0);
}
if (uc != null) {
bool containsAssert = false;
foreach (Cmd c in m_doomedCmds) {
Contract.Assert(c != null);
if (causals.Contains(c)) {
continue;
}
AssertCmd asrt = c as AssertCmd;
if (asrt != null) {
containsAssert = true;
break;
}
}
// Plausibility error
if (containsAssert) {
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Plausibility error:");
Console.ForegroundColor = col;
Console.Write("There is no legal exeuction passing: ");
Console.Write("({0};{1})", uc.tok.line, uc.tok.col);
uc.Emit(new TokenTextWriter("", Console.Out, false), 0);
} else { // Reachability error
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine("Reachability error:");
Console.ForegroundColor = col;
Console.Write("No execution can reach: ");
Console.Write("({0};{1})", uc.tok.line, uc.tok.col);
uc.Emit(new TokenTextWriter("", Console.Out, false), 0);
}
}
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("...on any execution passing through:");
foreach (Cmd c in m_doomedCmds) {
Contract.Assert(c != null);
if (causals.Contains(c)) {
continue;
}
Console.ForegroundColor = col;
Console.Write("In ({0};{1}): ", c.tok.line, c.tok.col);
Console.ForegroundColor = ConsoleColor.DarkYellow;
c.Emit(new TokenTextWriter("", Console.Out, false), 0);
}
Console.ForegroundColor = col;
Console.WriteLine("--");
}
}
private List CollectCausalStatements(Block b) {
Contract.Requires(b != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
Cmd lastCausal = null;
foreach (Cmd c in b.Cmds) {
Contract.Assert(c != null);
AssertCmd ac = c as AssertCmd;
AssumeCmd uc = c as AssumeCmd;
if (ac != null && !ContainsReachVariable(ac)) {
if (ac.Expr != Expr.True) {
lastCausal = c;
}
} else if (uc != null && !ContainsReachVariable(uc)) {
lastCausal = c;
}
}
List causals = new List();
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null && gc.labelTargets != null) {
List tmp;
//bool allcausal = true;
foreach (Block b_ in gc.labelTargets) {
Contract.Assert(b_ != null);
tmp = CollectCausalStatements(b_);
foreach (Cmd cau in tmp) {
if (!causals.Contains(cau))
causals.Add(cau);
}
}
//if (allcausal)
if (causals.Count > 0)
return causals;
}
if (lastCausal != null)
causals.Add(lastCausal);
return causals;
}
#endregion
bool BruteForceCESearch(Variable reachvar, Implementation impl, VerifierCallback callback,
Dictionary> cmdbackup, int startidx, int endidx) {
Contract.Requires(reachvar != null);
Contract.Requires(impl != null);
Contract.Requires(callback != null);
Contract.Requires(cce.NonNullElements(cmdbackup));
#region Modify implementation
for (int i = startidx; i <= endidx; i++) {
if (_copiedBlock.Contains(impl.Blocks[i]))
continue;
List cs = new List();
cmdbackup.Add(impl.Blocks[i], impl.Blocks[i].Cmds);
foreach (Cmd c in impl.Blocks[i].Cmds) {
Contract.Assert(c != null);
if (ContainsReachVariable(c)) {
cs.Add(c);
continue;
}
AssertCmd ac = c as AssertCmd;
AssumeCmd uc = c as AssumeCmd;
if (ac != null) {
cs.Add(new AssertCmd(ac.tok, Expr.True));
} else if (uc != null) {
cs.Add(new AssertCmd(uc.tok, Expr.True));
} else {
cs.Add(c);
}
}
impl.Blocks[i].Cmds = cs;
}
#endregion
ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined;
if (!ReCheckImpl(reachvar, impl, callback, out outcome)) {
UndoBlockModifications(impl, cmdbackup, startidx, endidx);
return false;
}
if (outcome == ProverInterface.Outcome.Valid) {
return true;
} else if (outcome == ProverInterface.Outcome.Invalid) {
UndoBlockModifications(impl, cmdbackup, startidx, endidx);
int mid = startidx + (endidx - startidx) / 2;
if (startidx >= endidx) {
// Now we found an interesting Block and we have to
// search for the interesting statements.
int cmdcount = impl.Blocks[endidx].Cmds.Length;
BruteForceCmd(impl.Blocks[endidx], 0, cmdcount / 2 - 1, reachvar, impl, callback);
BruteForceCmd(impl.Blocks[endidx], cmdcount / 2, cmdcount - 1, reachvar, impl, callback);
return true;
} else {
BruteForceCESearch(reachvar, impl, callback, cmdbackup, startidx, mid);
BruteForceCESearch(reachvar, impl, callback, cmdbackup, mid + 1, endidx);
return true;
}
} else {
UndoBlockModifications(impl, cmdbackup, startidx, endidx);
return false;
}
}
bool BruteForceCmd(Block b, int startidx, int endidx, Variable reachvar,
Implementation impl, VerifierCallback callback) {
Contract.Requires(b != null);
Contract.Requires(reachvar != null);
Contract.Requires(impl != null);
Contract.Requires(callback != null);
#region Modify Cmds
List backup = b.Cmds;
Contract.Assert(backup != null);
List cs = new List();
for (int i = 0; i < startidx; i++) {
cs.Add(b.Cmds[i]);
}
for (int i = startidx; i <= endidx; i++) {
Cmd c = b.Cmds[i];
if (ContainsReachVariable(c)) {
cs.Add(c);
continue;
}
cs.Add(new AssertCmd(c.tok, Expr.True));
}
for (int i = endidx + 1; i < b.Cmds.Length; i++) {
cs.Add(b.Cmds[i]);
}
b.Cmds = cs;
#endregion
#region Recheck
ProverInterface.Outcome outcome = ProverInterface.Outcome.Undetermined;
if (!ReCheckImpl(reachvar, impl, callback, out outcome)) {
b.Cmds = backup;
return false;
}
#endregion
if (outcome == ProverInterface.Outcome.Valid) {
return true;
} else if (outcome == ProverInterface.Outcome.Invalid) {
b.Cmds = backup;
if (startidx >= endidx) {
if (!ContainsReachVariable(b.Cmds[endidx])) {
// Console.Write(" Witness (");
//
// ConsoleColor col = Console.ForegroundColor;
// Console.ForegroundColor = ConsoleColor.White;
// Console.Write("{0};{1}", b.Cmds[endidx].tok.line, b.Cmds[endidx].tok.col );
// Console.ForegroundColor = col;
// Console.Write("): ");
// Console.ForegroundColor = ConsoleColor.Yellow;
// b.Cmds[endidx].Emit(new TokenTextWriter("", Console.Out, false), 0);
// Console.ForegroundColor = col;
m_doomedCmds.Add(b.Cmds[endidx]);
return true;
} else {
return false;
}
} else {
int mid = startidx + (endidx - startidx) / 2;
BruteForceCmd(b, startidx, mid, reachvar, impl, callback);
BruteForceCmd(b, mid + 1, endidx, reachvar, impl, callback);
return false; // This is pure random
}
} else {
b.Cmds = backup;
return false;
}
}
void UndoBlockModifications(Implementation impl, Dictionary/*!*/>/*!*/ cmdbackup,
int startidx, int endidx) {
Contract.Requires(cce.NonNullElements(cmdbackup));
Contract.Requires(impl != null);
for (int i = startidx; i <= endidx; i++) {
List cs = null;
if (cmdbackup.TryGetValue(impl.Blocks[i], out cs)) {
Contract.Assert(cs != null);
impl.Blocks[i].Cmds = cs;
cmdbackup.Remove(impl.Blocks[i]);
}
}
}
bool ReCheckImpl(Variable reachvar, Implementation impl, VerifierCallback callback,
out ProverInterface.Outcome outcome) {
Contract.Requires(reachvar != null);
Contract.Requires(impl != null);
Contract.Requires(callback != null);
Checker checker = FindCheckerFor(impl, 1000);
Contract.Assert(checker != null);
DoomCheck dc = new DoomCheck(impl, this.exitBlock, checker, m_UncheckableBlocks);
dc.ErrorHandler = new DoomErrorHandler(dc.Label2Absy, callback);
outcome = ProverInterface.Outcome.Undetermined;
List rv = new List();
rv.Add(reachvar);
if (!dc.CheckLabel(rv,null, out outcome)) {
checker.Close();
return false;
}
checker.Close();
return true;
}
bool ContainsReachVariable(Cmd c) {
Contract.Requires(c != null);
AssertCmd artc = c as AssertCmd;
AssumeCmd amec = c as AssumeCmd;
Expr e;
if (artc != null) {
e = artc.Expr;
} else if (amec != null) {
e = amec.Expr;
} else {
return false;
}
Set freevars = new Set();
e.ComputeFreeVariables(freevars);
foreach (Variable v in freevars) {
Contract.Assert(v != null);
if (v.Name.Contains(reachvarsuffix))
return true;
}
return false;
}
#endregion
#endif
Block exitBlock;
#region Program Passification
private void GenerateHelperBlocks(Implementation impl) {
Contract.Requires(impl != null);
Dictionary gotoCmdOrigins = new Dictionary();
exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins);
Contract.Assert(exitBlock != null);
AddBlocksBetween(impl.Blocks);
#region Insert pre- and post-conditions and where clauses as assume and assert statements
{
List cc = new List();
// where clauses of global variables
foreach (var gvar in program.GlobalVariables) {
if (gvar.TypedIdent.WhereExpr != null) {
Cmd c = new AssumeCmd(gvar.tok, gvar.TypedIdent.WhereExpr);
cc.Add(c);
}
}
// where clauses of in- and out-parameters
cc.AddRange(GetParamWhereClauses(impl));
// where clauses of local variables
foreach (Variable lvar in impl.LocVars) {
Contract.Assert(lvar != null);
if (lvar.TypedIdent.WhereExpr != null) {
Cmd c = new AssumeCmd(lvar.tok, lvar.TypedIdent.WhereExpr);
cc.Add(c);
}
}
// add cc and the preconditions to new blocks preceding impl.Blocks[0]
InjectPreconditions(impl, cc);
// append postconditions, starting in exitBlock and continuing into other blocks, if needed
InjectPostConditions(impl, exitBlock, gotoCmdOrigins);
}
#endregion
}
private Dictionary PassifyProgram(Implementation impl, ModelViewInfo mvInfo) {
Contract.Requires(impl != null);
Contract.Requires(mvInfo != null);
Contract.Requires(this.exitBlock != null);
Contract.Ensures(Contract.Result() != null);
CurrentLocalVariables = impl.LocVars;
return Convert2PassiveCmd(impl, mvInfo);
//return new Hashtable();
}
///
/// Add additional variable to allow checking as described in the paper
/// "It's doomed; we can prove it"
///
private List GenerateReachabilityPredicates(Implementation impl)
{
Contract.Requires(impl != null);
foreach (Block b in impl.Blocks)
{
Contract.Assert(b != null);
//if (b.Predecessors.Length==0) continue;
//if (b.Cmds.Length == 0 ) continue;
Variable v_ = new LocalVariable(Token.NoToken,
new TypedIdent(b.tok, b.Label + reachvarsuffix, new BasicType(SimpleType.Int )));
impl.LocVars.Add(v_);
m_BlockReachabilityMap[b] = v_;
IdentifierExpr lhs = new IdentifierExpr(b.tok, v_);
Contract.Assert(lhs != null);
impl.Proc.Modifies.Add(lhs);
List lhsl = new List();
lhsl.Add(new SimpleAssignLhs(Token.NoToken, lhs));
List rhsl = new List();
rhsl.Add(Expr.Literal(1) );
List cs = new List { new AssignCmd(Token.NoToken, lhsl, rhsl) };
cs.AddRange(b.Cmds);
b.Cmds = cs;
//checkBlocks.Add(new CheckableBlock(v_,b));
}
List incReachVars = new List();
foreach (KeyValuePair kvp in m_BlockReachabilityMap)
{
IdentifierExpr lhs = new IdentifierExpr(Token.NoToken, kvp.Value);
impl.Proc.Modifies.Add(lhs);
incReachVars.Add(new AssumeCmd(Token.NoToken, Expr.Le(lhs, Expr.Literal(1))));
}
return incReachVars;
}
#endregion
#region Compute loop-free approximation
// this might be redundant, but I didn't find a better place to get this information.
private Dictionary m_LastReachVarIncarnation = new Dictionary();
private void Transform4DoomedCheck(Implementation impl)
{
variable2SequenceNumber = new Dictionary();
incarnationOriginMap = new Dictionary();
if (impl.Blocks.Count < 1) return;
impl.PruneUnreachableBlocks();
AddBlocksBetween(impl.Blocks);
ResetPredecessors(impl.Blocks);
GraphAnalyzer ga = new GraphAnalyzer(impl.Blocks);
LoopRemover lr = new LoopRemover(ga);
lr.AbstractLoopUnrolling();
impl.Blocks = ga.ToImplementation(out m_UncheckableBlocks);
ResetPredecessors(impl.Blocks);
// Check for the "BlocksBetween" if all their successors are in m_UncheckableBlocks
List oldblocks = new List();
oldblocks.AddRange(impl.Blocks);
GenerateHelperBlocks(impl);
#region Check for the "BlocksBetween" if all their successors are in m_UncheckableBlocks
foreach (Block b in impl.Blocks)
{
if (oldblocks.Contains(b)) continue;
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc != null)
{
bool allsuccUncheckable = true;
foreach (Block _b in gc.labelTargets)
{
if (!m_UncheckableBlocks.Contains(_b))
{
allsuccUncheckable = false; break;
}
}
if (allsuccUncheckable && !m_UncheckableBlocks.Contains(b)) m_UncheckableBlocks.Add(b);
}
}
#endregion
impl.Blocks = DeepCopyBlocks(impl.Blocks, m_UncheckableBlocks);
m_BlockReachabilityMap = new Dictionary();
List cs = GenerateReachabilityPredicates(impl);
//foreach (Block test in getTheFFinalBlock(impl.Blocks[0]))
//{
// test.Cmds.AddRange(cs);
//}
ResetPredecessors(impl.Blocks);
//EmitImpl(impl,false);
Dictionary var2Expr = PassifyProgram(impl, new ModelViewInfo(program, impl));
// Collect the last incarnation of each reachability variable in the passive program
foreach (KeyValuePair kvp in m_BlockReachabilityMap)
{
if (var2Expr.ContainsKey(kvp.Value))
{
m_LastReachVarIncarnation[kvp.Value] = (Expr)var2Expr[kvp.Value];
}
}
}
List getTheFFinalBlock(Block b)
{
List lb = new List();
GotoCmd gc = b.TransferCmd as GotoCmd;
if (gc == null) lb.Add(b);
else
{
foreach (Block s in gc.labelTargets)
{
foreach (Block r in getTheFFinalBlock(s)) if (!lb.Contains(r)) lb.Add(r);
}
}
return lb;
}
private List DeepCopyBlocks(List lb, List uncheckables)
{
List clones = new List();
List uncheck_ = new List();
Dictionary clonemap = new Dictionary();
foreach (Block b in lb)
{
Block clone = CloneBlock(b);
clones.Add(clone);
clonemap[b] = clone;
if (uncheckables.Contains(b)) uncheck_.Add(clone);
}
uncheckables.Clear();
uncheckables.AddRange(uncheck_);
// update the successors and predecessors
foreach (Block b in lb)
{
List newpreds = new List();
foreach (Block b_ in b.Predecessors)
{
newpreds.Add(clonemap[b_]);
}
clonemap[b].Predecessors = newpreds;
GotoCmd gc = b.TransferCmd as GotoCmd;
ReturnCmd rc = b.TransferCmd as ReturnCmd;
if (gc != null)
{
List lseq = new List();
List bseq = new List();
foreach (string s in gc.labelNames) lseq.Add(s);
foreach (Block b_ in gc.labelTargets) bseq.Add(clonemap[b_]);
GotoCmd tcmd = new GotoCmd(gc.tok, lseq, bseq);
clonemap[b].TransferCmd = tcmd;
}
else if (rc != null)
{
clonemap[b].TransferCmd = new ReturnCmd(rc.tok);
}
}
return clones;
}
private Block CloneBlock(Block b)
{
Block clone = new Block(b.tok, b.Label, b.Cmds, b.TransferCmd);
if (this.exitBlock == b) this.exitBlock = clone;
return clone;
}
#endregion
}
}