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