// // Analysis.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.IO; using System.Linq; using Mono.CodeContracts.Static.AST; using Mono.CodeContracts.Static.AST.Visitors; using Mono.CodeContracts.Static.Analysis.Drivers; using Mono.CodeContracts.Static.ControlFlow; using Mono.CodeContracts.Static.DataFlowAnalysis; using Mono.CodeContracts.Static.DataStructures; using Mono.CodeContracts.Static.Lattices; using Mono.CodeContracts.Static.Providers; using Mono.CodeContracts.Static.Proving; namespace Mono.CodeContracts.Static.Analysis.NonNull { class Analysis : ILVisitorBase, NonNullDomain>, IAnalysis, IILVisitor, NonNullDomain>, IImmutableMap>>, IMethodResult, IFactBase where E : IEquatable where V : IEquatable { private readonly Dictionary> callSiteCache = new Dictionary> (); private readonly IMethodDriver method_driver; private IFixPointInfo> fix_point_info; public Analysis (IMethodDriver mdriver) { this.method_driver = mdriver; } protected internal IExpressionContextProvider ContextProvider { get { return this.method_driver.ContextProvider; } } protected IMetaDataProvider MetaDataProvider { get { return this.method_driver.MetaDataProvider; } } #region IAnalysis,IILVisitor,Domain>,IImmutableMap>> Members public IILVisitor, NonNullDomain> GetVisitor () { return this; } public NonNullDomain Join (Pair edge, NonNullDomain newstate, NonNullDomain prevstate, out bool weaker, bool widen) { bool nonNullWeaker; SetDomain nonNulls = prevstate.NonNulls.Join (newstate.NonNulls, widen, out nonNullWeaker); bool nullWeaker; SetDomain nulls = prevstate.Nulls.Join (newstate.Nulls, widen, out nullWeaker); weaker = nonNullWeaker || nullWeaker; return new NonNullDomain (nonNulls, nulls); } public NonNullDomain ImmutableVersion (NonNullDomain state) { return state; } public NonNullDomain MutableVersion (NonNullDomain state) { return state; } public NonNullDomain EdgeConversion (APC from, APC to, bool isJoinPoint, IImmutableMap> data, NonNullDomain state) { if (data == null) return state; SetDomain oldNonNulls = state.NonNulls; SetDomain nonNulls = SetDomain.TopValue; SetDomain oldNulls = state.Nulls; SetDomain nulls = SetDomain.TopValue; foreach (V variable in data.Keys) { bool nonNullContains = oldNonNulls.Contains (variable); bool nullContains = oldNulls.Contains (variable); if (nonNullContains || nullContains) { foreach (V anotherVariable in data [variable].AsEnumerable ()) { if (nonNullContains) nonNulls = nonNulls.With (anotherVariable); if (nullContains) nulls = nulls.With (anotherVariable); } } } return new NonNullDomain (nonNulls, nulls); } public bool IsBottom (APC pc, NonNullDomain state) { return state.NonNulls.IsBottom; } public Predicate SaveFixPointInfo (IFixPointInfo> fixPointInfo) { this.fix_point_info = fixPointInfo; //todo: implement this return pc => true; } public void Dump (Pair, TextWriter> pair) { TextWriter tw = pair.Value; tw.Write ("NonNulls: "); pair.Key.NonNulls.Dump (tw); tw.Write ("Nulls: "); pair.Key.Nulls.Dump (tw); } #endregion #region IFactBase Members public FlatDomain IsNull(APC pc, V variable) { if (ContextProvider.ValueContext.IsZero (pc, variable)) return ProofOutcome.True; NonNullDomain domain; if (!PreStateLookup (pc, out domain) || domain.NonNulls.IsBottom) return ProofOutcome.Bottom; if (domain.IsNonNull (variable)) return ProofOutcome.False; if (domain.IsNull (variable)) return ProofOutcome.True; return ProofOutcome.Top; } public FlatDomain IsNonNull(APC pc, V variable) { NonNullDomain domain; if (!PreStateLookup (pc, out domain) || domain.NonNulls.IsBottom) return ProofOutcome.Bottom; if (domain.IsNonNull (variable)) return ProofOutcome.True; if (ContextProvider.ValueContext.IsZero (pc, variable) || domain.IsNull (variable)) return ProofOutcome.False; FlatDomain aType = ContextProvider.ValueContext.GetType (pc, variable); if (aType.IsNormal() && MetaDataProvider.IsManagedPointer (aType.Value)) return ProofOutcome.True; return ProofOutcome.Top; } public bool IsUnreachable (APC pc) { NonNullDomain domain; if (!PreStateLookup (pc, out domain) || domain.NonNulls.IsBottom) return true; return false; } #endregion public override NonNullDomain DefaultVisit (APC pc, NonNullDomain data) { return data; } public static NonNullDomain AssumeNonNull (V dest, NonNullDomain domain) { if (!domain.NonNulls.Contains (dest)) return new NonNullDomain (domain.NonNulls.With (dest), domain.Nulls); return domain; } public static NonNullDomain AssumeNull (V dest, NonNullDomain before) { if (!before.Nulls.Contains (dest)) return new NonNullDomain (before.NonNulls, before.Nulls.With (dest)); return before; } public override NonNullDomain Assert(APC pc, EdgeTag tag, V condition, NonNullDomain data) { return ContextProvider.ExpressionContext.Decode >, NonNullDomain, ExpressionAssumeDecoder> ( ContextProvider.ExpressionContext.Refine (pc, condition), new ExpressionAssumeDecoder (ContextProvider), new Pair> (true, data)); } public override NonNullDomain Assume (APC pc, EdgeTag tag, V condition, NonNullDomain data) { IExpressionContext exprCtx = ContextProvider.ExpressionContext; E expr = exprCtx.Refine (pc, condition); return exprCtx.Decode>, NonNullDomain, ExpressionAssumeDecoder> (expr, new ExpressionAssumeDecoder (ContextProvider), new Pair> (tag != EdgeTag.False, data)); } public override NonNullDomain Unary (APC pc, UnaryOperator op, bool unsigned, V dest, V source, NonNullDomain data) { switch (op) { case UnaryOperator.Conv_i: case UnaryOperator.Conv_u: if (data.IsNonNull (source)) return AssumeNonNull (dest, data); break; } return data; } public override NonNullDomain Call (APC pc, Method method, bool virt, TypeList extraVarargs, V dest, ArgList args, NonNullDomain data) { this.callSiteCache [pc] = data; if (!MetaDataProvider.IsStatic (method)) return AssumeNonNull (args [0], data); return data; } public override NonNullDomain CastClass (APC pc, TypeNode type, V dest, V obj, NonNullDomain data) { if (data.NonNulls.Contains (obj)) return AssumeNonNull (dest, data); return data; } public override NonNullDomain Entry (APC pc, Method method, NonNullDomain data) { APC at = ContextProvider.MethodContext.CFG.Next (pc); NonNullDomain domain = data; IIndexable parameters = MetaDataProvider.Parameters (method); TypeNode eventArgsType; bool systemType = MetaDataProvider.TryGetSystemType ("System.EventArgs", out eventArgsType); for (int i = 0; i < parameters.Count; i++) { Parameter p = parameters [i]; TypeNode pType = MetaDataProvider.ParameterType (p); if (MetaDataProvider.IsManagedPointer (pType)) { V sv; if (ContextProvider.ValueContext.TryParameterValue (at, p, out sv)) domain = AssumeNonNull (sv, domain); } else { V sv; if (i == 0 && parameters.Count == 1 && MetaDataProvider.IsArray (pType) && MetaDataProvider.Name (method) == "Main" && MetaDataProvider.IsStatic (method) && ContextProvider.ValueContext.TryParameterValue (pc, p, out sv)) domain = AssumeNonNull (sv, domain); } } V sv1; if (systemType && parameters.Count == 2 && MetaDataProvider.Equal (MetaDataProvider.System_Object, MetaDataProvider.ParameterType (parameters [0])) && MetaDataProvider.DerivesFrom (MetaDataProvider.ParameterType (parameters [1]), eventArgsType) && ContextProvider.ValueContext.TryParameterValue (pc, parameters [1], out sv1)) domain = AssumeNonNull (sv1, domain); if (!MetaDataProvider.IsStatic (method) && ContextProvider.ValueContext.TryParameterValue (pc, MetaDataProvider.This (method), out sv1)) domain = AssumeNonNull (sv1, domain); return domain; } public override NonNullDomain LoadStack (APC pc, int offset, V dest, V source, bool isOld, NonNullDomain data) { NonNullDomain old; if (isOld && TryFindOldState (pc, out old)) { if (old.IsNonNull (source)) return AssumeNonNull (dest, data); if (old.IsNull (source)) return AssumeNull (dest, data); } return data; } public override NonNullDomain Isinst (APC pc, TypeNode type, V dest, V obj, NonNullDomain data) { if (data.IsNonNull (obj)) { FlatDomain aType = ContextProvider.ValueContext.GetType (pc, obj); if (aType.IsNormal() && MetaDataProvider.DerivesFrom (aType.Value, type)) return AssumeNonNull (dest, data); } return data; } public override NonNullDomain LoadArgAddress (APC pc, Parameter argument, bool isOld, V dest, NonNullDomain data) { return AssumeNonNull (dest, data); } public override NonNullDomain LoadConst (APC pc, TypeNode type, object constant, V dest, NonNullDomain data) { if (constant is string) return AssumeNonNull (dest, data); return data; } public override NonNullDomain LoadElement (APC pc, TypeNode type, V dest, V array, V index, NonNullDomain data) { return AssumeNonNull (array, data); } public override NonNullDomain LoadField (APC pc, Field field, V dest, V obj, NonNullDomain data) { NonNullDomain domain = AssumeNonNull (obj, data); FlatDomain aType = ContextProvider.ValueContext.GetType (ContextProvider.MethodContext.CFG.Next (pc), dest); if (aType.IsNormal() && MetaDataProvider.IsManagedPointer (aType.Value)) domain = AssumeNonNull (dest, domain); return domain; } public override NonNullDomain LoadFieldAddress (APC pc, Field field, V dest, V obj, NonNullDomain data) { NonNullDomain domain = AssumeNonNull (obj, data); return AssumeNonNull (dest, domain); } public override NonNullDomain LoadStaticFieldAddress (APC pc, Field field, V dest, NonNullDomain data) { return AssumeNonNull (dest, data); } public override NonNullDomain LoadLength (APC pc, V dest, V array, NonNullDomain data) { return AssumeNonNull (array, data); } public override NonNullDomain NewArray (APC pc, TypeNode type, V dest, ArgList lengths, NonNullDomain data) { return AssumeNonNull (dest, data); } public override NonNullDomain NewObj (APC pc, Method ctor, V dest, ArgList args, NonNullDomain data) { return AssumeNonNull (dest, data); } public override NonNullDomain StoreElement (APC pc, TypeNode type, V array, V index, V value, NonNullDomain data) { return AssumeNonNull (array, data); } public override NonNullDomain StoreField (APC pc, Field field, V obj, V value, NonNullDomain data) { return AssumeNonNull (obj, data); } private bool TryFindOldState (APC pc, out NonNullDomain old) { if (pc.SubroutineContext.AsEnumerable().Any (edge => edge.Tag.Is (EdgeTag.AfterMask))) return this.callSiteCache.TryGetValue (pc, out old); return false.Without (out old); } public NonNullDomain InitialValue (Func keyConverter) { return new NonNullDomain (new SetDomain (keyConverter), new SetDomain (keyConverter)); } #region Implementation of IMethodResult public IMethodAnalysis MethodAnalysis { get; set; } public void ValidateImplicitAssertions (IFactQuery facts, List proofResults) { } public IFactQuery FactQuery { get { return new SimpleLogicInference (ContextProvider, this, this.method_driver.BasicFacts.IsUnreachable); } } public FlatDomain ValidateExplicitAssertion(APC pc, V value) { NonNullDomain domain; if (PreStateLookup (pc, out domain) && !domain.NonNulls.IsBottom) { IExpressionContext exprCtx = ContextProvider.ExpressionContext; return exprCtx.Decode, ExpressionAssertDischarger>(exprCtx.Refine(pc, value), new ExpressionAssertDischarger(this, pc), true); } return ProofOutcome.Bottom; } private bool PreStateLookup (APC pc, out NonNullDomain domain) { return this.fix_point_info.PreStateLookup (pc, out domain); } #endregion } }