Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / Mono.CodeContracts / Mono.CodeContracts.Rewrite / TransformContractsVisitor.cs
1 //
2 // TransformContractsVisitor.cs
3 //
4 // Authors:
5 //      Chris Bacon (chrisbacon76@gmail.com)
6 //
7 // Copyright (C) 2010 Chris Bacon
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 System.Text;
33 using Mono.CodeContracts.Rewrite;
34 using Mono.CodeContracts.Rewrite.Ast;
35 using System.Diagnostics.Contracts;
36 using Mono.Cecil;
37 using Mono.CodeContracts.Rewrite.AstVisitors;
38 using Mono.Cecil.Cil;
39
40 namespace Mono.CodeContracts.Rewrite {
41         class TransformContractsVisitor : ExprVisitor {
42
43                 public TransformContractsVisitor (ModuleDefinition module, MethodDefinition method, Dictionary<Expr, Instruction> instructionLookup, ContractsRuntime contractsRuntime)
44                 {
45                         //this.module = method.Module;
46                         this.instructionLookup = instructionLookup;
47                         this.contractsRuntime = contractsRuntime;
48                         this.methodInfo = new MethodInfo (module, method);
49                 }
50
51                 //private ModuleDefinition module;
52                 private Dictionary<Expr, Instruction> instructionLookup;
53                 private ContractsRuntime contractsRuntime;
54                 private MethodInfo methodInfo;
55
56                 private List<ContractRequiresInfo> contractRequiresInfo = new List<ContractRequiresInfo> ();
57
58                 public IEnumerable<ContractRequiresInfo> ContractRequiresInfo {
59                         get { return this.contractRequiresInfo; }
60                 }
61
62                 protected override Expr VisitCall (ExprCall e)
63                 {
64                         var call = (ExprCall)base.VisitCall (e);
65
66                         var method = e.Method;
67                         if (method.DeclaringType.FullName == "System.Diagnostics.Contracts.Contract") {
68                                 switch (method.Name) {
69                                 case "Requires":
70                                         if (!method.HasGenericParameters) {
71                                                 switch (method.Parameters.Count) {
72                                                 case 1:
73                                                         return this.ProcessRequires1 (call);
74                                                 case 2:
75                                                         return this.ProcessRequires2 (call);
76                                                 default:
77                                                         throw new NotSupportedException ("Invalid number of parameters to Contract.Requires()");
78                                                 }
79                                         } else {
80                                                 goto default;
81                                         }
82                                 default:
83                                         throw new NotSupportedException ("Cannot handle Contract." + e.Method.Name + "()");
84                                 }
85                         }
86
87                         return call;
88                 }
89
90                 private string GetConditionString (Expr e)
91                 {
92                         var vSource = new SourcePositionVisitor (this.instructionLookup);
93                         vSource.Visit (e);
94                         var extractor = new ConditionTextExtractor (vSource.SourceCodeFileName, vSource.StartPosition, vSource.EndPosition);
95                         return extractor.GetConditionText ();
96                 }
97
98                 private Expr ProcessRequires1 (ExprCall e)
99                 {
100                         MethodDefinition mRequires = this.contractsRuntime.GetRequires ();
101                         Expr conditionExpr = e.Parameters.First ();
102                         Expr nullArgExpr = new ExprLoadConstant (this.methodInfo, null);
103                         string conditionText = this.GetConditionString (e);
104                         Expr conditionStringExpr = new ExprLoadConstant (this.methodInfo, conditionText);
105                         var call = new ExprCall (this.methodInfo, mRequires, new Expr [] { conditionExpr, nullArgExpr, conditionStringExpr });
106
107                         this.contractRequiresInfo.Add (new ContractRequiresInfo (e, call));
108
109                         return call;
110                 }
111
112                 private Expr ProcessRequires2 (ExprCall e)
113                 {
114                         MethodDefinition mRequires = this.contractsRuntime.GetRequires ();
115                         Expr conditionExpr = e.Parameters.First ();
116                         Expr msgExpr = e.Parameters.ElementAt (1);
117                         string conditionText = this.GetConditionString (e);
118                         Expr conditionStringExpr = new ExprLoadConstant (this.methodInfo, conditionText);
119                         var call = new ExprCall (this.methodInfo, mRequires, new Expr [] { conditionExpr, msgExpr, conditionStringExpr });
120
121                         this.contractRequiresInfo.Add (new ContractRequiresInfo (e, call));
122
123                         return call;
124                 }
125
126         }
127 }