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 
Next(PC pc, out PC nextLabel)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 
GetILOffset(PC current)209 		public int GetILOffset (PC current)
210 		{
211 			throw new NotImplementedException ();
212 		}
213 		#endregion
214 
DummyIndexable(Method method)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 
GetVarargs(MethodCall call, Method method)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 
GetMethodFrom(Expression callee)235 		private Method GetMethodFrom (Expression callee)
236 		{
237 			return (Method) ((MemberBinding) callee).BoundMember;
238 		}
239 
IsAtomicNested(Node nested)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 
Decode(PC pc, out Node nested)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>
DecodeInflate(PC pc, out Node nested)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 
Entry(Method method)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>
IsFaultHandler(ExceptionHandler handler)436 		public bool IsFaultHandler (ExceptionHandler handler)
437 		{
438 			return handler.HandlerType == NodeType.FaultHandler;
439 		}
440 
IsFilterHandler(ExceptionHandler handler)441 		public bool IsFilterHandler (ExceptionHandler handler)
442 		{
443 			return handler.HandlerType == NodeType.Filter;
444 		}
445 
IsFinallyHandler(ExceptionHandler handler)446 		public bool IsFinallyHandler (ExceptionHandler handler)
447 		{
448 			return handler.HandlerType == NodeType.Finally;
449 		}
450 
FilterExpressionStart(ExceptionHandler handler)451 		public PC FilterExpressionStart (ExceptionHandler handler)
452 		{
453 			return new PC (handler.FilterExpression);
454 		}
455 
HandlerEnd(ExceptionHandler handler)456 		public PC HandlerEnd (ExceptionHandler handler)
457 		{
458 			throw new NotImplementedException ();
459 		}
460 
HandlerStart(ExceptionHandler handler)461 		public PC HandlerStart (ExceptionHandler handler)
462 		{
463 			throw new NotImplementedException ();
464 		}
465 
TryStart(ExceptionHandler handler)466 		public PC TryStart (ExceptionHandler handler)
467 		{
468 			throw new NotImplementedException ();
469 		}
470 
TryEnd(ExceptionHandler handler)471 		public PC TryEnd (ExceptionHandler handler)
472 		{
473 			throw new NotImplementedException ();
474 		}
475 
IsCatchHandler(ExceptionHandler handler)476 		public bool IsCatchHandler (ExceptionHandler handler)
477 		{
478 			return handler.HandlerType == NodeType.Catch;
479 		}
480 
CatchType(ExceptionHandler handler)481 		public TypeNode CatchType (ExceptionHandler handler)
482 		{
483 			return handler.FilterType;
484 		}
485 
IsCatchAllHandler(ExceptionHandler handler)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 
GetTryBlocks(Method method)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 
PCMono.CodeContracts.Static.Providers.CodeProviderImpl.PC507 			public PC (Node Node)
508 				: this (Node, 0)
509 			{
510 			}
511 
PCMono.CodeContracts.Static.Providers.CodeProviderImpl.PC512 			public PC (Node node, int index)
513 			{
514 				this.Node = node;
515 				this.Index = index;
516 			}
517 
518 			#region IEquatable<PC> Members
EqualsMono.CodeContracts.Static.Providers.CodeProviderImpl.PC519 			public bool Equals (PC other)
520 			{
521 				return Equals (other.Node, this.Node) && other.Index == this.Index;
522 			}
523 			#endregion
524 
EqualsMono.CodeContracts.Static.Providers.CodeProviderImpl.PC525 			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 
GetHashCodeMono.CodeContracts.Static.Providers.CodeProviderImpl.PC534 			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 }
544