/// <summary> /// Refines the given state according to the knowledge stored in the egraph about sv /// /// In addition, the state can be null when the knowledge is inconsistent. /// </summary> /// <param name="cv">symbolic value we assume to be null (false)</param> private static void AssumeFalse(ISymValue cv, ref NonNullState state) { if (state == null) return; if (state.IsNonNull(cv)) { // infeasible // but we still want to go on analyzing this branch. state = null; return; } if (state.IsNull(cv)) return; foreach(EGraphTerm t in state.egraph.EqTerms(cv)) { if (t.Function == NonNullState.EqNullId) { // EqNull(op) == false, therefore op != null ISymValue op = t.Args[0]; AssumeTrue(op, ref state); } if (t.Function == NonNullState.NeNullId) { // NeNull(op) == false, therefore op == null ISymValue op = t.Args[0]; AssumeFalse(op, ref state); } if (t.Function == NonNullState.LogicalNegId) { // Not(op) == false, therefore op == true ISymValue op = t.Args[0]; AssumeTrue(op, ref state); } if (t.Function == NonNullState.IsInstId) { // IsInst(op) == null, cannot deduce anything about op } } // needs to be after we check EqTerms, as they verify mapping is current. if (state != null) state.AssumeNull(cv); }
/// <summary> /// Refines the given state according to the knowledge stored in the egraph about sv /// /// In addition, the state can be null when the knowledge is inconsistent. /// </summary> /// <param name="cv">symbolic value we assume to be non-null (true)</param> /// <param name="state">state if sv is non-null (true)</param> private static void AssumeTrue(ISymValue cv, ref NonNullState state) { if (state == null) return; if (state.IsNull(cv)) { // infeasible state = null; return; } if (state.IsNonNull(cv)) return; state.egraph[cv] = Lattice.AVal.NonNull; foreach(EGraphTerm t in state.egraph.EqTerms(cv)) { if (t.Function == NonNullState.EqNullId) { ISymValue op = t.Args[0]; AssumeFalse(op, ref state); } if (t.Function == NonNullState.NeNullId) { ISymValue op = t.Args[0]; AssumeTrue(op, ref state); } if (t.Function == NonNullState.LogicalNegId) { ISymValue op = t.Args[0]; AssumeFalse(op, ref state); } if (t.Function == NonNullState.IsInstId) { ISymValue op = t.Args[0]; AssumeTrue(op, ref state); } } }
/// <summary> /// Returns null, if result of Join is the same as atMerge. /// </summary> public static NonNullState Join(NonNullState atMerge, NonNullState incoming, CfgBlock joinPoint) { bool unchanged; IEGraph merged = atMerge.egraph.Join(incoming.egraph, joinPoint, out unchanged); TypeNode currentException = (atMerge.currentException != null)? ((incoming.currentException != null)? CciHelper.LeastCommonAncestor(atMerge.currentException, incoming.currentException) : null) : null; if (atMerge.currentException != currentException || !unchanged) { return new NonNullState(merged, currentException, atMerge.typeSystem); } return null; }
public void RefineBranchInformation(Variable cond, out NonNullState trueState, out NonNullState falseState) { ISymValue cv = Value(cond); trueState = new NonNullState(this); falseState = new NonNullState(this); AssumeTrue(cv, ref trueState); AssumeFalse(cv, ref falseState); }
public NonNullState(NonNullState old){ this.typeSystem = old.typeSystem; this.egraph = (IEGraph)old.egraph.Clone(); this.currentException = old.currentException; // this.existDelayInfo = old.ExistDelayInfo; }
/// <summary> /// Enforcement of once attributes: Check if the field is written more than one /// and also allow to write it if it was null before /// If required a to register the field mutated at the class level (FieldUsage in Analyzer) /// </summary> /// <param name="dest"></param> /// <param name="field"></param> /// <param name="stat"></param> /// <param name="nn"></param> private void CheckFieldOnceness(Variable dest, Field field, Statement stat, NonNullState nn) { IMutableSet fieldUsage = this.NNChecker.analyzer.FieldUsage; if (field.IsOnce && fieldUsage != null) { if (!fieldUsage.Contains(field)) { fieldUsage.Add(field); if (!nn.IsFieldNull(this.NNChecker, dest, field) && !this.ts.IsNullableType(field.Type)) { //hack // Need to add a new type of error HandleError(stat, stat, Error.GenericWarning, "[Once] fields can be written only if null"); } } else { // Need to add a new type of error HandleError(stat, stat, Error.GenericWarning, "[Once] fields can be written only once."); } } }
private NonNullState PushNullException(NonNullState nn) { NonNullState exnState = nn; nn = new NonNullState(nn); exnState.currentException=SystemTypes.NullReferenceException; NNChecker.PushExceptionState(NNChecker.currBlock, exnState); return nn; }
protected override object VisitCall(Variable dest, Variable receiver, Method callee, ExpressionList arguments, bool virtcall, Statement stat, object arg) { NonNullState nn=(NonNullState)arg; bool resultIsNonNull = false; // Check for Receiver if (!callee.IsStatic) CheckReceiver(stat,receiver,nn); // Check for parameter matching. if (arguments!=null && callee.Parameters != null){ for(int i=0;i<callee.Parameters.Count;i++){ Variable actual = (Variable)arguments[i]; if (ts.IsNonNullType(callee.GetParameterTypes()[i])) { if(nn.IsNull(actual)) { HandleError(stat, actual, Error.CannotCoerceNullToNonNullType); } else if(!nn.IsNonNull(actual)) { //System.Console.WriteLine("visit call, argument: {0}", actual); HandleError(stat, actual, Error.CoercionToNonNullTypeMightFail, callee.GetParameterTypes()[i]); } } nn.HavocIndirect(actual, callee.Parameters[i].Type as Reference); } } // special case some methods if (this.IsAssertionMethodThatDoesNotReturn(callee)) { // we assume that all assertion methods are called with false. return null; } else if (this.NNChecker.IsAssertNotNullImplicitMethod(callee)) { if (arguments.Count == 1){ Variable source = (Variable)arguments[0]; // guaranteed by CodeFlattener // compiler inserts this test throughout, so let's warn here. if (nn.IsNonNull(source)) { // opportunity to optimize away check. RecordUnnecessaryCheck(stat); // Console.WriteLine("Could optimize IsNonNull check at line {0}", stat.SourceContext.StartLine); } else { RecordNecessaryCheck(stat); if (nn.IsNull(source)) { HandleError(stat, source, Error.CannotCoerceNullToNonNullType); // do not explore this path further, except for exceptional path PushNullException(nn); return null; } else { //System.Console.WriteLine("visit call and callee is assertnotnullimplicitmethod"); HandleError(stat, source, Error.CoercionToNonNullTypeMightFail, OptionalModifier.For(SystemTypes.NonNullType, source.Type)); } } nn = PushNullException(nn); nn.AssumeNonNull(source); } } else if (NNChecker.IsAssertNotNullMethod(callee)) { if (arguments.Count == 1){ Variable source = (Variable)arguments[0]; // guaranteed by CodeFlattener // User inserted cast, so let's warn if it is unnecessary. if (nn.IsNonNull(source)) { // Let user know his check is useless here. RecordUnnecessaryCheck(stat); // Console.WriteLine("Could optimize IsNonNull check at line {0}", stat.SourceContext.StartLine); } else { RecordNecessaryCheck(stat); if (nn.IsNull(source)) { // Error already emitted at checker time. HandleError(stat, source, Error.CannotCoerceNullToNonNullType); // do not explore this path further except for exceptional path PushNullException(nn); return null; } } nn = PushNullException(nn); nn.AssumeNonNull(source); } } else if ( callee.Name.Name == "GetEnumerator" ) { // special case assume result is non-null because it is in generated code and confuses. if (dest != null) { nn.AssignNonNull(dest); dest = null; // avoid setting dest again below. } } else if (callee == GetTypeFromHandleMethod) { // special case that should be handled by annotating mscorlib if (dest != null) { nn.AssignNonNull(dest); dest = null; // avoid setting dest again below } } else { Property pget = IsPropertyGetter(callee); if (pget != null) { resultIsNonNull = nn.LoadProperty(receiver, pget, dest); dest = null; // prevent remaining code to overwrite dest } else { Property pset = IsPropertySetter(callee); if (pset != null) { nn.StoreProperty(receiver, pset, (Variable)arguments[0]); } else if (!callee.IsPure) { // Non-pure method: assume it destroy all heap patterns. nn.HavocHeap(); } } } // Push possible exceptions to handlers. if (callee.Contract != null) { for (int i = 0; i < callee.Contract.Ensures.Count; i++) { EnsuresExceptional e = callee.Contract.Ensures[i] as EnsuresExceptional; if (e != null) { NonNullState exnState = nn; nn = new NonNullState(nn); exnState.currentException = e.Type; NNChecker.PushExceptionState(NNChecker.currBlock, exnState); } } } // Return type matching. if(dest!=null){ if (nn.IsNonNullType(dest.Type) && ! (resultIsNonNull || nn.IsNonNullType(callee.ReturnType))) { if (!(callee.DeclaringMember is Property)) HandleError(stat, stat, Error.CoercionToNonNullTypeMightFail,dest.Type); nn.AssignNonNull(dest); } else { nn.AssignAccordingToType(dest, callee.ReturnType); } } return nn; }
/// <summary> /// Note: casts don't require a non-null argument. null value casts always succeed. /// </summary> protected override object VisitCastClass(Variable dest, TypeNode type, Variable source, Statement stat, object arg) { NonNullState nn = (NonNullState)arg; if ( ! nn.IsNull(source)) { NonNullState exnState = nn; nn = new NonNullState(nn); // make copy of current state to continue modify exnState.currentException=SystemTypes.InvalidCastException; NNChecker.PushExceptionState(NNChecker.currBlock, exnState); } // acts like a copy retaining null status nn.CopyVariable(source, dest); if(nn.IsNonNullType(dest.Type) && ! nn.IsNonNull(source)) { if(nn.IsNull(source)) HandleError(stat, source, Error.CannotCoerceNullToNonNullType); else { //System.Console.WriteLine("visit cast class: dest:{0}, source:{1}", dest, source); HandleError(stat, source, Error.CoercionToNonNullTypeMightFail,dest.Type); }; // mask future errors nn.AssignNonNull(dest); } return nn; }
private void CheckPointerUse(Statement stat, Variable v, NonNullState nn, string purpose) { if (v == null) return; if(nn.IsNull(v)) { HandleError(stat, v, Error.UseOfNullPointer, purpose); nn.AssignNonNull(v); } else if(!nn.IsNonNull(v)) { HandleError(stat, v, Error.UseOfPossiblyNullPointer, purpose); nn.AssumeNonNull(v); } }
private void CheckReceiver(Statement stat, Variable v, NonNullState nn, Node node) { Node offendingNode = v; if (v == null) return; if (v.Type.IsValueType) return; // Create a better source context for receiver null errors. offendingNode = new Statement(NodeType.Nop); offendingNode.SourceContext = v.SourceContext; // offendingNode.SourceContext.StartPos = offendingNode.SourceContext.EndPos; // offendingNode.SourceContext.EndPos++; if(nn.IsNull(v)) { HandleError(stat, offendingNode, Error.ReceiverCannotBeNull, this.ts.GetTypeName(v.Type)); nn.AssignNonNull(v); } else if(!nn.IsNonNull(v)) { HandleError(stat, offendingNode, Error.ReceiverMightBeNull, this.ts.GetTypeName(v.Type)); nn.AssumeNonNull(v); } }
/// <summary> /// It split exceptions for current handler and the next chained handler. /// /// It will: /// /// If the exception is completely intercepted by current handler, the /// exception will be consumed. /// /// If the exception caught but not completely, both current handler and /// the next handler will take the states. /// /// If the exception is irrelevant to current caught, the next handler /// will take over the state. Current handler is then bypassed. /// </summary> /// <param name="handler"></param> /// <param name="currentHandlerState"></param> /// <param name="nextHandlerState"></param> protected override void SplitExceptions(CfgBlock handler, ref IDataFlowState currentHandlerState, out IDataFlowState nextHandlerState) { Debug.Assert(currentHandlerState!=null,"Internal error in NonNull Analysis"); NonNullState state = (NonNullState)currentHandlerState; if(handler==null || handler.Length==0){ nextHandlerState=null; return; } if(handler[0] is Unwind){ nextHandlerState=null; currentHandlerState=null; return; } Debug.Assert(handler[0] is Catch, "Exception Handler does not starts with Catch"); Debug.Assert(state.currentException!=null,"No current exception to handle"); Catch c=(Catch)handler[0]; if(handler.ExceptionHandler!=null && !state.currentException.IsAssignableTo(c.Type)) { nextHandlerState = state;; } else { nextHandlerState=null; } // Compute what trickles through to the next handler // and what sticks to this handler. if(state.currentException.IsAssignableTo(c.Type)) { // everything sticks nextHandlerState = null; } else if (c.Type.IsAssignableTo(state.currentException)) { // c sticks, rest goes to next handler nextHandlerState = state; // copy state to modify the currentException state = new NonNullState(state); state.currentException = c.Type; currentHandlerState = state; }else { // nothing stick all goes to next handler nextHandlerState = state; currentHandlerState = null; } return; }
/// <summary> /// Implementation of visit Block. It is called from run. /// /// It calls VisitStatement. /// </summary> /// <param name="block"></param> /// <param name="stateOnEntry"></param> /// <returns></returns> protected override IDataFlowState VisitBlock(CfgBlock block, IDataFlowState stateOnEntry) { Debug.Assert(block!=null); currBlock=block; Analyzer.Write("---------block: "+block.UniqueId+";"); Analyzer.Write(" Exit:"); foreach (CfgBlock b in block.NormalSuccessors) Analyzer.Write(b.UniqueId+";"); if (block.UniqueSuccessor!=null) Analyzer.Write(" FallThrough: "+block.UniqueSuccessor+";"); if (block.ExceptionHandler!=null) Analyzer.Write(" ExHandler: "+block.ExceptionHandler.UniqueId+";"); Analyzer.WriteLine(""); NonNullState newState; if(stateOnEntry==null) newState=new NonNullState(typeSystem,null); else newState=new NonNullState((NonNullState)stateOnEntry); return base.VisitBlock (block, newState); }