2 // ContractRuntime.cs
\r
5 // Chris Bacon (chrisbacon76@gmail.com)
\r
7 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
\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
17 // The above copyright notice and this permission notice shall be
\r
18 // included in all copies or substantial portions of the Software.
\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
30 using System.Collections.Generic;
\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
41 namespace Mono.CodeContracts.Rewrite {
\r
42 class ContractsRuntime {
\r
44 private const string Namespace = "System.Diagnostics.Contracts";
\r
46 public ContractsRuntime (ModuleDefinition module, RewriterOptions options)
\r
48 this.module = module;
\r
49 this.options = options;
\r
52 private ModuleDefinition module;
\r
53 private RewriterOptions options;
\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
62 private void EnsureTypeContractRuntime ()
\r
64 if (this.typeContractsRuntime == null) {
\r
65 // namespace System.Diagnostics.Contracts {
\r
66 // [CompilerGenerated]
\r
67 // private static class __ContractsRuntime {
\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
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
82 this.typeContractsRuntime = type;
\r
86 private void EnsureTypeContractException ()
\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
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
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
132 private void EnsureMethodTriggerFailure ()
\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
138 // throw new ContractException(kind, message, userMessage, conditionText, inner);
\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
144 // Debug.Fail(message, userMessage);
\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
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
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
177 this.typeContractsRuntime.Methods.Add (method);
\r
178 this.methodTriggerFailure = method;
\r
182 private void EnsureMethodReportFailure ()
\r
184 if (this.methodReportFailure == null) {
\r
185 // internal static void ReportFailure(ContractFailureKind kind, string message, string conditionText, Exception inner)
\r
187 // string s = ContractHelper.RaiseContractFailedEvent(kind, message, conditionText, inner);
\r
188 // if (s != null) {
\r
189 // TriggerFailure(kind, s, message, conditionText, inner);
\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
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
230 private void EnsureGlobal ()
\r
232 this.EnsureTypeContractRuntime ();
\r
233 this.EnsureTypeContractException ();
\r
234 this.EnsureMethodTriggerFailure ();
\r
235 this.EnsureMethodReportFailure ();
\r
238 public MethodDefinition GetRequires ()
\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
246 // if (!condition) {
\r
247 // ReportFailure(ContractFailureKind.Precondition, message, conditionText, null);
\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
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
282 this.methodRequires = method;
\r
284 return this.methodRequires;
\r