protected override ICode VisitContinuation(StmtContinuation s) { this.continuations.Add(s); if (this.seen.Add(s.To)) { this.todo.Enqueue(s.To); } return s; }
protected override ICode VisitContinuation(StmtContinuation s) { if (s.To == this.countReferences) { this.Count++; } return(base.VisitContinuation(s)); }
protected override ICode VisitContinuation(StmtContinuation s) { this.continuations.Add(s); if (this.seen.Add(s.To)) { this.todo.Enqueue(s.To); } return(s); }
protected override ICode VisitContinuation(StmtContinuation s) { // TODO: Why is this de-recursing blocks in continuations? Why doesn't it just derecurse itself (if possible)??? if (s.To.StmtType != Stmt.NodeType.Block) { return base.VisitContinuation(s); } if (!this.seen.Add(s.To)) { return base.VisitContinuation(s); } var block = (StmtBlock)s.To; foreach (var stmt in block.Statements) { if (stmt.StmtType == Stmt.NodeType.Continuation) { // Continuation not inside 'if' var sCont = (StmtContinuation)stmt; if (sCont.To == block) { // Recursive, so derecurse with no condition on loop var body = new StmtBlock(s.Ctx, block.Statements.TakeWhile(x => x != stmt).ToArray()); var replaceWith = new StmtDoLoop(s.Ctx, body, new ExprLiteral(s.Ctx, true, s.Ctx.Boolean)); this.replaces.Add(s.To, replaceWith); return base.VisitContinuation(s); } } if (stmt.StmtType == Stmt.NodeType.If) { // Continuation only statement within 'if' with only a 'then' clause var sIf = (StmtIf)stmt; if (sIf.Else == null && sIf.Then.StmtType == Stmt.NodeType.Continuation) { var sThen = (StmtContinuation)sIf.Then; if (sThen.To == block) { // Recursive, so derecurse var condition = sIf.Condition; var bodyStmts = block.Statements.TakeWhile(x => x != stmt).ToArray(); var bodyLast = bodyStmts.LastOrDefault(); var body = new StmtBlock(s.Ctx, bodyStmts); var loop = new StmtDoLoop(s.Ctx, body, condition); var afterLoop = block.Statements.SkipWhile(x => x != stmt).Skip(1).ToArray(); if (VisitorFindContinuations.Get(new StmtBlock(s.Ctx, afterLoop)).Any(x => x.To == block)) { // Cannot de-recurse yet, must wait for continuations to be merged return base.VisitContinuation(s); } Stmt replaceWith; if (afterLoop.Any()) { var loopAndAfter = new[] { loop }.Concat(afterLoop).ToArray(); replaceWith = new StmtBlock(s.Ctx, loopAndAfter); } else { replaceWith = loop; } this.replaces.Add(s.To, replaceWith); return base.VisitContinuation(s); } } } if (VisitorFindContinuations.Any(stmt)) { // Another continuation present, cannot derecurse break; } } return base.VisitContinuation(s); }
protected override ICode VisitTry(StmtTry s) { var @try = this.RemoveContinuation(s.Try); if (@try != null) { if (s.Catches != null) { if (s.Catches.Count() != 1) { throw new InvalidOperationException("Should only ever see 1 catch here"); } var sCatch = s.Catches.First(); var @catch = this.RemoveContinuation(sCatch.Stmt); if (@catch != null) { if ((@try.Item2 == null || @catch.Item2 == null || @try.Item2 == @catch.Item2) && (@try.Item2 != null || @catch.Item2 != null)) { var newTry = new StmtTry(s.Ctx, @try.Item1, new[] { new StmtTry.Catch(@catch.Item1, sCatch.ExceptionVar) }, null); var newCont = new StmtContinuation(s.Ctx, @try.Item2 ?? @catch.Item2, false); return new StmtBlock(s.Ctx, newTry, newCont); } } // Special case // When 'leave' CIL branch to different instructions, allow specific code to be // moved inside the 'try' or 'catch' block. It should be impossible for this code to throw an exception var tryTos = VisitorFindContinuations.Get(@try.Item2); if (tryTos.Count() == 1 && tryTos.First().To == @catch.Item2 && @try.Item2.StmtType == Stmt.NodeType.Block) { var try2Stmts = ((StmtBlock)@try.Item2).Statements.ToArray(); var s0 = try2Stmts.Take(try2Stmts.Length - 1); if (s0.All(x => x.StmtType == Stmt.NodeType.Assignment)) { var sN = try2Stmts.Last(); if (sN.StmtType == Stmt.NodeType.Continuation) { var newTry = new StmtTry(s.Ctx, new StmtBlock(s.Ctx, @try.Item1, new StmtBlock(s.Ctx, s0), new StmtContinuation(s.Ctx, ((StmtContinuation)sN).To, true)), s.Catches, null); return newTry; } } } } if (s.Finally != null) { var @finally = this.RemoveContinuation(s.Finally); if (@finally != null) { if ((@try.Item2 == null || @finally.Item2 == null || @try.Item2 == @finally.Item2) && (@try.Item2 != null || @finally.Item2 != null)) { var newTry = new StmtTry(s.Ctx, @try.Item1, null, @finally.Item1); var newCont = new StmtContinuation(s.Ctx, @try.Item2 ?? @finally.Item2, false); return new StmtBlock(s.Ctx, newTry, newCont); } } } // TODO: This is a hack for badly handling fault handlers. They are ignored at the moment if (s.Catches == null && s.Finally == null) { var cont = @try.Item2 == null ? null : new StmtContinuation(s.Ctx, @try.Item2, false); return new StmtBlock(s.Ctx, @try.Item1, cont); } } return base.VisitTry(s); }
protected override ICode VisitContinuation(StmtContinuation s) { if (this.ifInfo == null) { // If there are no if statements in this continuation return base.VisitContinuation(s); } if (!this.ifInfo.Conditions.Any()) { throw new InvalidOperationException("There should be one or more conditions at this point"); } var combinedCondition = this.ifInfo.Conditions.Aggregate((a, b) => this.ctx.ExprGen.And(a, b)); this.ifInfo.AddToIf.Add(Tuple.Create(s, combinedCondition)); base.VisitContinuation(s); return null; }
protected override ICode VisitContinuation(StmtContinuation s) { if (!s.LeaveProtectedRegion) { // Must never substitute when leaving protected region. // This would change which statements were inside the try/catch/finally region if (s.To.StmtType == Stmt.NodeType.Continuation && !((StmtContinuation)s.To).LeaveProtectedRegion) { var newCont = new StmtContinuation(s.Ctx, ((StmtContinuation)s.To).To, false); return this.Visit(newCont); } var count = VisitorContToCounter.GetCount(s.To, this.root); if (count == 1) { return this.Visit(s.To); } } return base.VisitContinuation(s); }
protected override ICode VisitContinuation(StmtContinuation s) { if (this.ifInfo == null) { // If there are no if statements in this continuation return(base.VisitContinuation(s)); } if (!this.ifInfo.Conditions.Any()) { throw new InvalidOperationException("There should be one or more conditions at this point"); } var combinedCondition = this.ifInfo.Conditions.Aggregate((a, b) => this.ctx.ExprGen.And(a, b)); this.ifInfo.AddToIf.Add(Tuple.Create(s, combinedCondition)); base.VisitContinuation(s); return(null); }
protected override ICode VisitContinuation(StmtContinuation s) { if (!s.LeaveProtectedRegion) { // Must never substitute when leaving protected region. // This would change which statements were inside the try/catch/finally region if (s.To.StmtType == Stmt.NodeType.Continuation && !((StmtContinuation)s.To).LeaveProtectedRegion) { var newCont = new StmtContinuation(s.Ctx, ((StmtContinuation)s.To).To, false); return(this.Visit(newCont)); } var count = VisitorContToCounter.GetCount(s.To, this.root); if (count == 1) { return(this.Visit(s.To)); } } return(base.VisitContinuation(s)); }
protected override ICode VisitContinuation(StmtContinuation s) { return new StmtContinuation(s.Ctx, s.To, s.LeaveProtectedRegion); }
private void BuildBlock(IEnumerable <Instruction> insts, Instruction startOfNextPart) { var inst0 = insts.First(); var instN = insts.Last(); // Any instruction in the method could target this block var blockStarts = this.methodBlockStarts .Where(x => x.Offset >= inst0.Offset && x.Offset <= instN.Offset) .ToArray(); // For each block create a StmtCil, with the correct ending. // Endings that require a expression have them set to null; they will be filled in during CIL decoding for (int i = 0; i < blockStarts.Length; i++) { var start = blockStarts[i]; var end = i == blockStarts.Length - 1 ? insts.Last() : blockStarts[i + 1].Previous; var blockInsts = start.GetRange(end); Stmt blockEndStmt; var code = end.OpCode.Code; switch (end.OpCode.FlowControl) { case FlowControl.Cond_Branch: if (code == Code.Switch) { var cases = ((Instruction[])end.Operand).Select(x => new StmtContinuation(this.ctx, x, false)).ToArray(); foreach (var @case in cases) { this.mappable.Add(@case); } var @default = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add(@default); blockEndStmt = new StmtSwitch(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Int32), cases.Select((x, value) => new StmtSwitch.Case(value, x)).ToArray(), @default); } else { var ifTrue = new StmtContinuation(this.ctx, (Instruction)end.Operand, false); var ifFalse = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add(ifTrue); this.mappable.Add(ifFalse); blockEndStmt = new StmtIf(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Boolean), ifTrue, ifFalse); } break; case FlowControl.Branch: var leaveProtectedRegion = code == Code.Leave || code == Code.Leave_S; blockEndStmt = new StmtContinuation(this.ctx, (Instruction)end.Operand, leaveProtectedRegion); this.mappable.Add((StmtContinuation)blockEndStmt); break; case FlowControl.Next: case FlowControl.Call: blockEndStmt = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add((StmtContinuation)blockEndStmt); break; case FlowControl.Return: switch (code) { case Code.Endfinally: blockEndStmt = new StmtContinuation(this.ctx, startOfNextPart, true); this.mappable.Add((StmtContinuation)blockEndStmt); break; case Code.Ret: blockEndStmt = new StmtContinuation(this.ctx, this.endBlock, false); blockInsts = start == end?Enumerable.Empty <Instruction>() : start.GetRange(end.Previous); // Remove 'ret' from statements break; default: blockEndStmt = null; break; } break; case FlowControl.Throw: blockEndStmt = null; break; default: throw new NotImplementedException("Cannot handle: " + end.OpCode.FlowControl); } var block = new StmtCil(this.ctx, blockInsts, blockEndStmt); this.blockMap.Add(start, new List <Stmt> { block }); } }
protected override ICode VisitContinuation(StmtContinuation s) { this.continuations.Add(s); return s; }
protected override ICode VisitContinuation(StmtContinuation s) { return(new StmtContinuation(s.Ctx, s.To, s.LeaveProtectedRegion)); }
protected override ICode VisitTry(StmtTry s) { var @try = this.RemoveContinuation(s.Try); if (@try != null) { if (s.Catches != null) { if (s.Catches.Count() != 1) { throw new InvalidOperationException("Should only ever see 1 catch here"); } var sCatch = s.Catches.First(); var @catch = this.RemoveContinuation(sCatch.Stmt); if (@catch != null) { if ((@try.Item2 == null || @catch.Item2 == null || @try.Item2 == @catch.Item2) && (@try.Item2 != null || @catch.Item2 != null)) { var newTry = new StmtTry(s.Ctx, @try.Item1, new[] { new StmtTry.Catch(@catch.Item1, sCatch.ExceptionVar) }, null); var newCont = new StmtContinuation(s.Ctx, @try.Item2 ?? @catch.Item2, false); return(new StmtBlock(s.Ctx, newTry, newCont)); } } // Special case // When 'leave' CIL branch to different instructions, allow specific code to be // moved inside the 'try' or 'catch' block. It should be impossible for this code to throw an exception var tryTos = VisitorFindContinuations.Get(@try.Item2); if (tryTos.Count() == 1 && tryTos.First().To == @catch.Item2 && @try.Item2.StmtType == Stmt.NodeType.Block) { var try2Stmts = ((StmtBlock)@try.Item2).Statements.ToArray(); var s0 = try2Stmts.Take(try2Stmts.Length - 1); if (s0.All(x => x.StmtType == Stmt.NodeType.Assignment)) { var sN = try2Stmts.Last(); if (sN.StmtType == Stmt.NodeType.Continuation) { var newTry = new StmtTry(s.Ctx, new StmtBlock(s.Ctx, @try.Item1, new StmtBlock(s.Ctx, s0), new StmtContinuation(s.Ctx, ((StmtContinuation)sN).To, true)), s.Catches, null); return(newTry); } } } } if (s.Finally != null) { var @finally = this.RemoveContinuation(s.Finally); if (@finally != null) { if ((@try.Item2 == null || @finally.Item2 == null || @try.Item2 == @finally.Item2) && (@try.Item2 != null || @finally.Item2 != null)) { var newTry = new StmtTry(s.Ctx, @try.Item1, null, @finally.Item1); var newCont = new StmtContinuation(s.Ctx, @try.Item2 ?? @finally.Item2, false); return(new StmtBlock(s.Ctx, newTry, newCont)); } } } // TODO: This is a hack for badly handling fault handlers. They are ignored at the moment if (s.Catches == null && s.Finally == null) { var cont = @try.Item2 == null ? null : new StmtContinuation(s.Ctx, @try.Item2, false); return(new StmtBlock(s.Ctx, @try.Item1, cont)); } } return(base.VisitTry(s)); }
protected virtual ICode VisitContinuation(StmtContinuation s) { throw new InvalidOperationException("Non-recursive visitor, cannot handle continuations"); }
protected override ICode VisitContinuation(StmtContinuation s) { // Don't continue through continuations return(s); }
protected override ICode VisitContinuation(StmtContinuation s) { this.numConts++; this.containsLeaveProtectedRegion |= s.LeaveProtectedRegion; return(base.VisitContinuation(s)); }
protected override ICode VisitContinuation(StmtContinuation s) { // TODO: Why is this de-recursing blocks in continuations? Why doesn't it just derecurse itself (if possible)??? if (s.To.StmtType != Stmt.NodeType.Block) { return(base.VisitContinuation(s)); } if (!this.seen.Add(s.To)) { return(base.VisitContinuation(s)); } var block = (StmtBlock)s.To; foreach (var stmt in block.Statements) { if (stmt.StmtType == Stmt.NodeType.Continuation) { // Continuation not inside 'if' var sCont = (StmtContinuation)stmt; if (sCont.To == block) { // Recursive, so derecurse with no condition on loop var body = new StmtBlock(s.Ctx, block.Statements.TakeWhile(x => x != stmt).ToArray()); var replaceWith = new StmtDoLoop(s.Ctx, body, new ExprLiteral(s.Ctx, true, s.Ctx.Boolean)); this.replaces.Add(s.To, replaceWith); return(base.VisitContinuation(s)); } } if (stmt.StmtType == Stmt.NodeType.If) { // Continuation only statement within 'if' with only a 'then' clause var sIf = (StmtIf)stmt; if (sIf.Else == null && sIf.Then.StmtType == Stmt.NodeType.Continuation) { var sThen = (StmtContinuation)sIf.Then; if (sThen.To == block) { // Recursive, so derecurse var condition = sIf.Condition; var bodyStmts = block.Statements.TakeWhile(x => x != stmt).ToArray(); var bodyLast = bodyStmts.LastOrDefault(); var body = new StmtBlock(s.Ctx, bodyStmts); var loop = new StmtDoLoop(s.Ctx, body, condition); var afterLoop = block.Statements.SkipWhile(x => x != stmt).Skip(1).ToArray(); if (VisitorFindContinuations.Get(new StmtBlock(s.Ctx, afterLoop)).Any(x => x.To == block)) { // Cannot de-recurse yet, must wait for continuations to be merged return(base.VisitContinuation(s)); } Stmt replaceWith; if (afterLoop.Any()) { var loopAndAfter = new[] { loop }.Concat(afterLoop).ToArray(); replaceWith = new StmtBlock(s.Ctx, loopAndAfter); } else { replaceWith = loop; } this.replaces.Add(s.To, replaceWith); return(base.VisitContinuation(s)); } } } if (VisitorFindContinuations.Any(stmt)) { // Another continuation present, cannot derecurse break; } } return(base.VisitContinuation(s)); }
protected override ICode VisitContinuation(StmtContinuation s) { this.numConts++; this.containsLeaveProtectedRegion |= s.LeaveProtectedRegion; return base.VisitContinuation(s); }
protected override ICode VisitContinuation(StmtContinuation s) { // Don't continue through continuations return s; }
protected override ICode VisitContinuation(StmtContinuation s) { this.continuations.Add(s); return(s); }
public DebugView(StmtContinuation s) { this.to = s.to; this.To = s.To; }
protected override ICode VisitContinuation(StmtContinuation s) { if (s.To == this.countReferences) { this.Count++; } return base.VisitContinuation(s); }
protected override ICode VisitContinuation(StmtContinuation s) { this.NewLine(); this.code.AppendFormat("-> {0}", GetStmtName(s.To)); if (s.LeaveProtectedRegion) { this.code.Append(" [leave protected region]"); } this.continuations.Add(s); return s; }
private void BuildBlock(IEnumerable<Instruction> insts, Instruction startOfNextPart) { var inst0 = insts.First(); var instN = insts.Last(); // Any instruction in the method could target this block var blockStarts = this.methodBlockStarts .Where(x => x.Offset >= inst0.Offset && x.Offset <= instN.Offset) .ToArray(); // For each block create a StmtCil, with the correct ending. // Endings that require a expression have them set to null; they will be filled in during CIL decoding for (int i = 0; i < blockStarts.Length; i++) { var start = blockStarts[i]; var end = i == blockStarts.Length - 1 ? insts.Last() : blockStarts[i + 1].Previous; var blockInsts = start.GetRange(end); Stmt blockEndStmt; var code = end.OpCode.Code; switch (end.OpCode.FlowControl) { case FlowControl.Cond_Branch: if (code == Code.Switch) { var cases = ((Instruction[])end.Operand).Select(x => new StmtContinuation(this.ctx, x, false)).ToArray(); foreach (var @case in cases) { this.mappable.Add(@case); } var @default = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add(@default); blockEndStmt = new StmtSwitch(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Int32), cases.Select((x, value) => new StmtSwitch.Case(value, x)).ToArray(), @default); } else { var ifTrue = new StmtContinuation(this.ctx, (Instruction)end.Operand, false); var ifFalse = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add(ifTrue); this.mappable.Add(ifFalse); blockEndStmt = new StmtIf(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Boolean), ifTrue, ifFalse); } break; case FlowControl.Branch: var leaveProtectedRegion = code == Code.Leave || code == Code.Leave_S; blockEndStmt = new StmtContinuation(this.ctx, (Instruction)end.Operand, leaveProtectedRegion); this.mappable.Add((StmtContinuation)blockEndStmt); break; case FlowControl.Next: case FlowControl.Call: blockEndStmt = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add((StmtContinuation)blockEndStmt); break; case FlowControl.Return: switch (code) { case Code.Endfinally: blockEndStmt = new StmtContinuation(this.ctx, startOfNextPart, true); this.mappable.Add((StmtContinuation)blockEndStmt); break; case Code.Ret: blockEndStmt = new StmtContinuation(this.ctx, this.endBlock, false); blockInsts = start == end ? Enumerable.Empty<Instruction>() : start.GetRange(end.Previous); // Remove 'ret' from statements break; default: blockEndStmt = null; break; } break; case FlowControl.Throw: blockEndStmt = null; break; default: throw new NotImplementedException("Cannot handle: " + end.OpCode.FlowControl); } var block = new StmtCil(this.ctx, blockInsts, blockEndStmt); this.blockMap.Add(start, new List<Stmt> { block }); } }