5 // Chris Bacon (chrisbacon76@gmail.com)
7 // Copyright (C) 2010 Chris Bacon
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System.Collections.Generic;
34 using System.Diagnostics.Contracts;
36 using System.Diagnostics;
37 using System.Runtime.ConstrainedExecution;
38 using System.Runtime.CompilerServices;
40 namespace Mono.CodeContracts.Rewrite {
41 class ContractsRuntime {
43 private const string Namespace = "System.Diagnostics.Contracts";
45 public ContractsRuntime (ModuleDefinition module, RewriterOptions options)
48 this.options = options;
51 private ModuleDefinition module;
52 private RewriterOptions options;
54 private TypeDefinition typeContractsRuntime = null;
55 private TypeDefinition typeContractException = null;
56 private MethodDefinition methodContractExceptionCons = null;
57 private MethodDefinition methodTriggerFailure = null;
58 private MethodDefinition methodReportFailure = null;
59 private MethodDefinition methodRequires = null;
61 private void EnsureTypeContractRuntime ()
63 if (this.typeContractsRuntime == null) {
64 // namespace System.Diagnostics.Contracts {
65 // [CompilerGenerated]
66 // private static class __ContractsRuntime {
71 TypeReference typeObject = this.module.Import (typeof (object));
72 TypeDefinition type = new TypeDefinition ("__ContractsRuntime", Namespace,
73 TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, // | TypeAttributes.BeforeFieldInit,
75 this.module.Types.Add (type);
76 // Attach custom attributes
77 var attrCompilerGeneratedCons = typeof (CompilerGeneratedAttribute).GetConstructor (Type.EmptyTypes);
78 CustomAttribute attrCompilerGenerated = new CustomAttribute (this.module.Import (attrCompilerGeneratedCons));
79 type.CustomAttributes.Add (attrCompilerGenerated);
81 this.typeContractsRuntime = type;
85 private void EnsureTypeContractException ()
87 if (this.options.ThrowOnFailure && this.typeContractException == null) {
88 // [CompilerGenerated]
89 // private class ContractException : Exception {
90 // internal ContractException(ContractFailureKind kind, string usermsg, string condition, Exception inner)
91 // : base(failure, inner)
96 // Prepare type references
97 TypeReference typeVoid = this.module.Import (typeof (void));
98 TypeReference typeContractFailureKind = this.module.Import (typeof (ContractFailureKind));
99 TypeReference typeString = this.module.Import (typeof (string));
100 TypeReference typeException = this.module.Import (typeof (Exception));
102 TypeDefinition type = new TypeDefinition ("ContractException", Namespace,
103 TypeAttributes.NestedPrivate | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeException);
104 //this.typeContractsRuntime.NestedTypes.Add (type);
105 this.module.Types.Add(type);
106 // Create constructor
107 MethodDefinition cons = new MethodDefinition (".ctor",
108 MethodAttributes.Assembly | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, typeVoid);
109 cons.Parameters.Add (new ParameterDefinition ("kind", ParameterAttributes.None, typeContractFailureKind));
110 cons.Parameters.Add (new ParameterDefinition ("failure", ParameterAttributes.None, typeString));
111 cons.Parameters.Add (new ParameterDefinition ("usermsg", ParameterAttributes.None, typeString));
112 cons.Parameters.Add (new ParameterDefinition ("condition", ParameterAttributes.None, typeString));
113 cons.Parameters.Add (new ParameterDefinition ("inner", ParameterAttributes.None, typeException));
114 var il = cons.Body.GetILProcessor ();
115 il.Emit (OpCodes.Ldarg_0);
116 il.Emit (OpCodes.Ldarg_2);
117 il.Emit (OpCodes.Ldarg_S, cons.Parameters [4]);
118 MethodReference mExceptionCons = this.module.Import (typeof (Exception).GetConstructor (new [] { typeof (string), typeof (Exception) }));
119 il.Emit (OpCodes.Call, mExceptionCons);
120 il.Emit (OpCodes.Ret);
121 type.Methods.Add (cons);
122 // Attach custom attributes
123 var attrCompilerGeneratedCons = typeof (CompilerGeneratedAttribute).GetConstructor (Type.EmptyTypes);
124 CustomAttribute attrCompilerGenerated = new CustomAttribute (this.module.Import (attrCompilerGeneratedCons));
125 type.CustomAttributes.Add (attrCompilerGenerated);
126 // Store constructor and type
127 this.methodContractExceptionCons = cons;
128 this.typeContractException = type;
132 private void EnsureMethodTriggerFailure ()
134 if (this.methodTriggerFailure == null) {
135 // if the ThrowOnFailure option is true, then:
136 // internal static void TriggerFailure(ContractFailureKind kind, string message, string userMessage, string conditionText, Exception inner)
138 // throw new ContractException(kind, message, userMessage, conditionText, inner);
141 // if the ThrowOnFailure option is false, then:
142 // internal static void TriggerFailure(ContractFailureKind kind, string message, string userMessage, string conditionText, Exception inner)
144 // Debug.Fail(message, userMessage);
147 // Prepare type references
148 TypeReference typeVoid = this.module.Import (typeof (void));
149 TypeReference typeContractFailureKind = this.module.Import (typeof (ContractFailureKind));
150 TypeReference typeString = this.module.Import (typeof (string));
151 TypeReference typeException = this.module.Import (typeof (Exception));
153 MethodDefinition method = new MethodDefinition ("TriggerFailure",
154 MethodAttributes.Assembly | MethodAttributes.Static, typeVoid);
155 method.Parameters.Add (new ParameterDefinition ("kind", ParameterAttributes.None, typeContractFailureKind));
156 method.Parameters.Add (new ParameterDefinition ("message", ParameterAttributes.None, typeString));
157 method.Parameters.Add (new ParameterDefinition ("userMessage", ParameterAttributes.None, typeString));
158 method.Parameters.Add (new ParameterDefinition ("conditionText", ParameterAttributes.None, typeString));
159 method.Parameters.Add (new ParameterDefinition ("inner", ParameterAttributes.None, typeException));
160 var il = method.Body.GetILProcessor ();
161 if (this.options.ThrowOnFailure) {
162 il.Emit (OpCodes.Ldarg_0);
163 il.Emit (OpCodes.Ldarg_1);
164 il.Emit (OpCodes.Ldarg_2);
165 il.Emit (OpCodes.Ldarg_3);
166 il.Emit (OpCodes.Ldarg_S, method.Parameters [4]);
167 il.Emit (OpCodes.Newobj, this.methodContractExceptionCons);
168 il.Emit (OpCodes.Throw);
170 var mDebugFail = typeof (Debug).GetMethod ("Fail", new [] { typeof (string), typeof(string) });
171 MethodReference methodDebugFail = this.module.Import (mDebugFail);
172 il.Emit (OpCodes.Ldarg_1);
173 il.Emit (OpCodes.Ldarg_2);
174 il.Emit (OpCodes.Call, methodDebugFail);
175 il.Emit (OpCodes.Ret);
177 this.typeContractsRuntime.Methods.Add (method);
178 this.methodTriggerFailure = method;
182 private void EnsureMethodReportFailure ()
184 if (this.methodReportFailure == null) {
185 // internal static void ReportFailure(ContractFailureKind kind, string message, string conditionText, Exception inner)
187 // string s = ContractHelper.RaiseContractFailedEvent(kind, message, conditionText, inner);
189 // TriggerFailure(kind, s, message, conditionText, inner);
193 // Prepare type references
194 TypeReference typeVoid = this.module.Import (typeof (void));
195 TypeReference typeContractFailureKind = this.module.Import (typeof (ContractFailureKind));
196 TypeReference typeString = this.module.Import (typeof (string));
197 TypeReference typeException = this.module.Import (typeof (Exception));
198 var helper = typeof (ContractHelper);
199 MethodReference mRaiseContractFailedEvent = this.module.Import (helper.GetMethod ("RaiseContractFailedEvent"));
201 MethodDefinition method = new MethodDefinition ("ReportFailure",
202 MethodAttributes.Assembly | MethodAttributes.Static, typeVoid);
203 method.Parameters.Add (new ParameterDefinition ("kind", ParameterAttributes.None, typeContractFailureKind));
204 method.Parameters.Add (new ParameterDefinition ("message", ParameterAttributes.None, typeString));
205 method.Parameters.Add (new ParameterDefinition ("conditionText", ParameterAttributes.None, typeString));
206 method.Parameters.Add (new ParameterDefinition ("inner", ParameterAttributes.None, typeException));
207 VariableDefinition vMsg = new VariableDefinition (typeString);
208 method.Body.Variables.Add (vMsg);
209 method.Body.InitLocals = true;
210 var il = method.Body.GetILProcessor ();
211 il.Emit (OpCodes.Ldarg_0);
212 il.Emit (OpCodes.Ldarg_1);
213 il.Emit (OpCodes.Ldarg_2);
214 il.Emit (OpCodes.Ldarg_3);
215 il.Emit (OpCodes.Call, mRaiseContractFailedEvent);
216 il.Emit (OpCodes.Stloc_0);
217 il.Emit (OpCodes.Ldloc_0);
218 var instRet = il.Create (OpCodes.Ret);
219 il.Emit (OpCodes.Brfalse_S, instRet);
220 il.Emit (OpCodes.Ldarg_0);
221 il.Emit (OpCodes.Ldloc_0);
222 il.Emit (OpCodes.Ldarg_1);
223 il.Emit (OpCodes.Ldarg_2);
224 il.Emit (OpCodes.Ldarg_3);
225 il.Emit (OpCodes.Call, this.methodTriggerFailure);
227 this.typeContractsRuntime.Methods.Add (method);
228 this.methodReportFailure = method;
232 private void EnsureGlobal ()
234 this.EnsureTypeContractRuntime ();
235 this.EnsureTypeContractException ();
236 this.EnsureMethodTriggerFailure ();
237 this.EnsureMethodReportFailure ();
240 public MethodDefinition GetRequires ()
242 this.EnsureGlobal ();
243 if (this.methodRequires == null) {
244 // [DebuggerNonUserCode]
245 // [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
246 // internal static void Requires(bool condition, string message, string conditionText)
249 // ReportFailure(ContractFailureKind.Precondition, message, conditionText, null);
253 // Prepare type references
254 TypeReference typeVoid = this.module.Import (typeof (void));
255 TypeReference typeBoolean = this.module.Import (typeof (bool));
256 TypeReference typeString = this.module.Import (typeof (string));
258 MethodDefinition method = new MethodDefinition ("Requires",
259 MethodAttributes.Assembly | MethodAttributes.Static, typeVoid);
260 method.Parameters.Add (new ParameterDefinition ("condition", ParameterAttributes.None, typeBoolean));
261 method.Parameters.Add (new ParameterDefinition ("message", ParameterAttributes.None, typeString));
262 method.Parameters.Add (new ParameterDefinition ("conditionText", ParameterAttributes.None, typeString));
263 var il = method.Body.GetILProcessor ();
264 il.Emit (OpCodes.Ldarg_0);
265 var instRet = il.Create(OpCodes.Ret);
266 il.Emit (OpCodes.Brtrue_S, instRet);
267 il.Emit (OpCodes.Ldc_I4_0); // Assumes ContractFailureKind.Precondition == 0
268 il.Emit (OpCodes.Ldarg_1);
269 il.Emit (OpCodes.Ldarg_2);
270 il.Emit (OpCodes.Ldnull);
271 il.Emit (OpCodes.Call, this.methodReportFailure);
273 this.typeContractsRuntime.Methods.Add (method);
274 // Attach custom attributes
275 var attrDebugNonUserCodeCons = typeof (DebuggerNonUserCodeAttribute).GetConstructor (Type.EmptyTypes);
276 CustomAttribute attrDebugNonUserCode = new CustomAttribute (this.module.Import (attrDebugNonUserCodeCons));
277 method.CustomAttributes.Add (attrDebugNonUserCode);
278 var attrReliabilityContractCons = typeof (ReliabilityContractAttribute).GetConstructor (new [] { typeof (Consistency), typeof (Cer) });
279 // Blob for attribute: new ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)
280 byte [] blob = new byte [] { 1, 0, 3, 0, 0, 0, 1, 0, 0, 0 };
281 CustomAttribute attrReliabilityContract = new CustomAttribute (this.module.Import (attrReliabilityContractCons), blob);
282 method.CustomAttributes.Add (attrReliabilityContract);
284 this.methodRequires = method;
286 return this.methodRequires;