5 // Alexander Chebaturkin (chebaturkin@gmail.com)
7 // Copyright (C) 2011 Alexander Chebaturkin
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;
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;
37 namespace Mono.CodeContracts.Static.Providers {
38 class CodeProviderImpl : IMethodCodeProvider<CodeProviderImpl.PC, ExceptionHandler> {
39 public static readonly CodeProviderImpl Instance = new CodeProviderImpl ();
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>
46 Node node = Decode (pc, out nested);
47 if (IsAtomicNested (nested))
49 else if (nested != null)
50 return visitor.Aggregate (pc, new PC (nested), nested is Block, data);
53 return visitor.Nop (pc, data);
55 switch (node.NodeType) {
58 return visitor.Nop (pc, data);
61 return visitor.Binary (pc, BinaryOperator.Clt, Dummy.Value, Dummy.Value, Dummy.Value, data);
64 return visitor.Binary (pc, BinaryOperator.Cgt, Dummy.Value, Dummy.Value, Dummy.Value, data);
67 return visitor.Binary (pc, BinaryOperator.Ceq, Dummy.Value, Dummy.Value, Dummy.Value, data);
69 return visitor.Binary (pc, BinaryOperator.Cne_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
71 return visitor.Binary (pc, BinaryOperator.Cge, Dummy.Value, Dummy.Value, Dummy.Value, data);
73 return visitor.Binary (pc, BinaryOperator.Cle, Dummy.Value, Dummy.Value, Dummy.Value, data);
75 return visitor.Binary (pc, BinaryOperator.Add, Dummy.Value, Dummy.Value, Dummy.Value, data);
77 return visitor.Binary (pc, BinaryOperator.Sub, Dummy.Value, Dummy.Value, Dummy.Value, data);
79 return visitor.Binary (pc, BinaryOperator.Rem, Dummy.Value, Dummy.Value, Dummy.Value, data);
81 return visitor.Binary (pc, BinaryOperator.Rem_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
83 return visitor.Binary (pc, BinaryOperator.Mul, Dummy.Value, Dummy.Value, Dummy.Value, data);
85 return visitor.Binary (pc, BinaryOperator.Div, Dummy.Value, Dummy.Value, Dummy.Value, data);
87 return visitor.Binary (pc, BinaryOperator.Div_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
89 return visitor.Binary (pc, BinaryOperator.And, Dummy.Value, Dummy.Value, Dummy.Value, data);
91 return visitor.Binary (pc, BinaryOperator.Or, Dummy.Value, Dummy.Value, Dummy.Value, data);
93 return visitor.Binary (pc, BinaryOperator.Shr, Dummy.Value, Dummy.Value, Dummy.Value, data);
95 return visitor.Binary (pc, BinaryOperator.Xor, Dummy.Value, Dummy.Value, Dummy.Value, data);
97 return visitor.Binary (pc, BinaryOperator.Shl, Dummy.Value, Dummy.Value, Dummy.Value, data);
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);
107 return visitor.LoadConst (pc, literal.Type, literal.Value, Dummy.Value, data);
109 case NodeType.Parameter:
110 return visitor.LoadArg (pc, (Parameter) node, false, Dummy.Value, data);
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:
122 case NodeType.Return:
123 return visitor.Return (pc, Dummy.Value, data);
125 return visitor.Unary (pc, UnaryOperator.Neg, false, Dummy.Value, Dummy.Value, data);
127 case NodeType.LogicalNot:
128 return visitor.Unary (pc, UnaryOperator.Not, false, Dummy.Value, Dummy.Value, data);
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);
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) {
155 if (method.Parameters.Count == 1)
156 return visitor.Assume (pc, EdgeTag.Assume, Dummy.Value, data);
159 if (method.Parameters.Count == 1)
160 return visitor.Assert (pc, EdgeTag.Assert, Dummy.Value, data);
164 Indexable<Dummy> parameters = DummyIndexable (method);
165 return visitor.Call (pc, method, false, GetVarargs (call, method), Dummy.Value, parameters, data);
167 case NodeType.AssignmentStatement:
168 var assign = ((AssignmentStatement) node);
169 var local = assign.Target as Local;
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);
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);
181 return visitor.StoreField (pc, (Field) binding.BoundMember, Dummy.Value, Dummy.Value, data);
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);
192 return visitor.Nop (pc, data);
195 throw new NotImplementedException ();
198 public bool Next (PC pc, out PC nextLabel)
201 if (Decode (pc, out nested) == null && pc.Node != null) {
202 nextLabel = new PC (pc.Node, pc.Index + 1);
205 nextLabel = new PC ();
209 public int GetILOffset (PC current)
211 throw new NotImplementedException ();
215 private static Indexable<Dummy> DummyIndexable (Method method)
217 return new Indexable<Dummy> (Enumerable.Range (0, method.Parameters.Count).Select (it => Dummy.Value).ToList ());
220 private static Indexable<TypeNode> GetVarargs (MethodCall call, Method method)
222 int methodCount = method.Parameters.Count;
223 int callCount = call.Arguments.Count;
225 if (callCount <= methodCount)
226 return new Indexable<TypeNode> (null);
228 var array = new TypeNode[callCount - methodCount];
229 for (int i = methodCount; i < callCount; i++)
230 array [i - methodCount] = call.Arguments [i - methodCount].Type;
232 return new Indexable<TypeNode> (array);
235 private Method GetMethodFrom (Expression callee)
237 return (Method) ((MemberBinding) callee).BoundMember;
240 private static bool IsAtomicNested (Node nested)
244 switch (nested.NodeType) {
246 case NodeType.Parameter:
247 case NodeType.Literal:
255 private Node Decode (PC pc, out Node nested)
257 Node node = DecodeInflate (pc, out nested);
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>
268 private static Node DecodeInflate (PC pc, out Node nested)
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];
284 if (index == methodContract.RequiresCount) {
286 return methodContract;
289 //todo: aggregate ensures
291 return methodContract;
293 case NodeType.Requires:
294 var requires = (Requires) node;
296 nested = requires.Assertion;
302 var block = (Block) node;
303 if (block.Statements == null) {
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;
317 case NodeType.AssignmentStatement:
318 var assign = (AssignmentStatement) node;
319 int innerIndex = index;
321 var bind = assign.Target as MemberBinding;
324 if (bind.BoundMember.IsStatic)
326 if (innerIndex == 1) {
327 nested = bind.TargetObject;
330 } else if (assign.Target is Variable)
337 if (innerIndex == 2) {
338 nested = assign.Source;
344 case NodeType.ExpressionStatement:
345 var expressionStatement = (ExpressionStatement) node;
346 nested = expressionStatement.Expression;
347 return expressionStatement;
348 case NodeType.MethodCall:
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];
365 nested = binding.TargetObject;
368 if (index < methodCall.Arguments.Count + 1) {
369 nested = methodCall.Arguments [index - 1];
374 case NodeType.MemberBinding:
375 var bind1 = ((MemberBinding) node);
376 if (index == 0 && !bind1.BoundMember.IsStatic) {
377 nested = bind1.TargetObject;
382 case NodeType.Construct:
383 var construct = (Construct) node;
384 if (index < construct.Arguments.Count) {
385 nested = construct.Arguments [index];
390 case NodeType.Branch:
391 var branch = ((Branch) node);
392 if (branch.Condition != null && index == 0) {
393 nested = branch.Condition;
399 var binary = node as BinaryExpression;
400 if (binary != null) {
402 nested = binary.Left;
406 nested = binary.Right;
413 var unary = node as UnaryExpression;
416 nested = unary.Operand;
430 public PC Entry (Method method)
432 return new PC (method.Body);
435 #region Implementation of IMethodCodeProvider<PC,Local,Parameter,Method,FieldReference,TypeReference,Dummy>
436 public bool IsFaultHandler (ExceptionHandler handler)
438 return handler.HandlerType == NodeType.FaultHandler;
441 public bool IsFilterHandler (ExceptionHandler handler)
443 return handler.HandlerType == NodeType.Filter;
446 public bool IsFinallyHandler (ExceptionHandler handler)
448 return handler.HandlerType == NodeType.Finally;
451 public PC FilterExpressionStart (ExceptionHandler handler)
453 return new PC (handler.FilterExpression);
456 public PC HandlerEnd (ExceptionHandler handler)
458 throw new NotImplementedException ();
461 public PC HandlerStart (ExceptionHandler handler)
463 throw new NotImplementedException ();
466 public PC TryStart (ExceptionHandler handler)
468 throw new NotImplementedException ();
471 public PC TryEnd (ExceptionHandler handler)
473 throw new NotImplementedException ();
476 public bool IsCatchHandler (ExceptionHandler handler)
478 return handler.HandlerType == NodeType.Catch;
481 public TypeNode CatchType (ExceptionHandler handler)
483 return handler.FilterType;
486 public bool IsCatchAllHandler (ExceptionHandler handler)
488 if (!IsCatchHandler (handler))
490 if (handler.FilterType != null)
496 public IEnumerable<ExceptionHandler> GetTryBlocks (Method method)
502 #region Nested type: PC
503 public struct PC : IEquatable<PC> {
504 public readonly int Index;
505 public readonly Node Node;
507 public PC (Node Node)
512 public PC (Node node, int index)
518 #region IEquatable<PC> Members
519 public bool Equals (PC other)
521 return Equals (other.Node, this.Node) && other.Index == this.Index;
525 public override bool Equals (object obj)
527 if (ReferenceEquals (null, obj))
529 if (obj.GetType () != typeof (PC))
531 return Equals ((PC) obj);
534 public override int GetHashCode ()
537 return ((this.Node != null ? this.Node.GetHashCode () : 0)*397) ^ this.Index;