Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

544 lines
16 KiB
C#

//
// CodeProviderImpl.cs
//
// Authors:
// Alexander Chebaturkin (chebaturkin@gmail.com)
//
// Copyright (C) 2011 Alexander Chebaturkin
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.CodeContracts.Static.AST;
using Mono.CodeContracts.Static.AST.Visitors;
using Mono.CodeContracts.Static.ControlFlow;
using Mono.CodeContracts.Static.DataStructures;
namespace Mono.CodeContracts.Static.Providers {
class CodeProviderImpl : IMethodCodeProvider<CodeProviderImpl.PC, ExceptionHandler> {
public static readonly CodeProviderImpl Instance = new CodeProviderImpl ();
#region IMethodCodeProvider<PC,ExceptionHandler> Members
public Result Decode<Visitor, Data, Result> (PC pc, Visitor visitor, Data data)
where Visitor : IAggregateVisitor<PC, Data, Result>
{
Node nested;
Node node = Decode (pc, out nested);
if (IsAtomicNested (nested))
node = nested;
else if (nested != null)
return visitor.Aggregate (pc, new PC (nested), nested is Block, data);
if (node == null)
return visitor.Nop (pc, data);
switch (node.NodeType) {
case NodeType.Block:
case NodeType.Nop:
return visitor.Nop (pc, data);
case NodeType.Clt:
case NodeType.Lt:
return visitor.Binary (pc, BinaryOperator.Clt, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Cgt:
case NodeType.Gt:
return visitor.Binary (pc, BinaryOperator.Cgt, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Ceq:
case NodeType.Eq:
return visitor.Binary (pc, BinaryOperator.Ceq, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Ne:
return visitor.Binary (pc, BinaryOperator.Cne_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Ge:
return visitor.Binary (pc, BinaryOperator.Cge, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Le:
return visitor.Binary (pc, BinaryOperator.Cle, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Add:
return visitor.Binary (pc, BinaryOperator.Add, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Sub:
return visitor.Binary (pc, BinaryOperator.Sub, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Rem:
return visitor.Binary (pc, BinaryOperator.Rem, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Rem_Un:
return visitor.Binary (pc, BinaryOperator.Rem_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Mul:
return visitor.Binary (pc, BinaryOperator.Mul, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Div:
return visitor.Binary (pc, BinaryOperator.Div, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Div_Un:
return visitor.Binary (pc, BinaryOperator.Div_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.And:
return visitor.Binary (pc, BinaryOperator.And, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Or:
return visitor.Binary (pc, BinaryOperator.Or, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Shr:
return visitor.Binary (pc, BinaryOperator.Shr, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Xor:
return visitor.Binary (pc, BinaryOperator.Xor, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Shl:
return visitor.Binary (pc, BinaryOperator.Shl, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Shr_Un:
return visitor.Binary (pc, BinaryOperator.Shr_Un, Dummy.Value, Dummy.Value, Dummy.Value, data);
case NodeType.Literal:
var literal = (Literal) node;
if (literal.Value == null)
return visitor.LoadNull (pc, Dummy.Value, data);
if (literal.Type == CoreSystemTypes.Instance.TypeBoolean && (bool) literal.Value)
return visitor.LoadConst (pc, CoreSystemTypes.Instance.TypeInt32, 1, Dummy.Value, data);
return visitor.LoadConst (pc, literal.Type, literal.Value, Dummy.Value, data);
case NodeType.This:
case NodeType.Parameter:
return visitor.LoadArg (pc, (Parameter) node, false, Dummy.Value, data);
case NodeType.Local:
return visitor.LoadLocal (pc, (Local) node, Dummy.Value, data);
case NodeType.Branch:
var branch = (Branch) node;
if (branch.Condition != null)
return visitor.BranchTrue (pc, new PC (branch.Target), Dummy.Value, data);
return visitor.Branch (pc, new PC (branch.Target), branch.LeavesExceptionBlock, data);
case NodeType.ExpressionStatement:
break;
case NodeType.Box:
break;
case NodeType.Return:
return visitor.Return (pc, Dummy.Value, data);
case NodeType.Neg:
return visitor.Unary (pc, UnaryOperator.Neg, false, Dummy.Value, Dummy.Value, data);
case NodeType.Not:
case NodeType.LogicalNot:
return visitor.Unary (pc, UnaryOperator.Not, false, Dummy.Value, Dummy.Value, data);
case NodeType.Conv:
break;
case NodeType.Conv_I1:
return visitor.Unary (pc, UnaryOperator.Conv_i1, false, Dummy.Value, Dummy.Value, data);
case NodeType.Conv_I2:
return visitor.Unary (pc, UnaryOperator.Conv_i2, false, Dummy.Value, Dummy.Value, data);
case NodeType.Conv_I4:
return visitor.Unary (pc, UnaryOperator.Conv_i4, false, Dummy.Value, Dummy.Value, data);
case NodeType.Conv_I8:
return visitor.Unary (pc, UnaryOperator.Conv_i8, false, Dummy.Value, Dummy.Value, data);
case NodeType.Conv_R4:
return visitor.Unary (pc, UnaryOperator.Conv_r4, false, Dummy.Value, Dummy.Value, data);
case NodeType.Conv_R8:
return visitor.Unary (pc, UnaryOperator.Conv_r8, false, Dummy.Value, Dummy.Value, data);
case NodeType.MethodContract:
return visitor.Nop (pc, data);
case NodeType.Requires:
return visitor.Assume (pc, EdgeTag.Requires, Dummy.Value, data);
case NodeType.Call:
var call = (MethodCall) node;
Method method = GetMethodFrom (call.Callee);
if (method.HasGenericParameters)
throw new NotImplementedException ();
if (method.Name != null && method.DeclaringType.Name != null && method.DeclaringType.Name.EndsWith ("Contract")) {
switch (method.Name) {
case "Assume":
if (method.Parameters.Count == 1)
return visitor.Assume (pc, EdgeTag.Assume, Dummy.Value, data);
break;
case "Assert":
if (method.Parameters.Count == 1)
return visitor.Assert (pc, EdgeTag.Assert, Dummy.Value, data);
break;
}
}
Indexable<Dummy> parameters = DummyIndexable (method);
return visitor.Call (pc, method, false, GetVarargs (call, method), Dummy.Value, parameters, data);
case NodeType.AssignmentStatement:
var assign = ((AssignmentStatement) node);
var local = assign.Target as Local;
if (local != null)
return visitor.StoreLocal (pc, local, Dummy.Value, data);
var parameter = assign.Target as Parameter;
if (parameter != null)
return visitor.StoreArg (pc, parameter, Dummy.Value, data);
var binding = assign.Target as MemberBinding;
if (binding != null) {
if (binding.BoundMember.IsStatic)
return visitor.StoreStaticField (pc, (Field) binding.BoundMember, Dummy.Value, data);
else
return visitor.StoreField (pc, (Field) binding.BoundMember, Dummy.Value, Dummy.Value, data);
}
throw new NotImplementedException ();
case NodeType.Construct:
Method ctor = GetMethodFrom (((Construct) node).Constructor);
if (!(ctor.DeclaringType is ArrayTypeNode))
return visitor.NewObj (pc, ctor, Dummy.Value, DummyIndexable (ctor), data);
var arrayType = (ArrayTypeNode) ctor.DeclaringType;
return visitor.NewArray (pc, arrayType, Dummy.Value, DummyIndexable (ctor), data);
default:
return visitor.Nop (pc, data);
}
throw new NotImplementedException ();
}
public bool Next (PC pc, out PC nextLabel)
{
Node nested;
if (Decode (pc, out nested) == null && pc.Node != null) {
nextLabel = new PC (pc.Node, pc.Index + 1);
return true;
}
nextLabel = new PC ();
return false;
}
public int GetILOffset (PC current)
{
throw new NotImplementedException ();
}
#endregion
private static Indexable<Dummy> DummyIndexable (Method method)
{
return new Indexable<Dummy> (Enumerable.Range (0, method.Parameters.Count).Select (it => Dummy.Value).ToList ());
}
private static Indexable<TypeNode> GetVarargs (MethodCall call, Method method)
{
int methodCount = method.Parameters.Count;
int callCount = call.Arguments.Count;
if (callCount <= methodCount)
return new Indexable<TypeNode> (null);
var array = new TypeNode[callCount - methodCount];
for (int i = methodCount; i < callCount; i++)
array [i - methodCount] = call.Arguments [i - methodCount].Type;
return new Indexable<TypeNode> (array);
}
private Method GetMethodFrom (Expression callee)
{
return (Method) ((MemberBinding) callee).BoundMember;
}
private static bool IsAtomicNested (Node nested)
{
if (nested == null)
return false;
switch (nested.NodeType) {
case NodeType.Local:
case NodeType.Parameter:
case NodeType.Literal:
case NodeType.This:
return true;
default:
return false;
}
}
private Node Decode (PC pc, out Node nested)
{
Node node = DecodeInflate (pc, out nested);
return node;
}
/// <summary>
/// Decodes pc
/// </summary>
/// <param name="pc"></param>
/// <param name="nested"></param>
/// <returns>If node has nested, returns null and (nested = child). If last child given, node equals pc.Node</returns>
private static Node DecodeInflate (PC pc, out Node nested)
{
Node node = pc.Node;
if (node == null) {
nested = null;
return null;
}
int index = pc.Index;
switch (node.NodeType) {
case NodeType.MethodContract:
var methodContract = (MethodContract) node;
if (index < methodContract.RequiresCount) {
nested = methodContract.Requires [index];
return null;
}
if (index == methodContract.RequiresCount) {
nested = null;
return methodContract;
}
//todo: aggregate ensures
nested = null;
return methodContract;
case NodeType.Requires:
var requires = (Requires) node;
if (index == 0) {
nested = requires.Assertion;
return null;
}
nested = null;
return requires;
case NodeType.Block:
var block = (Block) node;
if (block.Statements == null) {
nested = null;
return block;
}
nested = index >= block.Statements.Count ? null : block.Statements [index];
return index + 1 < block.Statements.Count ? null : block;
case NodeType.Return:
var ret = (Return) node;
if (ret.Expression != null && index == 0) {
nested = ret.Expression;
return null;
}
nested = null;
return ret;
case NodeType.AssignmentStatement:
var assign = (AssignmentStatement) node;
int innerIndex = index;
{
var bind = assign.Target as MemberBinding;
if (bind != null) {
++innerIndex;
if (bind.BoundMember.IsStatic)
++innerIndex;
if (innerIndex == 1) {
nested = bind.TargetObject;
return null;
}
} else if (assign.Target is Variable)
innerIndex += 2;
else {
nested = null;
return assign;
}
}
if (innerIndex == 2) {
nested = assign.Source;
return null;
}
nested = null;
return assign;
case NodeType.ExpressionStatement:
var expressionStatement = (ExpressionStatement) node;
nested = expressionStatement.Expression;
return expressionStatement;
case NodeType.MethodCall:
case NodeType.Call:
case NodeType.Calli:
case NodeType.CallVirt:
var methodCall = (MethodCall) node;
var binding = (MemberBinding) methodCall.Callee;
if (binding.BoundMember.IsStatic) {
if (index < methodCall.Arguments.Count) {
nested = methodCall.Arguments [index];
return null;
}
nested = null;
return methodCall;
}
if (index == 0) {
nested = binding.TargetObject;
return null;
}
if (index < methodCall.Arguments.Count + 1) {
nested = methodCall.Arguments [index - 1];
return null;
}
nested = null;
return methodCall;
case NodeType.MemberBinding:
var bind1 = ((MemberBinding) node);
if (index == 0 && !bind1.BoundMember.IsStatic) {
nested = bind1.TargetObject;
return null;
}
nested = null;
return bind1;
case NodeType.Construct:
var construct = (Construct) node;
if (index < construct.Arguments.Count) {
nested = construct.Arguments [index];
return null;
}
nested = null;
return construct;
case NodeType.Branch:
var branch = ((Branch) node);
if (branch.Condition != null && index == 0) {
nested = branch.Condition;
return null;
}
nested = null;
return branch;
default:
var binary = node as BinaryExpression;
if (binary != null) {
if (index == 0) {
nested = binary.Left;
return null;
}
if (index == 1) {
nested = binary.Right;
return null;
}
nested = null;
return binary;
}
var unary = node as UnaryExpression;
if (unary != null) {
if (index == 0) {
nested = unary.Operand;
return null;
}
nested = null;
return unary;
}
//todo: ternary
nested = null;
return node;
}
}
public PC Entry (Method method)
{
return new PC (method.Body);
}
#region Implementation of IMethodCodeProvider<PC,Local,Parameter,Method,FieldReference,TypeReference,Dummy>
public bool IsFaultHandler (ExceptionHandler handler)
{
return handler.HandlerType == NodeType.FaultHandler;
}
public bool IsFilterHandler (ExceptionHandler handler)
{
return handler.HandlerType == NodeType.Filter;
}
public bool IsFinallyHandler (ExceptionHandler handler)
{
return handler.HandlerType == NodeType.Finally;
}
public PC FilterExpressionStart (ExceptionHandler handler)
{
return new PC (handler.FilterExpression);
}
public PC HandlerEnd (ExceptionHandler handler)
{
throw new NotImplementedException ();
}
public PC HandlerStart (ExceptionHandler handler)
{
throw new NotImplementedException ();
}
public PC TryStart (ExceptionHandler handler)
{
throw new NotImplementedException ();
}
public PC TryEnd (ExceptionHandler handler)
{
throw new NotImplementedException ();
}
public bool IsCatchHandler (ExceptionHandler handler)
{
return handler.HandlerType == NodeType.Catch;
}
public TypeNode CatchType (ExceptionHandler handler)
{
return handler.FilterType;
}
public bool IsCatchAllHandler (ExceptionHandler handler)
{
if (!IsCatchHandler (handler))
return false;
if (handler.FilterType != null)
return false;
return true;
}
public IEnumerable<ExceptionHandler> GetTryBlocks (Method method)
{
yield break;
}
#endregion
#region Nested type: PC
public struct PC : IEquatable<PC> {
public readonly int Index;
public readonly Node Node;
public PC (Node Node)
: this (Node, 0)
{
}
public PC (Node node, int index)
{
this.Node = node;
this.Index = index;
}
#region IEquatable<PC> Members
public bool Equals (PC other)
{
return Equals (other.Node, this.Node) && other.Index == this.Index;
}
#endregion
public override bool Equals (object obj)
{
if (ReferenceEquals (null, obj))
return false;
if (obj.GetType () != typeof (PC))
return false;
return Equals ((PC) obj);
}
public override int GetHashCode ()
{
unchecked {
return ((this.Node != null ? this.Node.GetHashCode () : 0)*397) ^ this.Index;
}
}
}
#endregion
}
}