Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions scripts/StjAttributeTransform.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<#
.SYNOPSIS
One-time mechanical transform that augments the frozen SARIF object model
with System.Text.Json attributes alongside the existing Newtonsoft ones.
.DESCRIPTION
Issue #3038 Phase 0. The model under src/Sarif/Autogenerated/ (and
NotYetAutoGenerated/, VersionOne/Autogenerated/) was last regenerated in
2022 and is treated as frozen source. This script ADDS STJ attributes via
a `using Stj = System.Text.Json.Serialization;` alias so both serializers
work against the same model during the transition:

[DataMember(Name = "x", ...)] -> + [Stj.JsonPropertyName("x")]
[JsonProperty(DefaultValueHandling
= ...IgnoreAndPopulate)] -> + [Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
[JsonIgnore] (Newtonsoft) -> + [Stj.JsonIgnore]
using Newtonsoft.Json; -> + using Stj = System.Text.Json.Serialization;

Per-property [JsonConverter(typeof(...))] attributes are LEFT IN PLACE
(Newtonsoft-only); STJ converter binding is global via
SarifSerializerOptions.Converters, mirroring SarifContractResolver.

Idempotent: re-running is a no-op once the Stj alias is present.
#>

[CmdletBinding()]
param()

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

$RepoRoot = Resolve-Path "$PSScriptRoot\.."
$roots = @(
"$RepoRoot\src\Sarif\Autogenerated",
"$RepoRoot\src\Sarif\NotYetAutoGenerated",
"$RepoRoot\src\Sarif\VersionOne\Autogenerated",
"$RepoRoot\src\Sarif\VersionOne\NotYetAutoGenerated"
)

$dataMember = [regex]'(?m)^(\s*)\[DataMember\(Name\s*=\s*"([^"]+)"[^\]]*\)\]\s*$'
$ignoreAndPopulate = [regex]'(?m)^(\s*)\[JsonProperty\(DefaultValueHandling\s*=\s*DefaultValueHandling\.\w+\)\]\s*$'
$newtonsoftJsonIgnore = [regex]'(?m)^(\s*)\[JsonIgnore\]\s*$'
$usingNewtonsoft = [regex]'(?m)^using Newtonsoft\.Json;\s*$'

$touched = 0
foreach ($root in $roots) {
if (-not (Test-Path $root)) { continue }
foreach ($file in Get-ChildItem -Path $root -Filter *.cs -File) {
$text = [IO.File]::ReadAllText($file.FullName)
if ($text -match 'using Stj = System\.Text\.Json\.Serialization;') { continue } # idempotent

$orig = $text

# Add the alias under the Newtonsoft using (or, if absent, under
# System.Runtime.Serialization which every model file carries).
if ($usingNewtonsoft.IsMatch($text)) {
$text = $usingNewtonsoft.Replace($text, "using Newtonsoft.Json;`r`nusing Stj = System.Text.Json.Serialization;", 1)
}
elseif ($text -match '(?m)^using System\.Runtime\.Serialization;\s*$') {
$text = $text -replace '(?m)^using System\.Runtime\.Serialization;\s*$', "using System.Runtime.Serialization;`r`nusing Stj = System.Text.Json.Serialization;"
}
else {
continue # not a model file we care about
}

# [DataMember(Name = "x", ...)] -> same line + [Stj.JsonPropertyName("x")]
$text = $dataMember.Replace($text, {
param($m)
$indent = $m.Groups[1].Value
$name = $m.Groups[2].Value
"$($m.Value.TrimEnd())`r`n$indent[Stj.JsonPropertyName(`"$name`")]"
})

# [JsonProperty(DefaultValueHandling = ...)] -> same + [Stj.JsonIgnore(Condition = WhenWritingDefault)]
$text = $ignoreAndPopulate.Replace($text, {
param($m)
$indent = $m.Groups[1].Value
"$($m.Value.TrimEnd())`r`n$indent[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]"
})

# [JsonIgnore] (Newtonsoft) -> same + [Stj.JsonIgnore]
$text = $newtonsoftJsonIgnore.Replace($text, {
param($m)
$indent = $m.Groups[1].Value
"$($m.Value.TrimEnd())`r`n$indent[Stj.JsonIgnore]"
})

if ($text -ne $orig) {
[IO.File]::WriteAllText($file.FullName, $text)
$touched++
}
}
}

Write-Host "StjAttributeTransform: augmented $touched model file(s)."
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<PackageVersion Include="CsvHelper" Version="15.0.5" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="JsonSchema.Net" Version="9.2.1" />
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.2.2" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.EventRegister" Version="1.1.28" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.3" />
Expand Down
16 changes: 15 additions & 1 deletion src/Sarif/Autogenerated/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Runtime.Serialization;

using Newtonsoft.Json;

using Stj = System.Text.Json.Serialization;
namespace Microsoft.CodeAnalysis.Sarif
{
/// <summary>
Expand Down Expand Up @@ -40,67 +40,81 @@ public virtual SarifNodeKind SarifNodeKind
/// The address expressed as a byte offset from the start of the addressable region.
/// </summary>
[DataMember(Name = "absoluteAddress", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("absoluteAddress")]
[DefaultValue(-1)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
public virtual int AbsoluteAddress { get; set; }

/// <summary>
/// The address expressed as a byte offset from the absolute address of the top-most parent object.
/// </summary>
[DataMember(Name = "relativeAddress", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("relativeAddress")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
public virtual int? RelativeAddress { get; set; } // NOTYETAUTOGENERATED: Property should be nullable int. https://github.com/Microsoft/jschema/issues/112

/// <summary>
/// The number of bytes in this range of addresses.
/// </summary>
[DataMember(Name = "length", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("length")]
public virtual int? Length { get; set; } // NOTYETAUTOGENERATED: Property should be nullable int. https://github.com/Microsoft/jschema/issues/112

/// <summary>
/// An open-ended string that identifies the address kind. 'data', 'function', 'header','instruction', 'module', 'page', 'section', 'segment', 'stack', 'stackFrame', 'table' are well-known values.
/// </summary>
[DataMember(Name = "kind", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("kind")]
public virtual string Kind { get; set; }

/// <summary>
/// A name that is associated with the address, e.g., '.text'.
/// </summary>
[DataMember(Name = "name", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("name")]
public virtual string Name { get; set; }

/// <summary>
/// A human-readable fully qualified name that is associated with the address.
/// </summary>
[DataMember(Name = "fullyQualifiedName", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("fullyQualifiedName")]
public virtual string FullyQualifiedName { get; set; }

/// <summary>
/// The byte offset of this address from the absolute or relative address of the parent object.
/// </summary>
[DataMember(Name = "offsetFromParent", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("offsetFromParent")]
public virtual int? OffsetFromParent { get; set; } // NOTYETAUTOGENERATED: Property should be nullable int. https://github.com/Microsoft/jschema/issues/112

/// <summary>
/// The index within run.addresses of the cached object for this address.
/// </summary>
[DataMember(Name = "index", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("index")]
[DefaultValue(-1)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
public virtual int Index { get; set; }

/// <summary>
/// The index within run.addresses of the parent object.
/// </summary>
[DataMember(Name = "parentIndex", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("parentIndex")]
[DefaultValue(-1)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
public virtual int ParentIndex { get; set; }

/// <summary>
/// Key/value pairs that provide additional information about the address.
/// </summary>
[DataMember(Name = "properties", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("properties")]
internal override IDictionary<string, SerializedPropertyInfo> Properties { get; set; }

/// <summary>
Expand Down
18 changes: 17 additions & 1 deletion src/Sarif/Autogenerated/Artifact.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using System.Runtime.Serialization;

using Newtonsoft.Json;

using Stj = System.Text.Json.Serialization;
namespace Microsoft.CodeAnalysis.Sarif
{
/// <summary>
Expand Down Expand Up @@ -40,85 +40,101 @@ public virtual SarifNodeKind SarifNodeKind
/// A short description of the artifact.
/// </summary>
[DataMember(Name = "description", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("description")]
public virtual Message Description { get; set; }

/// <summary>
/// The location of the artifact.
/// </summary>
[DataMember(Name = "location", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("location")]
public virtual ArtifactLocation Location { get; set; }

/// <summary>
/// Identifies the index of the immediate parent of the artifact, if this artifact is nested.
/// </summary>
[DataMember(Name = "parentIndex", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("parentIndex")]
[DefaultValue(-1)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
public virtual int ParentIndex { get; set; }

/// <summary>
/// The offset in bytes of the artifact within its containing artifact.
/// </summary>
[DataMember(Name = "offset", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("offset")]
public virtual int Offset { get; set; }

/// <summary>
/// The length of the artifact in bytes.
/// </summary>
[DataMember(Name = "length", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("length")]
[DefaultValue(-1)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
public virtual int Length { get; set; }

/// <summary>
/// The role or roles played by the artifact in the analysis.
/// </summary>
[DataMember(Name = "roles", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("roles")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[Stj.JsonIgnore(Condition = Stj.JsonIgnoreCondition.WhenWritingDefault)]
[JsonConverter(typeof(Microsoft.CodeAnalysis.Sarif.Readers.FlagsEnumConverter))]
public virtual ArtifactRoles Roles { get; set; }

/// <summary>
/// The MIME type (RFC 2045) of the artifact.
/// </summary>
[DataMember(Name = "mimeType", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("mimeType")]
public virtual string MimeType { get; set; }

/// <summary>
/// The contents of the artifact.
/// </summary>
[DataMember(Name = "contents", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("contents")]
public virtual ArtifactContent Contents { get; set; }

/// <summary>
/// Specifies the encoding for an artifact object that refers to a text file.
/// </summary>
[DataMember(Name = "encoding", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("encoding")]
public virtual string Encoding { get; set; }

/// <summary>
/// Specifies the source language for any artifact object that refers to a text file that contains source code.
/// </summary>
[DataMember(Name = "sourceLanguage", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("sourceLanguage")]
public virtual string SourceLanguage { get; set; }

/// <summary>
/// A dictionary, each of whose keys is the name of a hash function and each of whose values is the hashed value of the artifact produced by the specified hash function.
/// </summary>
[DataMember(Name = "hashes", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("hashes")]
public virtual IDictionary<string, string> Hashes { get; set; }

/// <summary>
/// The Coordinated Universal Time (UTC) date and time at which the artifact was most recently modified. See "Date/time properties" in the SARIF spec for the required format.
/// </summary>
[DataMember(Name = "lastModifiedTimeUtc", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("lastModifiedTimeUtc")]
[JsonConverter(typeof(Microsoft.CodeAnalysis.Sarif.Readers.DateTimeConverter))]
public virtual DateTime LastModifiedTimeUtc { get; set; }

/// <summary>
/// Key/value pairs that provide additional information about the artifact.
/// </summary>
[DataMember(Name = "properties", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("properties")]
internal override IDictionary<string, SerializedPropertyInfo> Properties { get; set; }

/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion src/Sarif/Autogenerated/ArtifactChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Runtime.Serialization;

using Stj = System.Text.Json.Serialization;
namespace Microsoft.CodeAnalysis.Sarif
{
/// <summary>
Expand Down Expand Up @@ -37,18 +37,21 @@ public virtual SarifNodeKind SarifNodeKind
/// The location of the artifact to change.
/// </summary>
[DataMember(Name = "artifactLocation", IsRequired = true)]
[Stj.JsonPropertyName("artifactLocation")]
public virtual ArtifactLocation ArtifactLocation { get; set; }

/// <summary>
/// An array of replacement objects, each of which represents the replacement of a single region in a single artifact specified by 'artifactLocation'.
/// </summary>
[DataMember(Name = "replacements", IsRequired = true)]
[Stj.JsonPropertyName("replacements")]
public virtual IList<Replacement> Replacements { get; set; }

/// <summary>
/// Key/value pairs that provide additional information about the change.
/// </summary>
[DataMember(Name = "properties", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("properties")]
internal override IDictionary<string, SerializedPropertyInfo> Properties { get; set; }

/// <summary>
Expand Down
6 changes: 5 additions & 1 deletion src/Sarif/Autogenerated/ArtifactContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Runtime.Serialization;

using Stj = System.Text.Json.Serialization;
namespace Microsoft.CodeAnalysis.Sarif
{
/// <summary>
Expand Down Expand Up @@ -37,24 +37,28 @@ public virtual SarifNodeKind SarifNodeKind
/// UTF-8-encoded content from a text artifact.
/// </summary>
[DataMember(Name = "text", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("text")]
public virtual string Text { get; set; }

/// <summary>
/// MIME Base64-encoded content from a binary artifact, or from a text artifact in its original encoding.
/// </summary>
[DataMember(Name = "binary", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("binary")]
public virtual string Binary { get; set; }

/// <summary>
/// An alternate rendered representation of the artifact (e.g., a decompiled representation of a binary region).
/// </summary>
[DataMember(Name = "rendered", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("rendered")]
public virtual MultiformatMessageString Rendered { get; set; }

/// <summary>
/// Key/value pairs that provide additional information about the artifact content.
/// </summary>
[DataMember(Name = "properties", IsRequired = false, EmitDefaultValue = false)]
[Stj.JsonPropertyName("properties")]
internal override IDictionary<string, SerializedPropertyInfo> Properties { get; set; }

/// <summary>
Expand Down
Loading
Loading