예제 #1
0
        private void PopulatePropertyWithGeneratedDefaultValue(ISarifNode node, PropertyInfo property)
        {
            // If we can't set this property, it is not of interest
            if (property.GetAccessors().Length != 2)
            {
                return;
            }

            // This special-casing is required to account for the fact that an
            // exception instance itself contains an array of exceptions
            // (exception.innerExceptions). We don't want to hydrate
            // the innerExceptions of any innerExceptions, which results
            // in re-entrancy that ends up consuming all stack space.
            //
            // Once we have populated a single exceptions.innerExceptions array,
            // we have accomplished all the testing we need in any case.
            if (node.SarifNodeKind == SarifNodeKind.ExceptionData &&
                property.Name == "InnerExceptions" &&
                _visitingExceptionData)
            {
                return;
            }

            // Similar approach applies for graph node.children
            if (node.SarifNodeKind == SarifNodeKind.Node &&
                property.Name == "Children" &&
                _visitingGraphNode)
            {
                return;
            }

            object propertyValue = null;
            Type   propertyType  = property.PropertyType;

            // isRequired flag ensures we don't end up generating a SARIF file that's missing a required property or an anyOf required property,
            // because such a file wouldn't validate.
            bool isRequired = PropertyIsRequiredBySchema(node.GetType().Name, property.Name) ||
                              PropertyIsAnyOfRequiredBySchema(node.GetType().Name, property.Name);

            if (GetPropertyFormatPattern(node.GetType().Name, property.Name) is string propertyFormatPattern)
            {
                propertyValue = GetFormattedStringValue(propertyFormatPattern);
            }
            else if (_typeToPropertyValueConstructorMap.TryGetValue(propertyType, out PrimitiveValueBuilder propertyValueBuilder))
            {
                propertyValue = propertyValueBuilder(isRequired);
            }
            else if (HasParameterlessConstructor(propertyType))
            {
                propertyValue = Activator.CreateInstance(propertyType);
            }
            else if (IsList(propertyType))
            {
                propertyValue = CreateEmptyList(propertyType);

                Type   genericTypeArgument = propertyType.GenericTypeArguments[0];
                object listElement         = null;

                // For arrays that are populated with SARIF types, we will instantiate a
                // single object instance and put it into the array. This allows the
                // default populating visitor to continue to explore the object model and
                // exercise nested types. This approach prevents comprehensive testing of
                // the arrays populated in this way (because they are non-empty
                if (genericTypeArgument.FullName.StartsWith("Microsoft.CodeAnalysis.Sarif."))
                {
                    listElement = Activator.CreateInstance(propertyType.GenericTypeArguments[0]);
                }
                else if (_typeToPropertyValueConstructorMap.TryGetValue(genericTypeArgument, out propertyValueBuilder))
                {
                    listElement = propertyValueBuilder(isRequired);
                }

                AddElementToList(propertyValue, listElement);
            }
            else if (IsDictionary(propertyType))
            {
                Type genericTypeArgument = propertyType.GenericTypeArguments[1];

                // We do not populate any propert bags directly. Instead, we will use the
                // IPropertyBagHolder API to instantiate and then empty these constructs
                if (genericTypeArgument != typeof(SerializedPropertyInfo))
                {
                    propertyValue = CreateEmptyDictionary(propertyType);

                    // For dictionaries that are populated with SARIF types, we will instantiate a
                    // single object instance and store it using an arbitrary key. This approach
                    // ensures we populate the SARIF sample with all possible object types
                    if (genericTypeArgument.FullName.StartsWith("Microsoft.CodeAnalysis.Sarif."))
                    {
                        object dictionaryValue = Activator.CreateInstance(genericTypeArgument);
                        AddElementToDictionary(propertyValue, dictionaryValue);
                    }
                }
            }
            else if ((property.PropertyType.BaseType == typeof(Enum)))
            {
                // This code sets any enum to the first non-zero value we encounter
                foreach (var enumValue in Enum.GetValues(property.PropertyType))
                {
                    if ((int)enumValue != 0)
                    {
                        propertyValue = enumValue;
                        break;
                    }
                }

                // This code ensures both that we encounter an enum value that is non-zero,
                // and that no enum definitions skips the value of one in its definition
                if ((int)propertyValue != 1)
                {
                    throw new InvalidOperationException();
                }
            }

            property.SetValue(node, propertyValue);
        }
예제 #2
0
        private void PopulateInstanceWithDefaultMemberValues(ISarifNode node)
        {
            Type nodeType = node.GetType();

            var binding = BindingFlags.Public | BindingFlags.Instance;

            foreach (PropertyInfo property in nodeType.GetProperties(binding))
            {
                // The node kind is always properly set in the OM and
                // isn't relevant to the SARIF schema itself
                if (property.Name == "SarifNodeKind")
                {
                    continue;
                }

                // Property bags and tags are populated via special methods on
                // the class rather than direct access of properties. These
                // property names extend from the PropertyBagHolder base type
                // that all properties-bearing types extend (nearly every SARIF
                // class at this point).
                if (property.Name == "PropertyNames" ||
                    property.Name == "Tags")
                {
                    continue;
                }

                if (ShouldExcludePopulationDueToOneOfCriteria(node.GetType().Name, property.Name))
                {
                    continue;
                }

                object defaultValue = null;

                MemberInfo member = nodeType.GetMember(property.Name)[0];
                foreach (CustomAttributeData attributeData in member?.GetCustomAttributesData())
                {
                    if (attributeData.AttributeType.FullName != "System.ComponentModel.DefaultValueAttribute")
                    {
                        continue;
                    }
                    defaultValue = attributeData.ConstructorArguments[0].Value;

                    // DefaultValue of -1 is to ensure an actual value of 0 will not be ignored during serialization,
                    // Hence, we will substitute them with 0.
                    if (defaultValue is int defaultIntValue && defaultIntValue == -1)
                    {
                        defaultValue = 0;
                    }
                }

                // If the member is decorated with a default value, we'll inject it. Otherwise,
                // we'll compute a default value based on the node type
                if (defaultValue != null)
                {
                    property.SetValue(node, defaultValue);
                    continue;
                }
                PopulatePropertyWithGeneratedDefaultValue(node, property);
            }

            // If we have a property bag holder (as most SARIF things are), we will
            // add and then immediately remove both a property and a tag. This has
            // the effect of leaving non-null but empty properties collection.
            var propertyBagHolder = node as PropertyBagHolder;

            if (propertyBagHolder != null)
            {
                string meaninglessValue = "ToBeRemoved";
                propertyBagHolder.SetProperty(propertyName: meaninglessValue, value: meaninglessValue);
                propertyBagHolder.RemoveProperty(propertyName: meaninglessValue);

                propertyBagHolder.Tags.Add(meaninglessValue);
                propertyBagHolder.Tags.Remove(meaninglessValue);
            }
        }