public override object Visit(ISarifNode node) { // We override a very low level visit, one that occurs for every SARIF object instance. // We do this for two reasons: 1) it would be unnecessarily burdensome to override // every specific VisitXXX method in order to populate it, 2) this approach, even // if undertaken, would be quite fragile. Visit methods that disappear would break our // build. More worrisome, the code would miss newly introduced types on the visitor. // // We could have avoided the visitor altogether and simply performed recursive reflection // over a root SarifLog instance. We use this mechanism in order to minimize introducing // an entirely one-off construct, to glean whatever small measure of reuse is afforded // by the visitor, and based on an assumption that future check-ins will utilize // chained visitors and/or utilize more core visitor functionality. PopulateInstanceWithDefaultMemberValues(node); return(base.Visit(node)); }
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); }
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); } }
/// <summary> /// Visits and rewrites a node in the Sarif object model. /// </summary> /// <param name="node"> /// The node to rewrite. /// </param> /// <returns> /// A rewritten instance of the node. /// </returns> public virtual object VisitActual(ISarifNode node) { if (node == null) { throw new ArgumentNullException("node"); } switch (node.SarifNodeKind) { case SarifNodeKind.Attachment: return(VisitAttachment((Attachment)node)); case SarifNodeKind.CodeFlow: return(VisitCodeFlow((CodeFlow)node)); case SarifNodeKind.CodeFlowLocation: return(VisitCodeFlowLocation((CodeFlowLocation)node)); case SarifNodeKind.Conversion: return(VisitConversion((Conversion)node)); case SarifNodeKind.Edge: return(VisitEdge((Edge)node)); case SarifNodeKind.EdgeTraversal: return(VisitEdgeTraversal((EdgeTraversal)node)); case SarifNodeKind.ExceptionData: return(VisitExceptionData((ExceptionData)node)); case SarifNodeKind.FileChange: return(VisitFileChange((FileChange)node)); case SarifNodeKind.FileContent: return(VisitFileContent((FileContent)node)); case SarifNodeKind.FileData: return(VisitFileData((FileData)node)); case SarifNodeKind.FileLocation: return(VisitFileLocation((FileLocation)node)); case SarifNodeKind.Fix: return(VisitFix((Fix)node)); case SarifNodeKind.Graph: return(VisitGraph((Graph)node)); case SarifNodeKind.GraphTraversal: return(VisitGraphTraversal((GraphTraversal)node)); case SarifNodeKind.Hash: return(VisitHash((Hash)node)); case SarifNodeKind.Invocation: return(VisitInvocation((Invocation)node)); case SarifNodeKind.Location: return(VisitLocation((Location)node)); case SarifNodeKind.LogicalLocation: return(VisitLogicalLocation((LogicalLocation)node)); case SarifNodeKind.Message: return(VisitMessage((Message)node)); case SarifNodeKind.Node: return(VisitNode((Node)node)); case SarifNodeKind.Notification: return(VisitNotification((Notification)node)); case SarifNodeKind.PhysicalLocation: return(VisitPhysicalLocation((PhysicalLocation)node)); case SarifNodeKind.Rectangle: return(VisitRectangle((Rectangle)node)); case SarifNodeKind.Region: return(VisitRegion((Region)node)); case SarifNodeKind.Replacement: return(VisitReplacement((Replacement)node)); case SarifNodeKind.Resources: return(VisitResources((Resources)node)); case SarifNodeKind.Result: return(VisitResult((Result)node)); case SarifNodeKind.Rule: return(VisitRule((Rule)node)); case SarifNodeKind.RuleConfiguration: return(VisitRuleConfiguration((RuleConfiguration)node)); case SarifNodeKind.Run: return(VisitRun((Run)node)); case SarifNodeKind.SarifLog: return(VisitSarifLog((SarifLog)node)); case SarifNodeKind.Stack: return(VisitStack((Stack)node)); case SarifNodeKind.StackFrame: return(VisitStackFrame((StackFrame)node)); case SarifNodeKind.ThreadFlow: return(VisitThreadFlow((ThreadFlow)node)); case SarifNodeKind.Tool: return(VisitTool((Tool)node)); case SarifNodeKind.VersionControlDetails: return(VisitVersionControlDetails((VersionControlDetails)node)); default: return(node); } }
/// <summary> /// Starts a rewriting visit of a node in the Sarif object model. /// </summary> /// <param name="node"> /// The node to rewrite. /// </param> /// <returns> /// A rewritten instance of the node. /// </returns> public virtual object Visit(ISarifNode node) { return(this.VisitActual(node)); }
/// <summary> /// Visits and rewrites a node in the Sarif object model. /// </summary> /// <param name="node"> /// The node to rewrite. /// </param> /// <returns> /// A rewritten instance of the node. /// </returns> public virtual object VisitActual(ISarifNode node) { if (node == null) { throw new ArgumentNullException("node"); } switch (node.SarifNodeKind) { case SarifNodeKind.AnnotatedCodeLocation: return(VisitAnnotatedCodeLocation((AnnotatedCodeLocation)node)); case SarifNodeKind.CodeFlow: return(VisitCodeFlow((CodeFlow)node)); case SarifNodeKind.FileChange: return(VisitFileChange((FileChange)node)); case SarifNodeKind.FileData: return(VisitFileData((FileData)node)); case SarifNodeKind.Fix: return(VisitFix((Fix)node)); case SarifNodeKind.FormattedRuleMessage: return(VisitFormattedRuleMessage((FormattedRuleMessage)node)); case SarifNodeKind.Hash: return(VisitHash((Hash)node)); case SarifNodeKind.Invocation: return(VisitInvocation((Invocation)node)); case SarifNodeKind.Location: return(VisitLocation((Location)node)); case SarifNodeKind.LogicalLocationComponent: return(VisitLogicalLocationComponent((LogicalLocationComponent)node)); case SarifNodeKind.PhysicalLocation: return(VisitPhysicalLocation((PhysicalLocation)node)); case SarifNodeKind.Region: return(VisitRegion((Region)node)); case SarifNodeKind.Replacement: return(VisitReplacement((Replacement)node)); case SarifNodeKind.Result: return(VisitResult((Result)node)); case SarifNodeKind.Rule: return(VisitRule((Rule)node)); case SarifNodeKind.Run: return(VisitRun((Run)node)); case SarifNodeKind.SarifLog: return(VisitSarifLog((SarifLog)node)); case SarifNodeKind.Stack: return(VisitStack((Stack)node)); case SarifNodeKind.StackFrame: return(VisitStackFrame((StackFrame)node)); case SarifNodeKind.Tool: return(VisitTool((Tool)node)); default: return(node); } }
/// <summary> /// Visits and rewrites a node in the Sarif object model. /// </summary> /// <param name="node"> /// The node to rewrite. /// </param> /// <returns> /// A rewritten instance of the node. /// </returns> public virtual object VisitActual(ISarifNode node) { if (node == null) { throw new ArgumentNullException("node"); } switch (node.SarifNodeKind) { case SarifNodeKind.Artifact: return(VisitArtifact((Artifact)node)); case SarifNodeKind.ArtifactChange: return(VisitArtifactChange((ArtifactChange)node)); case SarifNodeKind.ArtifactContent: return(VisitArtifactContent((ArtifactContent)node)); case SarifNodeKind.ArtifactLocation: return(VisitArtifactLocation((ArtifactLocation)node)); case SarifNodeKind.Attachment: return(VisitAttachment((Attachment)node)); case SarifNodeKind.CodeFlow: return(VisitCodeFlow((CodeFlow)node)); case SarifNodeKind.Conversion: return(VisitConversion((Conversion)node)); case SarifNodeKind.Edge: return(VisitEdge((Edge)node)); case SarifNodeKind.EdgeTraversal: return(VisitEdgeTraversal((EdgeTraversal)node)); case SarifNodeKind.ExceptionData: return(VisitExceptionData((ExceptionData)node)); case SarifNodeKind.ExternalPropertyFile: return(VisitExternalPropertyFile((ExternalPropertyFile)node)); case SarifNodeKind.ExternalPropertyFiles: return(VisitExternalPropertyFiles((ExternalPropertyFiles)node)); case SarifNodeKind.Fix: return(VisitFix((Fix)node)); case SarifNodeKind.Graph: return(VisitGraph((Graph)node)); case SarifNodeKind.GraphTraversal: return(VisitGraphTraversal((GraphTraversal)node)); case SarifNodeKind.Invocation: return(VisitInvocation((Invocation)node)); case SarifNodeKind.Location: return(VisitLocation((Location)node)); case SarifNodeKind.LogicalLocation: return(VisitLogicalLocation((LogicalLocation)node)); case SarifNodeKind.Message: return(VisitMessage((Message)node)); case SarifNodeKind.MultiformatMessageString: return(VisitMultiformatMessageString((MultiformatMessageString)node)); case SarifNodeKind.Node: return(VisitNode((Node)node)); case SarifNodeKind.Notification: return(VisitNotification((Notification)node)); case SarifNodeKind.PhysicalLocation: return(VisitPhysicalLocation((PhysicalLocation)node)); case SarifNodeKind.PropertyBag: return(VisitPropertyBag((PropertyBag)node)); case SarifNodeKind.Rectangle: return(VisitRectangle((Rectangle)node)); case SarifNodeKind.Region: return(VisitRegion((Region)node)); case SarifNodeKind.Replacement: return(VisitReplacement((Replacement)node)); case SarifNodeKind.ReportingConfiguration: return(VisitReportingConfiguration((ReportingConfiguration)node)); case SarifNodeKind.ReportingConfigurationOverride: return(VisitReportingConfigurationOverride((ReportingConfigurationOverride)node)); case SarifNodeKind.ReportingDescriptor: return(VisitReportingDescriptor((ReportingDescriptor)node)); case SarifNodeKind.Result: return(VisitResult((Result)node)); case SarifNodeKind.ResultProvenance: return(VisitResultProvenance((ResultProvenance)node)); case SarifNodeKind.Run: return(VisitRun((Run)node)); case SarifNodeKind.RunAutomationDetails: return(VisitRunAutomationDetails((RunAutomationDetails)node)); case SarifNodeKind.SarifLog: return(VisitSarifLog((SarifLog)node)); case SarifNodeKind.Stack: return(VisitStack((Stack)node)); case SarifNodeKind.StackFrame: return(VisitStackFrame((StackFrame)node)); case SarifNodeKind.ThreadFlow: return(VisitThreadFlow((ThreadFlow)node)); case SarifNodeKind.ThreadFlowLocation: return(VisitThreadFlowLocation((ThreadFlowLocation)node)); case SarifNodeKind.Tool: return(VisitTool((Tool)node)); case SarifNodeKind.ToolComponent: return(VisitToolComponent((ToolComponent)node)); case SarifNodeKind.VersionControlDetails: return(VisitVersionControlDetails((VersionControlDetails)node)); default: return(node); } }