StatementList AddCheckForNull(StatementList statements, Expression src, TypeNode type) { if (type.Template == SystemTypes.GenericBoxed) { If checknull = new If(new BinaryExpression( new MethodCall(new MemberBinding(src, type.GetMethod(Identifier.For("IsNull"),null)), new ExpressionList()), Literal.True, NodeType.Ne), new Block(new StatementList()), null); statements.Add(checknull); return checknull.TrueBlock.Statements; } else if (type is TypeAlias) { // cast to underlying type and check that for null. TypeNode atype = ((TypeAlias)type).AliasedType; return AddCheckForNull(statements, CastTo(src, atype), atype); } else if (type is ConstrainedType) { // cast to underlying type and check that for null. TypeNode atype = ((ConstrainedType)type).UnderlyingType; return AddCheckForNull(statements, CastTo(src, atype), atype); } else if (!IsStream(type) && !type.IsValueType) { //stream types are doing weird things to the null check? if (type == SystemTypes.String || IsStream(type)) src = CastTo(src, SystemTypes.Object); If checknull = new If(new BinaryExpression(src, Literal.Null, NodeType.Ne), new Block(new StatementList()), null); statements.Add(checknull); return checknull.TrueBlock.Statements; } return statements; }
private void ProcessClosureClass(Method method, TypeNode closure, bool isAsync) { Contract.Requires(method != null); Contract.Requires(closure != null); Method movenext = closure.GetMethod(StandardIds.MoveNext); if (movenext == null) return; movenext.IsAsync = isAsync; if (movenext.Body == null) return; if (movenext.Body.Statements == null) return; SourceContext defaultSourceContext; Block contractInitializerBlock = new Block(new StatementList()); HelperMethods.StackDepthTracker dupStackTracker = new HelperMethods.StackDepthTracker(); AssumeBlock originalContractPosition; StatementList contractClump = GetContractClumpFromMoveNext(method, movenext, contractNodes, contractInitializerBlock.Statements, out defaultSourceContext, ref dupStackTracker, out originalContractPosition); if (contractClump != null) { // Look for bad stuff BadStuff(method, contractClump, defaultSourceContext); // Make sure any locals in the contracts are disjoint from the locals in the rest of the body // can use the same one throughout GatherLocals gatherLocals = new GatherLocals(); // Make sure that the entire contract section is closed. if (!CheckClump(movenext, gatherLocals, currentMethodSourceContext, new Block(contractClump))) { movenext.ClearBody(); return; } // Checking that had the side effect of populating the hashtable, but now each contract will be individually visited. // That process needs to start with a fresh table. gatherLocals.Locals = new TrivialHashtable(); RequiresList Preconditions = new RequiresList(); EnsuresList Postconditions = new EnsuresList(); RequiresList Validations = new RequiresList(); EnsuresList modelPostconditions = new EnsuresList(); EnsuresList asyncPostconditions = null; // REVIEW: What should we do with the Validations in this case? Should we map them to the enumerator method? Maybe not, since without // rewriting this won't happen. if (!ExtractFromClump(contractClump, movenext, gatherLocals, Preconditions, Postconditions, Validations, modelPostconditions, defaultSourceContext, method, contractInitializerBlock, ref dupStackTracker)) { movenext.ClearBody(); return; } if (isAsync) { asyncPostconditions = SplitAsyncEnsures(ref Postconditions, method); } try { // Next is to attach the preconditions to method (instead of movenext) // To do so, we have to duplicate the expressions and statements in Precondition, Postcondition and contractInitializerBlock Duplicator dup = new Duplicator(closure.DeclaringModule, method.DeclaringType); var origPreconditions = Preconditions; var origValidations = Validations; var origcontractInitializerBlock = contractInitializerBlock; Preconditions = dup.VisitRequiresList(Preconditions); Postconditions = dup.VisitEnsuresList(Postconditions); Validations = dup.VisitRequiresList(Validations); contractInitializerBlock = dup.VisitBlock(contractInitializerBlock); asyncPostconditions = dup.VisitEnsuresList(asyncPostconditions); var mapClosureExpToOriginal = BuildMappingFromClosureToOriginal(closure, movenext, method); Preconditions = mapClosureExpToOriginal.Apply(Preconditions); Postconditions = mapClosureExpToOriginal.Apply(Postconditions); Validations = mapClosureExpToOriginal.Apply(Validations); contractInitializerBlock = mapClosureExpToOriginal.Apply(contractInitializerBlock); asyncPostconditions = mapClosureExpToOriginal.Apply(asyncPostconditions); //MemberList members = FindClosureMembersInContract(closure, movenext); // MakeClosureAccessibleToOriginalMethod(closure, members); if (method.Contract == null) method.Contract = new MethodContract(method); method.Contract.Requires = Preconditions; method.Contract.Validations = Validations; // Postconditions are sanity checked here, because Result<T> must be compared against the // return type of the original method. It is most conveniently done after the type substitution. // TODO: refactor the checking part altogether out of ExtractFromClump. method.Contract.Ensures = Postconditions; method.Contract.ModelEnsures = modelPostconditions; method.Contract.ContractInitializer = contractInitializerBlock; method.Contract.AsyncEnsures = asyncPostconditions; // Following replacement causes some weird issues for complex preconditions (like x != null && x.Length > 0) // when CCRewriter is used with /publicsurface or Preconditions only. // This fix could be temporal and proper fix would be applied in the future. // After discussion this issue with original CC authors (Mike Barnett and Francesco Logozzo), // we decided that this fix is safe and lack of Assume statement in the MoveNext method will not affect // customers (neither CCRewriter customers nor CCCheck customers). // If this assumption would not be true in the future, proper fix should be applied. // put requires as assumes into movenext method at original position // ReplaceRequiresWithAssumeInMoveNext(origPreconditions, originalContractPosition); // no postPreamble to initialize, as method is not a ctor } finally { // this is done in caller!!! //// normalize contract by forcing IsPure to look at attributes and removing contract it is empty //var contract = method.Contract; //var isPure = contract.IsPure; //if (!isPure && contract.RequiresCount == 0 && contract.EnsuresCount == 0 && contract.ModelEnsuresCount == 0 && contract.ValidationsCount == 0 && contract.AsyncEnsuresCount == 0) //{ // method.Contract = null; //} else //{ // // turn helper method calls to Result, OldValue, ValueAtReturn into proper AST nodes. // this.extractionFinalizer.VisitMethodContract(method.Contract); //} } } }
Expression GetConvertFromString(TypeNode targetType, Expression src, bool always) { if (targetType == SystemTypes.String) return src;// nothing to do! if (targetType is EnumNode) { // e.g. return (DayOfWeek)Enum.Parse(typeof(DayOfWeek),"Sunday"); Method method = SystemTypes.Enum.GetMethod(Identifier.For("Parse"), new TypeNode[2]{ SystemTypes.Type, SystemTypes.String}); UnaryExpression typeOfEnum = new UnaryExpression(new MemberBinding(null, targetType), NodeType.Typeof); MethodCall call = new MethodCall(new MemberBinding(new MemberBinding(null, targetType), method), new ExpressionList(new Expression[2]{typeOfEnum, src }), NodeType.Call, SystemTypes.Object); return CastTo(call, targetType); } // See if it has a type converter. Class converterClass = Checker.GetTypeConverter(targetType); if (converterClass != null) { TypeConverter converter = TypeDescriptor.GetConverter(targetType.GetRuntimeType()); if (converter != null) { Type converterType = converter.GetType(); AssemblyNode converterAssembly = AssemblyNode.GetAssembly(converterType.Assembly, cache, false, true); converterClass = converterAssembly.GetType(Identifier.For(converterType.Namespace), Identifier.For(converterType.Name)) as Class; Expression e = tempChecker.GetConverterFromString(converter, converterClass, SystemTypes.String, src, SystemTypes.Object); if (e != null) { //Do I need to add namespace and assembly reference for type converter? return CastTo(e, targetType); } } } if (always) { // e.g. return PointArray.Parse("10,20 30,40 50,60"); Method method = targetType.GetImplicitCoercionFromMethod(SystemTypes.String); if (method == null) { method = targetType.GetMethod(Identifier.For("Parse"), new TypeNode[1]{ SystemTypes.String}); } if (method != null) { MemberBinding typeBinding = new MemberBinding(null, targetType); MethodCall call = new MethodCall(new MemberBinding(typeBinding, method), new ExpressionList(new Expression[1]{ src }), NodeType.Call, targetType ); return call; } } return null; }
public RuntimeContractMethods(TypeNode userContractType, ContractNodes contractNodes, AssemblyNode targetAssembly, bool throwOnFailure, int rewriteLevel, bool publicSurfaceOnly, bool callSiteRequires, int recursionGuard, bool hideFromDebugger, bool userExplicitValidation ) { this.contractNodes = contractNodes; this.targetAssembly = targetAssembly; this.ThrowOnFailure = throwOnFailure; this.RewriteLevel = rewriteLevel; this.PublicSurfaceOnly = publicSurfaceOnly; this.CallSiteRequires = callSiteRequires; this.regularRecursionGuard = recursionGuard; this.HideFromDebugger = hideFromDebugger; this.UseExplicitValidation = userExplicitValidation; // extract methods from user methods #region Get the user-specified rewriter methods (optional) REVIEW!! Needs a lot of error handling if (userContractType != null) { Method method = null; MethodList reqMethods = userContractType.GetMethods(Identifier.For("Requires"), SystemTypes.Boolean, SystemTypes.String, SystemTypes.String); for (int i = 0; i < reqMethods.Count; i++) { method = reqMethods[i]; if (method != null) { if (method.TemplateParameters == null || method.TemplateParameters.Count != 1) { /*if (method != null) */ this.requiresMethod = method; } else { this.requiresWithExceptionMethod = method; } } } method = userContractType.GetMethod(Identifier.For("Ensures"), SystemTypes.Boolean, SystemTypes.String, SystemTypes.String); if (method != null) this.ensuresMethod = method; method = userContractType.GetMethod(Identifier.For("EnsuresOnThrow"), SystemTypes.Boolean, SystemTypes.String, SystemTypes.String, SystemTypes.Exception); if (method != null) this.ensuresOnThrowMethod = method; method = userContractType.GetMethod(Identifier.For("Invariant"), SystemTypes.Boolean, SystemTypes.String, SystemTypes.String); if (method != null) this.invariantMethod = method; method = userContractType.GetMethod(Identifier.For("Assert"), SystemTypes.Boolean, SystemTypes.String, SystemTypes.String); if (method != null) this.assertMethod = method; method = userContractType.GetMethod(Identifier.For("Assume"), SystemTypes.Boolean, SystemTypes.String, SystemTypes.String); if (method != null) this.assumeMethod = method; // Need to make sure that the type ContractFailureKind is the one used in the user-supplied methods, which is not necessarily // the one that is defined in the assembly that defines the contract class. For instance, extracting/rewriting from a 4.0 assembly // but where the user-supplied assembly is pre-4.0. var mems = userContractType.GetMembersNamed(ContractNodes.ReportFailureName); TypeNode contractFailureKind = contractNodes.ContractFailureKind; //if (mems != null) { foreach(var mem in mems){ method = mem as Method; if (method == null) continue; if (method.Parameters.Count != 4) continue; if (method.Parameters[0].Type.Name != contractNodes.ContractFailureKind.Name) continue; if (method.Parameters[1].Type != SystemTypes.String) continue; if (method.Parameters[2].Type != SystemTypes.String) continue; if (method.Parameters[3].Type != SystemTypes.Exception) continue; this.failureMethod = method; contractFailureKind = method.Parameters[0].Type; break; } } if (this.failureMethod == null) { mems = userContractType.GetMembersNamed(ContractNodes.RaiseContractFailedEventName); // if (mems != null) { foreach (var mem in mems) { method = mem as Method; if (method == null) continue; if (method.Parameters.Count != 4) continue; if (method.Parameters[0].Type.Name.UniqueIdKey != contractNodes.ContractFailureKind.Name.UniqueIdKey) continue; if (method.Parameters[1].Type != SystemTypes.String) continue; if (method.Parameters[2].Type != SystemTypes.String) continue; if (method.Parameters[3].Type != SystemTypes.Exception) continue; this.raiseFailureEventMethod = method; contractFailureKind = method.Parameters[0].Type; break; } } } else { method = userContractType.GetMethod(ContractNodes.RaiseContractFailedEventName, contractFailureKind, SystemTypes.String, SystemTypes.String, SystemTypes.Exception); if (method != null) this.raiseFailureEventMethod = method; } if (this.raiseFailureEventMethod != null) { // either take all both RaiseContractFailedEvent and TriggerFailure or neither method = userContractType.GetMethod(ContractNodes.TriggerFailureName, contractFailureKind, SystemTypes.String, SystemTypes.String, SystemTypes.String, SystemTypes.Exception); if (method != null) this.triggerFailureMethod = method; } } #endregion Get the user-specified rewriter methods (optional) REVIEW!! Needs a lot of error handling }