Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / Mono.CodeContracts / Mono.CodeContracts.Static.Providers / CodeProviderImpl.cs
1 // 
2 // CodeProviderImpl.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 System.Linq;
32 using Mono.CodeContracts.Static.AST;
33 using Mono.CodeContracts.Static.AST.Visitors;
34 using Mono.CodeContracts.Static.ControlFlow;
35 using Mono.CodeContracts.Static.DataStructures;
36
37 namespace Mono.CodeContracts.Static.Providers {
38         class CodeProviderImpl : IMethodCodeProvider<CodeProviderImpl.PC, ExceptionHandler> {
39                 public static readonly CodeProviderImpl Instance = new CodeProviderImpl ();
40
41                 #region IMethodCodeProvider<PC,ExceptionHandler> Members
42                 public Result Decode<Visitor, Data, Result> (PC pc, Visitor visitor, Data data)
43                         where Visitor : IAggregateVisitor<PC, Data, Result>
44                 {
45                         Node nested;
46                         Node node = Decode (pc, out nested);
47                         if (IsAtomicNested (nested))
48                                 node = nested;
49                         else if (nested != null)
50                                 return visitor.Aggregate (pc, new PC (nested), nested is Block, data);
51
52                         if (node == null)
53                                 return visitor.Nop (pc, data);
54
55                         switch (node.NodeType) {
56                         case NodeType.Block:
57                         case NodeType.Nop:
58                                 return visitor.Nop (pc, data);
59                         case NodeType.Clt:
60                         case NodeType.Lt:
61                                 return visitor.Binary (pc, BinaryOperator.Clt, Dummy.Value, Dummy.Value, Dummy.Value, data);
62                         case NodeType.Cgt:
63                         case NodeType.Gt:
64                                 return visitor.Binary (pc, BinaryOperator.Cgt, Dummy.Value, Dummy.Value, Dummy.Value, data);
65                         case NodeType.Ceq:
66                         case NodeType.Eq:
67                                 return visitor.Binary (pc, BinaryOperator.Ceq, Dummy.Value, Dummy.Value, Dummy.Value, data);
68                         case NodeType.Ne:
69                                 return visitor.Binary (pc, BinaryOperator.Cne_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
70                         case NodeType.Ge:
71                                 return visitor.Binary (pc, BinaryOperator.Cge, Dummy.Value, Dummy.Value, Dummy.Value, data);
72                         case NodeType.Le:
73                                 return visitor.Binary (pc, BinaryOperator.Cle, Dummy.Value, Dummy.Value, Dummy.Value, data);
74                         case NodeType.Add:
75                                 return visitor.Binary (pc, BinaryOperator.Add, Dummy.Value, Dummy.Value, Dummy.Value, data);
76                         case NodeType.Sub:
77                                 return visitor.Binary (pc, BinaryOperator.Sub, Dummy.Value, Dummy.Value, Dummy.Value, data);
78                         case NodeType.Rem:
79                                 return visitor.Binary (pc, BinaryOperator.Rem, Dummy.Value, Dummy.Value, Dummy.Value, data);
80                         case NodeType.Rem_Un:
81                                 return visitor.Binary (pc, BinaryOperator.Rem_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
82                         case NodeType.Mul:
83                                 return visitor.Binary (pc, BinaryOperator.Mul, Dummy.Value, Dummy.Value, Dummy.Value, data);
84                         case NodeType.Div:
85                                 return visitor.Binary (pc, BinaryOperator.Div, Dummy.Value, Dummy.Value, Dummy.Value, data);
86                         case NodeType.Div_Un:
87                                 return visitor.Binary (pc, BinaryOperator.Div_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
88                         case NodeType.And:
89                                 return visitor.Binary (pc, BinaryOperator.And, Dummy.Value, Dummy.Value, Dummy.Value, data);
90                         case NodeType.Or:
91                                 return visitor.Binary (pc, BinaryOperator.Or, Dummy.Value, Dummy.Value, Dummy.Value, data);
92                         case NodeType.Shr:
93                                 return visitor.Binary (pc, BinaryOperator.Shr, Dummy.Value, Dummy.Value, Dummy.Value, data);
94                         case NodeType.Xor:
95                                 return visitor.Binary (pc, BinaryOperator.Xor, Dummy.Value, Dummy.Value, Dummy.Value, data);
96                         case NodeType.Shl:
97                                 return visitor.Binary (pc, BinaryOperator.Shl, Dummy.Value, Dummy.Value, Dummy.Value, data);
98                         case NodeType.Shr_Un:
99                                 return visitor.Binary (pc, BinaryOperator.Shr_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
100                         case NodeType.Literal:
101                                 var literal = (Literal) node;
102                                 if (literal.Value == null)
103                                         return visitor.LoadNull (pc, Dummy.Value, data);
104                                 if (literal.Type == CoreSystemTypes.Instance.TypeBoolean && (bool) literal.Value)
105                                         return visitor.LoadConst (pc, CoreSystemTypes.Instance.TypeInt32, 1, Dummy.Value, data);
106
107                                 return visitor.LoadConst (pc, literal.Type, literal.Value, Dummy.Value, data);
108                         case NodeType.This:
109                         case NodeType.Parameter:
110                                 return visitor.LoadArg (pc, (Parameter) node, false, Dummy.Value, data);
111                         case NodeType.Local:
112                                 return visitor.LoadLocal (pc, (Local) node, Dummy.Value, data);
113                         case NodeType.Branch:
114                                 var branch = (Branch) node;
115                                 if (branch.Condition != null)
116                                         return visitor.BranchTrue (pc, new PC (branch.Target), Dummy.Value, data);
117                                 return visitor.Branch (pc, new PC (branch.Target), branch.LeavesExceptionBlock, data);
118                         case NodeType.ExpressionStatement:
119                                 break;
120                         case NodeType.Box:
121                                 break;
122                         case NodeType.Return:
123                                 return visitor.Return (pc, Dummy.Value, data);
124                         case NodeType.Neg:
125                                 return visitor.Unary (pc, UnaryOperator.Neg, false, Dummy.Value, Dummy.Value, data);
126                         case NodeType.Not:
127                         case NodeType.LogicalNot:
128                                 return visitor.Unary (pc, UnaryOperator.Not, false, Dummy.Value, Dummy.Value, data);
129                         case NodeType.Conv:
130                                 break;
131                         case NodeType.Conv_I1:
132                                 return visitor.Unary (pc, UnaryOperator.Conv_i1, false, Dummy.Value, Dummy.Value, data);
133                         case NodeType.Conv_I2:
134                                 return visitor.Unary (pc, UnaryOperator.Conv_i2, false, Dummy.Value, Dummy.Value, data);
135                         case NodeType.Conv_I4:
136                                 return visitor.Unary (pc, UnaryOperator.Conv_i4, false, Dummy.Value, Dummy.Value, data);
137                         case NodeType.Conv_I8:
138                                 return visitor.Unary (pc, UnaryOperator.Conv_i8, false, Dummy.Value, Dummy.Value, data);
139                         case NodeType.Conv_R4:
140                                 return visitor.Unary (pc, UnaryOperator.Conv_r4, false, Dummy.Value, Dummy.Value, data);
141                         case NodeType.Conv_R8:
142                                 return visitor.Unary (pc, UnaryOperator.Conv_r8, false, Dummy.Value, Dummy.Value, data);
143                         case NodeType.MethodContract:
144                                 return visitor.Nop (pc, data);
145                         case NodeType.Requires:
146                                 return visitor.Assume (pc, EdgeTag.Requires, Dummy.Value, data);
147                         case NodeType.Call:
148                                 var call = (MethodCall) node;
149                                 Method method = GetMethodFrom (call.Callee);
150                                 if (method.HasGenericParameters)
151                                         throw new NotImplementedException ();
152                                 if (method.Name != null && method.DeclaringType.Name != null && method.DeclaringType.Name.EndsWith ("Contract")) {
153                                         switch (method.Name) {
154                                         case "Assume":
155                                                 if (method.Parameters.Count == 1)
156                                                         return visitor.Assume (pc, EdgeTag.Assume, Dummy.Value, data);
157                                                 break;
158                                         case "Assert":
159                                                 if (method.Parameters.Count == 1)
160                                                         return visitor.Assert (pc, EdgeTag.Assert, Dummy.Value, data);
161                                                 break;
162                                         }
163                                 }
164                                 Indexable<Dummy> parameters = DummyIndexable (method);
165                                 return visitor.Call (pc, method, false, GetVarargs (call, method), Dummy.Value, parameters, data);
166
167                         case NodeType.AssignmentStatement:
168                                 var assign = ((AssignmentStatement) node);
169                                 var local = assign.Target as Local;
170                                 if (local != null)
171                                         return visitor.StoreLocal (pc, local, Dummy.Value, data);
172                                 var parameter = assign.Target as Parameter;
173                                 if (parameter != null)
174                                         return visitor.StoreArg (pc, parameter, Dummy.Value, data);
175
176                                 var binding = assign.Target as MemberBinding;
177                                 if (binding != null) {
178                                         if (binding.BoundMember.IsStatic)
179                                                 return visitor.StoreStaticField (pc, (Field) binding.BoundMember, Dummy.Value, data);
180                                         else
181                                                 return visitor.StoreField (pc, (Field) binding.BoundMember, Dummy.Value, Dummy.Value, data);
182                                 }
183
184                                 throw new NotImplementedException ();
185                         case NodeType.Construct:
186                                 Method ctor = GetMethodFrom (((Construct) node).Constructor);
187                                 if (!(ctor.DeclaringType is ArrayTypeNode))
188                                         return visitor.NewObj (pc, ctor, Dummy.Value, DummyIndexable (ctor), data);
189                                 var arrayType = (ArrayTypeNode) ctor.DeclaringType;
190                                 return visitor.NewArray (pc, arrayType, Dummy.Value, DummyIndexable (ctor), data);
191                         default:
192                                 return visitor.Nop (pc, data);
193                         }
194
195                         throw new NotImplementedException ();
196                 }
197
198                 public bool Next (PC pc, out PC nextLabel)
199                 {
200                         Node nested;
201                         if (Decode (pc, out nested) == null && pc.Node != null) {
202                                 nextLabel = new PC (pc.Node, pc.Index + 1);
203                                 return true;
204                         }
205                         nextLabel = new PC ();
206                         return false;
207                 }
208
209                 public int GetILOffset (PC current)
210                 {
211                         throw new NotImplementedException ();
212                 }
213                 #endregion
214
215                 private static Indexable<Dummy> DummyIndexable (Method method)
216                 {
217                         return new Indexable<Dummy> (Enumerable.Range (0, method.Parameters.Count).Select (it => Dummy.Value).ToList ());
218                 }
219
220                 private static Indexable<TypeNode> GetVarargs (MethodCall call, Method method)
221                 {
222                         int methodCount = method.Parameters.Count;
223                         int callCount = call.Arguments.Count;
224
225                         if (callCount <= methodCount)
226                                 return new Indexable<TypeNode> (null);
227
228                         var array = new TypeNode[callCount - methodCount];
229                         for (int i = methodCount; i < callCount; i++)
230                                 array [i - methodCount] = call.Arguments [i - methodCount].Type;
231
232                         return new Indexable<TypeNode> (array);
233                 }
234
235                 private Method GetMethodFrom (Expression callee)
236                 {
237                         return (Method) ((MemberBinding) callee).BoundMember;
238                 }
239
240                 private static bool IsAtomicNested (Node nested)
241                 {
242                         if (nested == null)
243                                 return false;
244                         switch (nested.NodeType) {
245                         case NodeType.Local:
246                         case NodeType.Parameter:
247                         case NodeType.Literal:
248                         case NodeType.This:
249                                 return true;
250                         default:
251                                 return false;
252                         }
253                 }
254
255                 private Node Decode (PC pc, out Node nested)
256                 {
257                         Node node = DecodeInflate (pc, out nested);
258
259                         return node;
260                 }
261
262                 /// <summary>
263                 /// Decodes pc
264                 /// </summary>
265                 /// <param name="pc"></param>
266                 /// <param name="nested"></param>
267                 /// <returns>If node has nested, returns null and (nested = child). If last child given, node equals pc.Node</returns>
268                 private static Node DecodeInflate (PC pc, out Node nested)
269                 {
270                         Node node = pc.Node;
271                         if (node == null) {
272                                 nested = null;
273                                 return null;
274                         }
275
276                         int index = pc.Index;
277                         switch (node.NodeType) {
278                         case NodeType.MethodContract:
279                                 var methodContract = (MethodContract) node;
280                                 if (index < methodContract.RequiresCount) {
281                                         nested = methodContract.Requires [index];
282                                         return null;
283                                 }
284                                 if (index == methodContract.RequiresCount) {
285                                         nested = null;
286                                         return methodContract;
287                                 }
288
289                                 //todo: aggregate ensures
290                                 nested = null;
291                                 return methodContract;
292
293                         case NodeType.Requires:
294                                 var requires = (Requires) node;
295                                 if (index == 0) {
296                                         nested = requires.Assertion;
297                                         return null;
298                                 }
299                                 nested = null;
300                                 return requires;
301                         case NodeType.Block:
302                                 var block = (Block) node;
303                                 if (block.Statements == null) {
304                                         nested = null;
305                                         return block;
306                                 }
307                                 nested = index >= block.Statements.Count ? null : block.Statements [index];
308                                 return index + 1 < block.Statements.Count ? null : block;
309                         case NodeType.Return:
310                                 var ret = (Return) node;
311                                 if (ret.Expression != null && index == 0) {
312                                         nested = ret.Expression;
313                                         return null;
314                                 }
315                                 nested = null;
316                                 return ret;
317                         case NodeType.AssignmentStatement:
318                                 var assign = (AssignmentStatement) node;
319                                 int innerIndex = index;
320                                 {
321                                         var bind = assign.Target as MemberBinding;
322                                         if (bind != null) {
323                                                 ++innerIndex;
324                                                 if (bind.BoundMember.IsStatic)
325                                                         ++innerIndex;
326                                                 if (innerIndex == 1) {
327                                                         nested = bind.TargetObject;
328                                                         return null;
329                                                 }
330                                         } else if (assign.Target is Variable)
331                                                 innerIndex += 2;
332                                         else {
333                                                 nested = null;
334                                                 return assign;
335                                         }
336                                 }
337                                 if (innerIndex == 2) {
338                                         nested = assign.Source;
339                                         return null;
340                                 }
341
342                                 nested = null;
343                                 return assign;
344                         case NodeType.ExpressionStatement:
345                                 var expressionStatement = (ExpressionStatement) node;
346                                 nested = expressionStatement.Expression;
347                                 return expressionStatement;
348                         case NodeType.MethodCall:
349                         case NodeType.Call:
350                         case NodeType.Calli:
351                         case NodeType.CallVirt:
352                                 var methodCall = (MethodCall) node;
353                                 var binding = (MemberBinding) methodCall.Callee;
354                                 if (binding.BoundMember.IsStatic) {
355                                         if (index < methodCall.Arguments.Count) {
356                                                 nested = methodCall.Arguments [index];
357                                                 return null;
358                                         }
359
360                                         nested = null;
361                                         return methodCall;
362                                 }
363
364                                 if (index == 0) {
365                                         nested = binding.TargetObject;
366                                         return null;
367                                 }
368                                 if (index < methodCall.Arguments.Count + 1) {
369                                         nested = methodCall.Arguments [index - 1];
370                                         return null;
371                                 }
372                                 nested = null;
373                                 return methodCall;
374                         case NodeType.MemberBinding:
375                                 var bind1 = ((MemberBinding) node);
376                                 if (index == 0 && !bind1.BoundMember.IsStatic) {
377                                         nested = bind1.TargetObject;
378                                         return null;
379                                 }
380                                 nested = null;
381                                 return bind1;
382                         case NodeType.Construct:
383                                 var construct = (Construct) node;
384                                 if (index < construct.Arguments.Count) {
385                                         nested = construct.Arguments [index];
386                                         return null;
387                                 }
388                                 nested = null;
389                                 return construct;
390                         case NodeType.Branch:
391                                 var branch = ((Branch) node);
392                                 if (branch.Condition != null && index == 0) {
393                                         nested = branch.Condition;
394                                         return null;
395                                 }
396                                 nested = null;
397                                 return branch;
398                         default:
399                                 var binary = node as BinaryExpression;
400                                 if (binary != null) {
401                                         if (index == 0) {
402                                                 nested = binary.Left;
403                                                 return null;
404                                         }
405                                         if (index == 1) {
406                                                 nested = binary.Right;
407                                                 return null;
408                                         }
409                                         nested = null;
410                                         return binary;
411                                 }
412
413                                 var unary = node as UnaryExpression;
414                                 if (unary != null) {
415                                         if (index == 0) {
416                                                 nested = unary.Operand;
417                                                 return null;
418                                         }
419
420                                         nested = null;
421                                         return unary;
422                                 }
423
424                                 //todo: ternary
425                                 nested = null;
426                                 return node;
427                         }
428                 }
429
430                 public PC Entry (Method method)
431                 {
432                         return new PC (method.Body);
433                 }
434
435                 #region Implementation of IMethodCodeProvider<PC,Local,Parameter,Method,FieldReference,TypeReference,Dummy>
436                 public bool IsFaultHandler (ExceptionHandler handler)
437                 {
438                         return handler.HandlerType == NodeType.FaultHandler;
439                 }
440
441                 public bool IsFilterHandler (ExceptionHandler handler)
442                 {
443                         return handler.HandlerType == NodeType.Filter;
444                 }
445
446                 public bool IsFinallyHandler (ExceptionHandler handler)
447                 {
448                         return handler.HandlerType == NodeType.Finally;
449                 }
450
451                 public PC FilterExpressionStart (ExceptionHandler handler)
452                 {
453                         return new PC (handler.FilterExpression);
454                 }
455
456                 public PC HandlerEnd (ExceptionHandler handler)
457                 {
458                         throw new NotImplementedException ();
459                 }
460
461                 public PC HandlerStart (ExceptionHandler handler)
462                 {
463                         throw new NotImplementedException ();
464                 }
465
466                 public PC TryStart (ExceptionHandler handler)
467                 {
468                         throw new NotImplementedException ();
469                 }
470
471                 public PC TryEnd (ExceptionHandler handler)
472                 {
473                         throw new NotImplementedException ();
474                 }
475
476                 public bool IsCatchHandler (ExceptionHandler handler)
477                 {
478                         return handler.HandlerType == NodeType.Catch;
479                 }
480
481                 public TypeNode CatchType (ExceptionHandler handler)
482                 {
483                         return handler.FilterType;
484                 }
485
486                 public bool IsCatchAllHandler (ExceptionHandler handler)
487                 {
488                         if (!IsCatchHandler (handler))
489                                 return false;
490                         if (handler.FilterType != null)
491                                 return false;
492
493                         return true;
494                 }
495
496                 public IEnumerable<ExceptionHandler> GetTryBlocks (Method method)
497                 {
498                         yield break;
499                 }
500                 #endregion
501
502                 #region Nested type: PC
503                 public struct PC : IEquatable<PC> {
504                         public readonly int Index;
505                         public readonly Node Node;
506
507                         public PC (Node Node)
508                                 : this (Node, 0)
509                         {
510                         }
511
512                         public PC (Node node, int index)
513                         {
514                                 this.Node = node;
515                                 this.Index = index;
516                         }
517
518                         #region IEquatable<PC> Members
519                         public bool Equals (PC other)
520                         {
521                                 return Equals (other.Node, this.Node) && other.Index == this.Index;
522                         }
523                         #endregion
524
525                         public override bool Equals (object obj)
526                         {
527                                 if (ReferenceEquals (null, obj))
528                                         return false;
529                                 if (obj.GetType () != typeof (PC))
530                                         return false;
531                                 return Equals ((PC) obj);
532                         }
533
534                         public override int GetHashCode ()
535                         {
536                                 unchecked {
537                                         return ((this.Node != null ? this.Node.GetHashCode () : 0)*397) ^ this.Index;
538                                 }
539                         }
540                 }
541                 #endregion
542         }
543 }