void InjectMethodReturnGuard(ValidationFlags localValidationFlags, MethodDefinition method, MethodBody body) { var returnPoints = body.Instructions .Select((o, ix) => new { o, ix }) .Where(a => a.o.OpCode == OpCodes.Ret) .Select(a => a.ix) .OrderByDescending(ix => ix); foreach (var ret in returnPoints) { if (localValidationFlags.HasFlag(ValidationFlags.ReturnValues) && method.ReturnType.IsRefType() && method.ReturnType.FullName != typeof(void).FullName && !method.IsGetter && !nullabilityAnalyzer.AllowsNullReturnValue(method)) { var errorMessage = string.Format(ReturnValueOfMethodIsNull, method.FullName); AddReturnNullGuard(method, ret, method.ReturnType, errorMessage, Instruction.Create(OpCodes.Throw)); } if (localValidationFlags.HasFlag(ValidationFlags.Arguments)) { foreach (var parameter in method.Parameters.Reverse()) { // This is no longer the return instruction location, but it is where we want to jump to. var returnInstruction = body.Instructions[ret]; if (localValidationFlags.HasFlag(ValidationFlags.OutValues) && parameter.ParameterType.IsRefType() && parameter.ParameterType.IsByReference && !parameter.IsIn && !nullabilityAnalyzer.AllowsNullOutput(parameter, method)) { var errorMessage = $"[NullGuard] Out parameter '{parameter.Name}' is null."; var guardInstructions = new List <Instruction>(); if (isDebug) { LoadArgumentOntoStack(guardInstructions, parameter); CallDebugAssertInstructions(guardInstructions, errorMessage); } LoadArgumentOntoStack(guardInstructions, parameter); IfNull(guardInstructions, returnInstruction, i => { LoadInvalidOperationException(i, errorMessage); // Throw the top item off the stack i.Add(Instruction.Create(OpCodes.Throw)); }); body.InsertAtMethodReturnPoint(ret, guardInstructions); } } } } }
private void InjectMethodReturnGuard(ValidationFlags localValidationFlags, MethodDefinition method, MethodBody body) { var guardInstructions = new List <Instruction>(); var returnPoints = body.Instructions .Select((o, ix) => new { o, ix }) .Where(a => a.o.OpCode == OpCodes.Ret) .Select(a => a.ix) .OrderByDescending(ix => ix); foreach (var ret in returnPoints) { var returnInstruction = body.Instructions[ret]; if (localValidationFlags.HasFlag(ValidationFlags.ReturnValues) && !method.MethodReturnType.AllowsNull() && method.ReturnType.IsRefType() && method.ReturnType.FullName != typeof(void).FullName) { AddReturnNullGuard(body.Instructions, ret, method.ReturnType, String.Format(CultureInfo.InvariantCulture, STR_ReturnValueOfMethodIsNull, method.FullName), Instruction.Create(OpCodes.Throw)); } if (localValidationFlags.HasFlag(ValidationFlags.Arguments)) { foreach (var parameter in method.Parameters.Reverse()) { // This is no longer the return instruction location, but it is where we want to jump to. returnInstruction = body.Instructions[ret]; if (localValidationFlags.HasFlag(ValidationFlags.OutValues) && parameter.IsOut && parameter.ParameterType.IsRefType()) { guardInstructions.Clear(); if (isDebug) { InstructionPatterns.LoadArgumentOntoStack(guardInstructions, parameter); InstructionPatterns.CallDebugAssertInstructions(guardInstructions, String.Format(CultureInfo.InvariantCulture, STR_OutParameterIsNull, parameter.Name)); } InstructionPatterns.LoadArgumentOntoStack(guardInstructions, parameter); InstructionPatterns.IfNull(guardInstructions, returnInstruction, i => { InstructionPatterns.LoadInvalidOperationException(i, String.Format(CultureInfo.InvariantCulture, STR_OutParameterIsNull, parameter.Name)); // Throw the top item off the stack i.Add(Instruction.Create(OpCodes.Throw)); }); body.Instructions.Insert(ret, guardInstructions); } } } } }
private void InjectMethodReturnGuard(ValidationFlags localValidationFlags, MethodDefinition method, MethodBody body) { var guardInstructions = new List<Instruction>(); var returnPoints = body.Instructions .Select((o, ix) => new { o, ix }) .Where(a => a.o.OpCode == OpCodes.Ret) .Select(a => a.ix) .OrderByDescending(ix => ix); foreach (var ret in returnPoints) { var returnInstruction = body.Instructions[ret]; if (localValidationFlags.HasFlag(ValidationFlags.ReturnValues) && !method.MethodReturnType.AllowsNull() && method.ReturnType.IsRefType() && method.ReturnType.FullName != typeof(void).FullName) { AddReturnNullGuard(body.Instructions, ret, method.ReturnType, String.Format(CultureInfo.InvariantCulture, STR_ReturnValueOfMethodIsNull, method.FullName), Instruction.Create(OpCodes.Throw)); } if (localValidationFlags.HasFlag(ValidationFlags.Arguments)) { foreach (var parameter in method.Parameters.Reverse()) { // This is no longer the return instruction location, but it is where we want to jump to. returnInstruction = body.Instructions[ret]; if (localValidationFlags.HasFlag(ValidationFlags.OutValues) && parameter.IsOut && parameter.ParameterType.IsRefType()) { guardInstructions.Clear(); if (isDebug) { InstructionPatterns.LoadArgumentOntoStack(guardInstructions, parameter); InstructionPatterns.CallDebugAssertInstructions(guardInstructions, String.Format(CultureInfo.InvariantCulture, STR_OutParameterIsNull, parameter.Name)); } InstructionPatterns.LoadArgumentOntoStack(guardInstructions, parameter); InstructionPatterns.IfNull(guardInstructions, returnInstruction, i => { InstructionPatterns.LoadInvalidOperationException(i, String.Format(CultureInfo.InvariantCulture, STR_OutParameterIsNull, parameter.Name)); // Throw the top item off the stack i.Add(Instruction.Create(OpCodes.Throw)); }); body.Instructions.Insert(ret, guardInstructions); } } } } }
protected IEnumerable <ValidationResult> PerformValidation(IPersistEntity ent, bool hierarchical, ValidationFlags validateLevel = ValidationFlags.Properties) { var thisEntAdded = false; var hierResult = new ValidationResult() { Item = ent, IssueType = ValidationFlags.None }; if (validateLevel == ValidationFlags.None) { yield break; //nothing to do } var expType = ent.ExpressType; if (validateLevel.HasFlag(ValidationFlags.Properties)) { foreach (var prop in expType.Properties.Values) { var errs = GetSchemaErrors(ent, prop, validateLevel, hierarchical); foreach (var validationResult in errs) { validationResult.IssueType |= ValidationFlags.Properties; thisEntAdded = UpdateCount(thisEntAdded); if (hierarchical) { hierResult.AddDetail(validationResult); } else { validationResult.Item = ent; yield return(validationResult); } } } } if (validateLevel.HasFlag(ValidationFlags.Inverses)) { foreach (var inv in expType.Inverses) { var errs = GetSchemaErrors(ent, inv, validateLevel, hierarchical); foreach (var validationResult in errs) { validationResult.IssueType |= ValidationFlags.Inverses; thisEntAdded = UpdateCount(thisEntAdded); if (hierarchical) { hierResult.AddDetail(validationResult); } else { validationResult.Item = ent; yield return(validationResult); } } } } if (validateLevel.HasFlag(ValidationFlags.EntityWhereClauses) && ent is IExpressValidatable) { var errs = ((IExpressValidatable)ent).Validate(); foreach (var validationResult in errs) { thisEntAdded = UpdateCount(thisEntAdded); if (hierarchical) { hierResult.AddDetail(validationResult); } else { yield return(validationResult); } } } if (hierarchical && hierResult.IssueType != ValidationFlags.None) { // the IssueType is populated if any children have been added. hierResult.Message = string.Format("Entity #{0} ({1}) has validation failures.", ent.EntityLabel, expType.Name); yield return(hierResult); } }
protected static IEnumerable <ValidationResult> GetSchemaErrors(IPersist instance, ExpressMetaProperty prop, ValidationFlags validateLevel, bool hierarchical) { var attr = prop.EntityAttribute; var propVal = prop.PropertyInfo.GetValue(instance, null); var propName = prop.PropertyInfo.Name; if (propVal is IExpressValueType) { var val = ((IExpressValueType)propVal).Value; var underlyingType = ((IExpressValueType)propVal).UnderlyingSystemType; if (attr.State == EntityAttributeState.Mandatory && val == null && underlyingType != typeof(bool?)) { yield return(new ValidationResult() { Item = instance, IssueType = ValidationFlags.Properties, IssueSource = propName, Message = string.Format("{0}.{1} is not optional.", instance.GetType().Name, propName) }); } if (validateLevel.HasFlag(ValidationFlags.TypeWhereClauses) && propVal is IExpressValidatable) { var hierResult = new ValidationResult() { Item = instance, IssueType = ValidationFlags.None }; foreach (var issue in ((IExpressValidatable)propVal).Validate()) { if (hierarchical) { hierResult.AddDetail(issue); } else { yield return(issue); } } if (hierarchical && hierResult.IssueType != ValidationFlags.None) { // the IssueType is populated if any children have been added. hierResult.Message = string.Format("Property {0} has validation failures.", prop.Name); yield return(hierResult); } } yield break; } if (attr.State == EntityAttributeState.Mandatory && propVal == null) { yield return new ValidationResult() { Item = instance, IssueType = ValidationFlags.Properties, IssueSource = propName, Message = string.Format("{0}.{1} is not optional.", instance.GetType().Name, propName) } } ; if (attr.State == EntityAttributeState.Optional && propVal == null) { //if it is null and optional then it is ok yield break; } if (attr.State == EntityAttributeState.Optional && propVal is IOptionalItemSet && !((IOptionalItemSet)propVal).Initialized) { //if it is non-initialized list and optional then it is ok yield break; } if (attr.IsEnumerable) { if ( (attr.MinCardinality == null || attr.MinCardinality.Length == 0 || attr.MinCardinality.All(c => c < 0)) && (attr.MaxCardinality == null || attr.MaxCardinality.Length == 0 || attr.MaxCardinality.All(c => c < 1))) //we don't care how many so don't check { yield break; } var depth = attr.MinCardinality.Length; if (depth != attr.MaxCardinality.Length) { throw new System.Exception("Inconsistent metadata: minimal and maximal cardinality has to have the same length."); } var sb = new StringBuilder(); var items = (IEnumerable)propVal; CheckCardinality(attr.MinCardinality, attr.MaxCardinality, items, 0, sb); var msg = sb.ToString(); if (string.IsNullOrWhiteSpace(msg)) { yield break; } yield return(new ValidationResult() { Item = instance, IssueType = ValidationFlags.Properties, IssueSource = propName, Message = string.Format("{0}.{1}: {2}", instance.GetType().Name, prop.Name, msg) }); } }