Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / class / Mono.CodeContracts / Mono.CodeContracts.Static.ControlFlow.Subroutines.Builders / SubroutineWithHandlersBuilder.cs
1 // 
2 // SubroutineWithHandlersBuilder.cs
3 // 
4 // Authors:
5 //      Alexander Chebaturkin (chebaturkin@gmail.com)
6 // 
7 // Copyright (C) 2011 Alexander Chebaturkin
8 // 
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //  
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 // 
28
29 using System;
30 using System.Collections.Generic;
31 using Mono.CodeContracts.Static.AST;
32 using Mono.CodeContracts.Static.ControlFlow.Blocks;
33 using Mono.CodeContracts.Static.DataStructures;
34 using Mono.CodeContracts.Static.Providers;
35
36 namespace Mono.CodeContracts.Static.ControlFlow.Subroutines.Builders {
37         class SubroutineWithHandlersBuilder<Label, Handler> : SubroutineBuilder<Label> {
38                 private readonly Dictionary<Label, Handler> handler_starting_at = new Dictionary<Label, Handler> ();
39                 private readonly Method method;
40                 private readonly Dictionary<Label, Queue<Handler>> subroutine_handler_end_list = new Dictionary<Label, Queue<Handler>> ();
41                 private readonly Dictionary<Label, Queue<Handler>> try_end_list = new Dictionary<Label, Queue<Handler>> ();
42                 private readonly Dictionary<Label, Stack<Handler>> try_start_list = new Dictionary<Label, Stack<Handler>> ();
43                 private Sequence<SubroutineWithHandlers<Label, Handler>> subroutine_stack;
44
45                 public SubroutineWithHandlersBuilder (IMethodCodeProvider<Label, Handler> codeProvider,
46                                                       SubroutineFacade subroutineFacade,
47                                                       Method method,
48                                                       Label entry)
49                         : base (codeProvider, subroutineFacade, entry)
50                 {
51                         this.method = method;
52                         ComputeTryBlockStartAndEndInfo (this.method);
53                         Initialize (entry);
54                 }
55
56                 private new IMethodCodeProvider<Label, Handler> CodeProvider
57                 {
58                         get { return (IMethodCodeProvider<Label, Handler>) base.CodeProvider; }
59                 }
60
61                 protected SubroutineWithHandlers<Label, Handler> CurrentSubroutineWithHandlers
62                 {
63                         get { return this.subroutine_stack.Head; }
64                 }
65
66                 private Sequence<Handler> CurrentProtectingHanlders
67                 {
68                         get { return CurrentSubroutineWithHandlers.CurrentProtectingHandlers; }
69                         set { CurrentSubroutineWithHandlers.CurrentProtectingHandlers = value; }
70                 }
71
72                 public override SubroutineBase<Label> CurrentSubroutine
73                 {
74                         get { return this.subroutine_stack.Head; }
75                 }
76
77                 private void ComputeTryBlockStartAndEndInfo (Method method)
78                 {
79                         foreach (Handler handler in CodeProvider.GetTryBlocks (method)) {
80                                 if (CodeProvider.IsFilterHandler (handler))
81                                         AddTargetLabel (CodeProvider.FilterExpressionStart (handler));
82                                 AddTargetLabel (CodeProvider.HandlerStart (handler));
83                                 AddTargetLabel (CodeProvider.HandlerEnd (handler));
84                                 AddTryStart (handler);
85                                 AddTryEnd (handler);
86                                 AddHandlerEnd (handler);
87                                 this.handler_starting_at.Add (CodeProvider.HandlerStart (handler), handler);
88                         }
89                 }
90
91                 private void AddHandlerEnd (Handler handler)
92                 {
93                         if (!IsFaultOrFinally (handler))
94                                 return;
95
96                         Label handlerEnd = CodeProvider.HandlerEnd (handler);
97                         Queue<Handler> queue;
98                         this.subroutine_handler_end_list.TryGetValue (handlerEnd, out queue);
99                         if (queue == null) {
100                                 queue = new Queue<Handler> ();
101                                 this.subroutine_handler_end_list [handlerEnd] = queue;
102                         }
103                         queue.Enqueue (handler);
104                         AddTargetLabel (handlerEnd);
105                 }
106
107                 private void AddTryEnd (Handler handler)
108                 {
109                         Label tryEnd = CodeProvider.TryEnd (handler);
110                         Queue<Handler> queue;
111                         this.try_end_list.TryGetValue (tryEnd, out queue);
112                         if (queue == null) {
113                                 queue = new Queue<Handler> ();
114                                 this.try_end_list [tryEnd] = queue;
115                         }
116                         queue.Enqueue (handler);
117                         AddTargetLabel (tryEnd);
118                 }
119
120                 private void AddTryStart (Handler handler)
121                 {
122                         Label tryStart = CodeProvider.TryStart (handler);
123                         Stack<Handler> stack;
124                         this.try_start_list.TryGetValue (tryStart, out stack);
125                         if (stack == null) {
126                                 stack = new Stack<Handler> ();
127                                 this.try_start_list [tryStart] = stack;
128                         }
129                         stack.Push (handler);
130                         AddTargetLabel (tryStart);
131                 }
132
133                 public CFGBlock BuildBlocks (Label entry, SubroutineWithHandlers<Label, Handler> subroutine)
134                 {
135                         this.subroutine_stack = Sequence<SubroutineWithHandlers<Label, Handler>>.Cons (subroutine, null);
136                         return base.BuildBlocks (entry);
137                 }
138
139                 public override void RecordInformationSameAsOtherBlock (BlockWithLabels<Label> newBlock, BlockWithLabels<Label> currentBlock)
140                 {
141                         Sequence<Handler> list;
142                         if (!CurrentSubroutineWithHandlers.ProtectingHandlers.TryGetValue (currentBlock, out list))
143                                 return;
144                         CurrentSubroutineWithHandlers.ProtectingHandlers.Add (newBlock, list);
145                 }
146
147                 public override BlockWithLabels<Label> RecordInformationForNewBlock (Label currentLabel, BlockWithLabels<Label> previousBlock)
148                 {
149                         BlockWithLabels<Label> result = null;
150                         Queue<Handler> handlerEnd = GetHandlerEnd (currentLabel);
151                         if (handlerEnd != null) {
152                                 foreach (Handler handler in handlerEnd) {
153                                         this.subroutine_stack.Head.Commit ();
154                                         this.subroutine_stack = this.subroutine_stack.Tail;
155                                         previousBlock = null;
156                                 }
157                         }
158                         Queue<Handler> tryEnd = GetTryEnd (currentLabel);
159                         if (tryEnd != null) {
160                                 foreach (Handler handler in tryEnd) {
161                                         if (!Equals (handler, CurrentProtectingHanlders.Head))
162                                                 throw new InvalidOperationException ("wrong handler");
163                                         CurrentProtectingHanlders = CurrentProtectingHanlders.Tail;
164                                 }
165                         }
166                         Handler handler1;
167                         if (IsHandlerStart (currentLabel, out handler1)) {
168                                 if (IsFaultOrFinally (handler1)) {
169                                         SubroutineWithHandlers<Label, Handler> sub = !CodeProvider.IsFaultHandler (handler1)
170                                                                                         ? new FinallySubroutine<Label, Handler> (this.SubroutineFacade, currentLabel, this)
171                                                                                         : (FaultFinallySubroutineBase<Label, Handler>) new FaultSubroutine<Label, Handler> (this.SubroutineFacade, currentLabel, this);
172                                         CurrentSubroutineWithHandlers.FaultFinallySubroutines.Add (handler1, sub);
173                                         this.subroutine_stack = this.subroutine_stack.Cons (sub);
174                                         previousBlock = null;
175                                 } else
176                                         result = CurrentSubroutineWithHandlers.CreateCatchFilterHeader (handler1, currentLabel);
177                         }
178                         if (result == null)
179                                 result = base.RecordInformationForNewBlock (currentLabel, previousBlock);
180                         Stack<Handler> tryStart = GetTryStart (currentLabel);
181                         if (tryStart != null) {
182                                 foreach (Handler handler in tryStart)
183                                         CurrentProtectingHanlders = CurrentProtectingHanlders.Cons (handler);
184                         }
185
186                         CurrentSubroutineWithHandlers.ProtectingHandlers.Add (result, CurrentProtectingHanlders);
187                         return result;
188                 }
189
190                 private bool IsFaultOrFinally (Handler handler)
191                 {
192                         return CodeProvider.IsFaultHandler (handler) || CodeProvider.IsFinallyHandler (handler);
193                 }
194
195                 private bool IsHandlerStart (Label currentLabel, out Handler handler)
196                 {
197                         return this.handler_starting_at.TryGetValue (currentLabel, out handler);
198                 }
199
200                 private Stack<Handler> GetTryStart (Label currentLabel)
201                 {
202                         Stack<Handler> queue;
203                         this.try_start_list.TryGetValue (currentLabel, out queue);
204                         return queue;
205                 }
206
207                 private Queue<Handler> GetTryEnd (Label currentLabel)
208                 {
209                         Queue<Handler> queue;
210                         this.try_end_list.TryGetValue (currentLabel, out queue);
211                         return queue;
212                 }
213
214                 private Queue<Handler> GetHandlerEnd (Label currentLabel)
215                 {
216                         Queue<Handler> queue;
217                         this.subroutine_handler_end_list.TryGetValue (currentLabel, out queue);
218                         return queue;
219                 }
220         }
221 }