00f0e99112da2c8944d6423b0a100965e054de1a
[mono.git] / mcs / class / Mono.CodeContracts / Mono.CodeContracts.Rewrite / ContractsRuntime.cs
1 //\r
2 // ContractRuntime.cs\r
3 //\r
4 // Authors:\r
5 //      Chris Bacon (chrisbacon76@gmail.com)\r
6 //\r
7 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)\r
8 //\r
9 // Permission is hereby granted, free of charge, to any person obtaining\r
10 // a copy of this software and associated documentation files (the\r
11 // "Software"), to deal in the Software without restriction, including\r
12 // without limitation the rights to use, copy, modify, merge, publish,\r
13 // distribute, sublicense, and/or sell copies of the Software, and to\r
14 // permit persons to whom the Software is furnished to do so, subject to\r
15 // the following conditions:\r
16 // \r
17 // The above copyright notice and this permission notice shall be\r
18 // included in all copies or substantial portions of the Software.\r
19 // \r
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
27 //\r
28 \r
29 using System;\r
30 using System.Collections.Generic;\r
31 using System.Linq;\r
32 using System.Text;\r
33 using Mono.Cecil;\r
34 using System.Diagnostics.Contracts;\r
35 using Mono.Cecil.Cil;\r
36 using System.Diagnostics.Contracts.Internal;\r
37 using System.Diagnostics;\r
38 using System.Runtime.ConstrainedExecution;\r
39 using System.Runtime.CompilerServices;\r
40 \r
41 namespace Mono.CodeContracts.Rewrite {\r
42         class ContractsRuntime {\r
43 \r
44                 private const string Namespace = "System.Diagnostics.Contracts";\r
45 \r
46                 public ContractsRuntime (ModuleDefinition module, RewriterOptions options)\r
47                 {\r
48                         this.module = module;\r
49                         this.options = options;\r
50                 }\r
51 \r
52                 private ModuleDefinition module;\r
53                 private RewriterOptions options;\r
54 \r
55                 private TypeDefinition typeContractsRuntime = null;\r
56                 private TypeDefinition typeContractException = null;\r
57                 private MethodDefinition methodContractExceptionCons = null;\r
58                 private MethodDefinition methodTriggerFailure = null;\r
59                 private MethodDefinition methodReportFailure = null;\r
60                 private MethodDefinition methodRequires = null;\r
61 \r
62                 private void EnsureTypeContractRuntime ()\r
63                 {\r
64                         if (this.typeContractsRuntime == null) {\r
65                                 // namespace System.Diagnostics.Contracts {\r
66                                 //     [CompilerGenerated]\r
67                                 //     private static class __ContractsRuntime {\r
68                                 //     }\r
69                                 // }\r
70                                 \r
71                                 // Create type\r
72                                 TypeReference typeObject = this.module.Import (typeof (object));\r
73                                 TypeDefinition type = new TypeDefinition (Namespace, "__ContractsRuntime",\r
74                                         TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.AutoClass,\r
75                                         typeObject);\r
76                                 this.module.Types.Add (type);\r
77                                 // Attach custom attributes\r
78                                 var attrCompilerGeneratedCons = typeof (CompilerGeneratedAttribute).GetConstructor (Type.EmptyTypes);\r
79                                 CustomAttribute attrCompilerGenerated = new CustomAttribute (this.module.Import (attrCompilerGeneratedCons));\r
80                                 type.CustomAttributes.Add (attrCompilerGenerated);\r
81                                 // Store type\r
82                                 this.typeContractsRuntime = type;\r
83                         }\r
84                 }\r
85 \r
86                 private void EnsureTypeContractException ()\r
87                 {\r
88                         if (this.options.ThrowOnFailure && this.typeContractException == null) {\r
89                                 // [CompilerGenerated]\r
90                                 // private class ContractException : Exception {\r
91                                 //     internal ContractException(ContractFailureKind kind, string usermsg, string condition, Exception inner)\r
92                                 //         : base(failure, inner)\r
93                                 //     {\r
94                                 //     }\r
95                                 // }\r
96                                 \r
97                                 // Prepare type references\r
98                                 TypeReference typeVoid = this.module.Import (typeof (void));\r
99                                 TypeReference typeContractFailureKind = this.module.Import (typeof (ContractFailureKind));\r
100                                 TypeReference typeString = this.module.Import (typeof (string));\r
101                                 TypeReference typeException = this.module.Import (typeof (Exception));\r
102                                 // Create type\r
103                                 TypeDefinition type = new TypeDefinition ("", "ContractException",\r
104                                         TypeAttributes.NestedPrivate | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeException);\r
105                                 this.typeContractsRuntime.NestedTypes.Add (type);\r
106                                 // Create constructor\r
107                                 MethodDefinition cons = new MethodDefinition (".ctor",\r
108                                         MethodAttributes.Assem | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, typeVoid);\r
109                                 cons.Parameters.Add (new ParameterDefinition ("kind", ParameterAttributes.None, typeContractFailureKind));\r
110                                 cons.Parameters.Add (new ParameterDefinition ("failure", ParameterAttributes.None, typeString));\r
111                                 cons.Parameters.Add (new ParameterDefinition ("usermsg", ParameterAttributes.None, typeString));\r
112                                 cons.Parameters.Add (new ParameterDefinition ("condition", ParameterAttributes.None, typeString));\r
113                                 cons.Parameters.Add (new ParameterDefinition ("inner", ParameterAttributes.None, typeException));\r
114                                 var il = cons.Body.GetILProcessor ();\r
115                                 il.Emit (OpCodes.Ldarg_0);\r
116                                 il.Emit (OpCodes.Ldarg_2);\r
117                                 il.Emit (OpCodes.Ldarg_S, cons.Parameters [4]);\r
118                                 MethodReference mExceptionCons = this.module.Import (typeof (Exception).GetConstructor (new [] { typeof (string), typeof (Exception) }));\r
119                                 il.Emit (OpCodes.Call, mExceptionCons);\r
120                                 il.Emit (OpCodes.Ret);\r
121                                 type.Methods.Add (cons);\r
122                                 // Attach custom attributes\r
123                                 var attrCompilerGeneratedCons = typeof (CompilerGeneratedAttribute).GetConstructor (Type.EmptyTypes);\r
124                                 CustomAttribute attrCompilerGenerated = new CustomAttribute (this.module.Import (attrCompilerGeneratedCons));\r
125                                 type.CustomAttributes.Add (attrCompilerGenerated);\r
126                                 // Store constructor and type\r
127                                 this.methodContractExceptionCons = cons;\r
128                                 this.typeContractException = type;\r
129                         }\r
130                 }\r
131 \r
132                 private void EnsureMethodTriggerFailure ()\r
133                 {\r
134                         if (this.methodTriggerFailure == null) {\r
135                                 // if the ThrowOnFailure option is true, then:\r
136                                 // internal static void TriggerFailure(ContractFailureKind kind, string message, string userMessage, string conditionText, Exception inner)\r
137                                 // {\r
138                                 //     throw new ContractException(kind, message, userMessage, conditionText, inner);\r
139                                 // }\r
140                                 \r
141                                 // if the ThrowOnFailure option is false, then:\r
142                                 // internal static void TriggerFailure(ContractFailureKind kind, string message, string userMessage, string conditionText, Exception inner)\r
143                                 // {\r
144                                 //     Debug.Fail(message, userMessage);\r
145                                 // }\r
146                                 \r
147                                 // Prepare type references\r
148                                 TypeReference typeVoid = this.module.Import (typeof (void));\r
149                                 TypeReference typeContractFailureKind = this.module.Import (typeof (ContractFailureKind));\r
150                                 TypeReference typeString = this.module.Import (typeof (string));\r
151                                 TypeReference typeException = this.module.Import (typeof (Exception));\r
152                                 // Create method\r
153                                 MethodDefinition method = new MethodDefinition ("TriggerFailure",\r
154                                         MethodAttributes.Assem | MethodAttributes.Static, typeVoid);\r
155                                 method.Parameters.Add (new ParameterDefinition ("kind", ParameterAttributes.None, typeContractFailureKind));\r
156                                 method.Parameters.Add (new ParameterDefinition ("message", ParameterAttributes.None, typeString));\r
157                                 method.Parameters.Add (new ParameterDefinition ("userMessage", ParameterAttributes.None, typeString));\r
158                                 method.Parameters.Add (new ParameterDefinition ("conditionText", ParameterAttributes.None, typeString));\r
159                                 method.Parameters.Add (new ParameterDefinition ("inner", ParameterAttributes.None, typeException));\r
160                                 var il = method.Body.GetILProcessor ();\r
161                                 if (this.options.ThrowOnFailure) {\r
162                                         il.Emit (OpCodes.Ldarg_0);\r
163                                         il.Emit (OpCodes.Ldarg_1);\r
164                                         il.Emit (OpCodes.Ldarg_2);\r
165                                         il.Emit (OpCodes.Ldarg_3);\r
166                                         il.Emit (OpCodes.Ldarg_S, method.Parameters [4]);\r
167                                         il.Emit (OpCodes.Newobj, this.methodContractExceptionCons);\r
168                                         il.Emit (OpCodes.Throw);\r
169                                 } else {\r
170                                         var mDebugFail = typeof (Debug).GetMethod ("Fail", new [] { typeof (string), typeof(string) });\r
171                                         MethodReference methodDebugFail = this.module.Import (mDebugFail);\r
172                                         il.Emit (OpCodes.Ldarg_1);\r
173                                         il.Emit (OpCodes.Ldarg_2);\r
174                                         il.Emit (OpCodes.Call, methodDebugFail);\r
175                                         il.Emit (OpCodes.Ret);\r
176                                 }\r
177                                 this.typeContractsRuntime.Methods.Add (method);\r
178                                 this.methodTriggerFailure = method;\r
179                         }\r
180                 }\r
181 \r
182                 private void EnsureMethodReportFailure ()\r
183                 {\r
184                         if (this.methodReportFailure == null) {\r
185                                 // internal static void ReportFailure(ContractFailureKind kind, string message, string conditionText, Exception inner)\r
186                                 // {\r
187                                 //     string s = ContractHelper.RaiseContractFailedEvent(kind, message, conditionText, inner);\r
188                                 //     if (s != null) {\r
189                                 //         TriggerFailure(kind, s, message, conditionText, inner);\r
190                                 //     }\r
191                                 // }\r
192                                 \r
193                                 // Prepare type references\r
194                                 TypeReference typeVoid = this.module.Import (typeof (void));\r
195                                 TypeReference typeContractFailureKind = this.module.Import (typeof (ContractFailureKind));\r
196                                 TypeReference typeString = this.module.Import (typeof (string));\r
197                                 TypeReference typeException = this.module.Import (typeof (Exception));\r
198                                 MethodReference mRaiseContractFailedEvent = this.module.Import (typeof (ContractHelper).GetMethod ("RaiseContractFailedEvent"));\r
199                                 // Create method\r
200                                 MethodDefinition method = new MethodDefinition ("ReportFailure",\r
201                                         MethodAttributes.Assem | MethodAttributes.Static, typeVoid);\r
202                                 method.Parameters.Add (new ParameterDefinition ("kind", ParameterAttributes.None, typeContractFailureKind));\r
203                                 method.Parameters.Add (new ParameterDefinition ("message", ParameterAttributes.None, typeString));\r
204                                 method.Parameters.Add (new ParameterDefinition ("conditionText", ParameterAttributes.None, typeString));\r
205                                 method.Parameters.Add (new ParameterDefinition ("inner", ParameterAttributes.None, typeException));\r
206                                 VariableDefinition vMsg = new VariableDefinition ("msg", typeString);\r
207                                 method.Body.Variables.Add (vMsg);\r
208                                 var il = method.Body.GetILProcessor ();\r
209                                 il.Emit (OpCodes.Ldarg_0);\r
210                                 il.Emit (OpCodes.Ldarg_1);\r
211                                 il.Emit (OpCodes.Ldarg_2);\r
212                                 il.Emit (OpCodes.Ldarg_3);\r
213                                 il.Emit (OpCodes.Call, mRaiseContractFailedEvent);\r
214                                 il.Emit (OpCodes.Stloc_0);\r
215                                 il.Emit (OpCodes.Ldloc_0);\r
216                                 var instRet = il.Create (OpCodes.Ret);\r
217                                 il.Emit (OpCodes.Brfalse_S, instRet);\r
218                                 il.Emit (OpCodes.Ldarg_0);\r
219                                 il.Emit (OpCodes.Ldloc_0);\r
220                                 il.Emit (OpCodes.Ldarg_1);\r
221                                 il.Emit (OpCodes.Ldarg_2);\r
222                                 il.Emit (OpCodes.Ldarg_3);\r
223                                 il.Emit (OpCodes.Call, this.methodTriggerFailure);\r
224                                 il.Append (instRet);\r
225                                 this.typeContractsRuntime.Methods.Add (method);\r
226                                 this.methodReportFailure = method;\r
227                         }\r
228                 }\r
229 \r
230                 private void EnsureGlobal ()\r
231                 {\r
232                         this.EnsureTypeContractRuntime ();\r
233                         this.EnsureTypeContractException ();\r
234                         this.EnsureMethodTriggerFailure ();\r
235                         this.EnsureMethodReportFailure ();\r
236                 }\r
237 \r
238                 public MethodDefinition GetRequires ()\r
239                 {\r
240                         this.EnsureGlobal ();\r
241                         if (this.methodRequires == null) {\r
242                                 // [DebuggerNonUserCode]\r
243                                 // [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]\r
244                                 // internal static void Requires(bool condition, string message, string conditionText)\r
245                                 // {\r
246                                 //     if (!condition) {\r
247                                 //         ReportFailure(ContractFailureKind.Precondition, message, conditionText, null);\r
248                                 //     }\r
249                                 // }\r
250                                 \r
251                                 // Prepare type references\r
252                                 TypeReference typeVoid = this.module.Import (typeof (void));\r
253                                 TypeReference typeBoolean = this.module.Import (typeof (bool));\r
254                                 TypeReference typeString = this.module.Import (typeof (string));\r
255                                 // Create method\r
256                                 MethodDefinition method = new MethodDefinition ("Requires",\r
257                                     MethodAttributes.Assem | MethodAttributes.Static, typeVoid);\r
258                                 method.Parameters.Add (new ParameterDefinition ("condition", ParameterAttributes.None, typeBoolean));\r
259                                 method.Parameters.Add (new ParameterDefinition ("message", ParameterAttributes.None, typeString));\r
260                                 method.Parameters.Add (new ParameterDefinition ("conditionText", ParameterAttributes.None, typeString));\r
261                                 var il = method.Body.GetILProcessor ();\r
262                                 il.Emit (OpCodes.Ldarg_0);\r
263                                 var instRet = il.Create(OpCodes.Ret);\r
264                                 il.Emit (OpCodes.Brtrue_S, instRet);\r
265                                 il.Emit (OpCodes.Ldc_I4_0); // Assumes ContractFailureKind.Precondition == 0\r
266                                 il.Emit (OpCodes.Ldarg_1);\r
267                                 il.Emit (OpCodes.Ldarg_2);\r
268                                 il.Emit (OpCodes.Ldnull);\r
269                                 il.Emit (OpCodes.Call, this.methodReportFailure);\r
270                                 il.Append (instRet);\r
271                                 this.typeContractsRuntime.Methods.Add (method);\r
272                                 // Attach custom attributes\r
273                                 var attrDebugNonUserCodeCons = typeof (DebuggerNonUserCodeAttribute).GetConstructor (Type.EmptyTypes);\r
274                                 CustomAttribute attrDebugNonUserCode = new CustomAttribute (this.module.Import (attrDebugNonUserCodeCons));\r
275                                 method.CustomAttributes.Add (attrDebugNonUserCode);\r
276                                 var attrReliabilityContractCons = typeof (ReliabilityContractAttribute).GetConstructor (new [] { typeof (Consistency), typeof (Cer) });\r
277                                 // Blob for attribute: new ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)\r
278                                 byte [] blob = new byte [] { 1, 0, 3, 0, 0, 0, 1, 0, 0, 0 };\r
279                                 CustomAttribute attrReliabilityContract = new CustomAttribute (this.module.Import (attrReliabilityContractCons), blob);\r
280                                 method.CustomAttributes.Add (attrReliabilityContract);\r
281                                 // Store method\r
282                                 this.methodRequires = method;\r
283                         }\r
284                         return this.methodRequires;\r
285                 }\r
286 \r
287         }\r
288 }\r