// // SubroutineWithHandlers.cs // // Authors: // Alexander Chebaturkin (chebaturkin@gmail.com) // // Copyright (C) 2011 Alexander Chebaturkin // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.IO; using System.Linq; using Mono.CodeContracts.Static.Analysis; using Mono.CodeContracts.Static.ControlFlow.Blocks; using Mono.CodeContracts.Static.ControlFlow.Subroutines.Builders; using Mono.CodeContracts.Static.DataStructures; using Mono.CodeContracts.Static.Providers; namespace Mono.CodeContracts.Static.ControlFlow.Subroutines { abstract class SubroutineWithHandlers<Label, Handler> : SubroutineBase<Label> { protected readonly Dictionary<Handler, BlockWithLabels<Label>> CatchFilterHeaders = new Dictionary<Handler, BlockWithLabels<Label>> (); public readonly Dictionary<Handler, Subroutine> FaultFinallySubroutines = new Dictionary<Handler, Subroutine> (); protected readonly Dictionary<Handler, BlockWithLabels<Label>> FilterCodeBlocks = new Dictionary<Handler, BlockWithLabels<Label>> (); public readonly Dictionary<CFGBlock, Sequence<Handler>> ProtectingHandlers = new Dictionary<CFGBlock, Sequence<Handler>> (); public Sequence<Handler> CurrentProtectingHandlers = Sequence<Handler>.Empty; protected SubroutineWithHandlers (SubroutineFacade subroutineFacade) : base (subroutineFacade) { } protected SubroutineWithHandlers (SubroutineFacade subroutineFacade, Label startLabel, SubroutineBuilder<Label> builder) : base (subroutineFacade, startLabel, builder) { } protected new IMethodCodeProvider<Label, Handler> CodeProvider { get { return (IMethodCodeProvider<Label, Handler>) base.CodeProvider; } } private bool IsFault (Handler handler) { return CodeProvider.IsFaultHandler (handler); } private Sequence<Handler> ProtectingHandlerList (CFGBlock block) { Sequence<Handler> list; this.ProtectingHandlers.TryGetValue (block, out list); return list; } public BlockWithLabels<Label> CreateCatchFilterHeader (Handler handler, Label label) { BlockWithLabels<Label> block; if (!this.LabelsThatStartBlocks.TryGetValue (label, out block)) { block = new CatchFilterEntryBlock<Label> (this, ref this.BlockIdGenerator); this.CatchFilterHeaders.Add (handler, block); this.LabelsThatStartBlocks.Add (label, block); if (CodeProvider.IsFilterHandler (handler)) { BlockWithLabels<Label> targetBlock = GetTargetBlock (CodeProvider.FilterExpressionStart (handler)); this.FilterCodeBlocks.Add (handler, targetBlock); } } return block; } public override IEnumerable<Subroutine> UsedSubroutines (HashSet<int> alreadySeen) { return this.FaultFinallySubroutines.Values.Concat (base.UsedSubroutines (alreadySeen)); } public override Sequence<Pair<EdgeTag, Subroutine>> EdgeSubroutinesOuterToInner (CFGBlock current, CFGBlock succ, out bool isExceptionHandlerEdge, Sequence<Edge<CFGBlock, EdgeTag>> context) { if (current.Subroutine != this) return current.Subroutine.EdgeSubroutinesOuterToInner (current, succ, out isExceptionHandlerEdge, context); Sequence<Handler> l1 = ProtectingHandlerList (current); Sequence<Handler> l2 = ProtectingHandlerList (succ); isExceptionHandlerEdge = IsCatchFilterHeader (succ); Sequence<Pair<EdgeTag, Subroutine>> result = GetOrdinaryEdgeSubroutines (current, succ, context); while (l1 != l2) { if (l1.Length () >= l2.Length ()) { Handler head = l1.Head; if (IsFaultOrFinally (head) && (!IsFault (head) || isExceptionHandlerEdge)) result = result.Cons (new Pair<EdgeTag, Subroutine> (EdgeTag.Finally, this.FaultFinallySubroutines [head])); l1 = l1.Tail; } else l2 = l2.Tail; } return result; } private bool IsFaultOrFinally (Handler handler) { return CodeProvider.IsFaultHandler (handler) || CodeProvider.IsFinallyHandler (handler); } public override IEnumerable<Pair<Dummy, CFGBlock>> Successors (CFGBlock node) { foreach (var pair in SuccessorEdges [node]) yield return new Pair<Dummy, CFGBlock> (Dummy.Value, pair.Value); foreach (Handler handler in ProtectingHandlerList (node).AsEnumerable ()) { if (!IsFaultOrFinally (handler)) yield return new Pair<Dummy, CFGBlock> (Dummy.Value, this.CatchFilterHeaders [handler]); } if (node != ExceptionExit) yield return new Pair<Dummy, CFGBlock> (Dummy.Value, ExceptionExit); } public override IEnumerable<CFGBlock> ExceptionHandlers<Data, TType> (CFGBlock block, Subroutine innerSubroutine, Data data, IHandlerFilter<Data> handlerPredicate) { IHandlerFilter<Data> handleFilter = handlerPredicate; Sequence<Handler> protectingHandlers = ProtectingHandlerList (block); if (innerSubroutine != null && innerSubroutine.IsFaultFinally) { for (; protectingHandlers != null; protectingHandlers = protectingHandlers.Tail) { if (IsFaultOrFinally (protectingHandlers.Head) && this.FaultFinallySubroutines [protectingHandlers.Head] == innerSubroutine) { protectingHandlers = protectingHandlers.Tail; break; } } } for (; protectingHandlers != null; protectingHandlers = protectingHandlers.Tail) { Handler handler = protectingHandlers.Head; if (!IsFaultOrFinally (handler)) { if (handleFilter != null) { bool stopPropagation; if (CodeProvider.IsCatchHandler (handler)) { if (handleFilter.Catch (data, CodeProvider.CatchType (handler), out stopPropagation)) yield return this.CatchFilterHeaders [handler]; } else if (handleFilter.Filter (data, new APC (this.FilterCodeBlocks [handler], 0, null), out stopPropagation)) yield return this.CatchFilterHeaders [handler]; if (stopPropagation) yield break; } else yield return this.CatchFilterHeaders [handler]; if (CodeProvider.IsCatchAllHandler (handler)) yield break; } } yield return ExceptionExit; } protected override void PrintReferencedSubroutines (TextWriter tw, HashSet<Subroutine> subs, ILPrinter<APC> printer, Func<CFGBlock, IEnumerable<Sequence<Edge<CFGBlock, EdgeTag>>>> contextLookup, Sequence<Edge<CFGBlock, EdgeTag>> context, HashSet<Pair<Subroutine, Sequence<Edge<CFGBlock, EdgeTag>>>> printed) { foreach (Subroutine sub in this.FaultFinallySubroutines.Values) { if (contextLookup == null) sub.Print (tw, printer, contextLookup, context, printed); else { foreach (var ctx in contextLookup (sub.Entry)) sub.Print (tw, printer, contextLookup, ctx, printed); } } base.PrintReferencedSubroutines (tw, subs, printer, contextLookup, context, printed); } protected override void PrintHandlers (TextWriter tw, BlockWithLabels<Label> block) { tw.Write (" Handlers: "); foreach (Handler handler in ProtectingHandlerList (block).AsEnumerable ()) { if (IsFaultOrFinally (handler)) tw.Write ("SR{0} ", this.FaultFinallySubroutines [handler].Id); else tw.Write ("{0} ", this.CatchFilterHeaders [handler].Index); } if (block != ExceptionExit) tw.Write ("{0} ", ExceptionExit.Index); tw.WriteLine (); } } }