Skip to content

Commit 01f63e7

Browse files
authored
Use local variable to evaluate property value only once in JsonSourceGenerator (#87725)
* Use temporary variable to evaluate property value only once * Add test
1 parent da71284 commit 01f63e7

File tree

3 files changed

+42
-1
lines changed

3 files changed

+42
-1
lines changed

src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,18 @@ private void GenerateFastPathFuncForObject(SourceWriter writer, ContextGeneratio
776776
? $"(({propertyGenSpec.DeclaringType.FullyQualifiedName}){ValueVarName})"
777777
: ValueVarName;
778778

779-
string propValueExpr = $"{objectExpr}.{propertyGenSpec.NameSpecifiedInSourceCode}";
779+
string propValueExpr;
780+
if (defaultCheckType != DefaultCheckType.None)
781+
{
782+
// Use temporary variable to evaluate property value only once
783+
string localVariableName = $"__value_{propertyGenSpec.NameSpecifiedInSourceCode}";
784+
writer.WriteLine($"{propertyGenSpec.PropertyType.FullyQualifiedName} {localVariableName} = {objectExpr}.{propertyGenSpec.NameSpecifiedInSourceCode};");
785+
propValueExpr = localVariableName;
786+
}
787+
else
788+
{
789+
propValueExpr = $"{objectExpr}.{propertyGenSpec.NameSpecifiedInSourceCode}";
790+
}
780791

781792
switch (defaultCheckType)
782793
{

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ public class CustomWrappingResolver<T> : IJsonTypeInfoResolver
535535

536536
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)]
537537
[JsonSerializable(typeof(JsonMessage))]
538+
[JsonSerializable(typeof(AllocatingOnPropertyAccess))]
538539
public partial class FastPathSerializationContext : JsonSerializerContext
539540
{ }
540541

@@ -816,5 +817,22 @@ public TestResolver(Func<Type, JsonSerializerOptions, JsonTypeInfo?> getTypeInfo
816817

817818
public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options) => _getTypeInfo(type, options);
818819
}
820+
821+
[Fact]
822+
public static void FastPathSerialization_EvaluatePropertyOnlyOnceWhenIgnoreNullOrDefaultIsSpecified()
823+
{
824+
JsonSerializerOptions options = FastPathSerializationContext.Default.Options;
825+
JsonTypeInfo<AllocatingOnPropertyAccess> allocatingOnPropertyAccessInfo = (JsonTypeInfo<AllocatingOnPropertyAccess>)options.GetTypeInfo(typeof(AllocatingOnPropertyAccess));
826+
Assert.NotNull(allocatingOnPropertyAccessInfo.SerializeHandler);
827+
828+
var value = new AllocatingOnPropertyAccess();
829+
Assert.Equal(0, value.WhenWritingNullAccessCounter);
830+
Assert.Equal(0, value.WhenWritingDefaultAccessCounter);
831+
832+
string expectedJson = """{"SomeAllocatingProperty":"Current Value: 1","SomeAllocatingProperty2":"Current Value: 1"}""";
833+
Assert.Equal(expectedJson, JsonSerializer.Serialize(value, options));
834+
Assert.Equal(1, value.WhenWritingNullAccessCounter);
835+
Assert.Equal(1, value.WhenWritingDefaultAccessCounter);
836+
}
819837
}
820838
}

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,18 @@ public class JsonMessage
154154
public int Length => Message?.Length ?? 0; // Read-only property
155155
}
156156

157+
public class AllocatingOnPropertyAccess
158+
{
159+
public int WhenWritingNullAccessCounter = 0;
160+
public int WhenWritingDefaultAccessCounter = 0;
161+
162+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
163+
public string SomeAllocatingProperty => $"Current Value: {++WhenWritingNullAccessCounter}";
164+
165+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
166+
public string SomeAllocatingProperty2 => $"Current Value: {++WhenWritingDefaultAccessCounter}";
167+
}
168+
157169
internal struct MyStruct { }
158170

159171
public struct PersonStruct

0 commit comments

Comments
 (0)