예제 #1
0
    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);
                    }
                }
            }
        }
    }
예제 #2
0
    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);
                    }
                }
            }
        }
    }
예제 #3
0
    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);
                    }
                }
            }
        }
    }
예제 #4
0
        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);
            }
        }
예제 #5
0
        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)
                });
            }
        }