From 5b0081f6bece6fd4cd25254e967d98c81b5ae6e5 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 7 Jan 2022 15:09:38 -0800 Subject: [PATCH] Fix NETStandard library using JSON source gen NETStandard libraries using JSON source gen would fail to load on .NETCore due to missing IsExternalInit in the assembly. On .NETCore this is defined, whereas on .NETStandard JSON carries an internal copy. The compiler emits references to the internal type when a NETStandard library calls init-only setters in JSON types, but these references are not satisfied when the library runs on .NETCore. Fix this by adding a type forward to JSON for the IsExternalInit type. --- ...ystem.Text.Json.Typeforwards.netcoreapp.cs | 6 ++++ .../ref/System.Text.Json.csproj | 5 +++ ...ystem.Text.Json.Typeforwards.netcoreapp.cs | 6 ++++ .../src/System.Text.Json.csproj | 1 + ...em.Text.Json.TestLibrary.Roslyn3.11.csproj | 8 +++++ ...tem.Text.Json.TestLibrary.Roslyn4.0.csproj | 8 +++++ .../System.Text.Json.TestLibrary.targets | 17 ++++++++++ .../TestClasses.cs | 17 ++++++++++ .../NETStandardContextTests.cs | 31 +++++++++++++++++++ ...n.SourceGeneration.Roslyn3.11.Tests.csproj | 1 + ...on.SourceGeneration.Roslyn4.0.Tests.csproj | 1 + ...m.Text.Json.SourceGeneration.Tests.targets | 1 + 12 files changed, 102 insertions(+) create mode 100644 src/libraries/System.Text.Json/ref/System.Text.Json.Typeforwards.netcoreapp.cs create mode 100644 src/libraries/System.Text.Json/src/System.Text.Json.Typeforwards.netcoreapp.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn3.11.csproj create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn4.0.csproj create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.targets create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/TestClasses.cs create mode 100644 src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/NETStandardContextTests.cs diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.Typeforwards.netcoreapp.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.Typeforwards.netcoreapp.cs new file mode 100644 index 0000000000000..81030616268c3 --- /dev/null +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.Typeforwards.netcoreapp.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// The compiler emits a reference to the internal copy of this type in our non-NETCoreApp assembly +// so we must include a forward to be compatible with libraries compiled against non-NETCoreApp System.Text.Json +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.CompilerServices.IsExternalInit))] diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.csproj b/src/libraries/System.Text.Json/ref/System.Text.Json.csproj index 6ef8f2ad1f044..aa194d3641829 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.csproj @@ -7,6 +7,11 @@ + + + + + diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.Typeforwards.netcoreapp.cs b/src/libraries/System.Text.Json/src/System.Text.Json.Typeforwards.netcoreapp.cs new file mode 100644 index 0000000000000..81030616268c3 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System.Text.Json.Typeforwards.netcoreapp.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// The compiler emits a reference to the internal copy of this type in our non-NETCoreApp assembly +// so we must include a forward to be compatible with libraries compiled against non-NETCoreApp System.Text.Json +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Runtime.CompilerServices.IsExternalInit))] diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 3092e2e0b7f99..fbd5fbbb9360a 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -312,6 +312,7 @@ System.Text.Json.Utf8JsonReader + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn3.11.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn3.11.csproj new file mode 100644 index 0000000000000..367b28d96d371 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn3.11.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn4.0.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn4.0.csproj new file mode 100644 index 0000000000000..138741ea7741a --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.Roslyn4.0.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.targets new file mode 100644 index 0000000000000..bf1d9e18c74a0 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.targets @@ -0,0 +1,17 @@ + + + netstandard2.0 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/TestClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/TestClasses.cs new file mode 100644 index 0000000000000..a0eebf2e4fd38 --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/TestClasses.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json.Serialization; + +namespace System.Text.Json.SourceGeneration.Tests.NETStandard +{ + public class MyPoco + { + public string Value { get; set; } + } + + [JsonSerializable(typeof(MyPoco))] + public partial class NETStandardSerializerContext : JsonSerializerContext + { + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/NETStandardContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/NETStandardContextTests.cs new file mode 100644 index 0000000000000..557473b603b7d --- /dev/null +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/NETStandardContextTests.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using Xunit; + +namespace System.Text.Json.SourceGeneration.Tests.NETStandard +{ + public class NETStandardContextTests + { + /// + /// Tests that we can serialize and deserialize a type defined in a NETStandard assembly. + /// This tests an issue where we were emitting source-gen logic that caused the compiler + /// to emit a reference to an internal definition of IsExternalInit that was missing + /// on later versions of .NET (since it was defined by the framework). + /// + [Fact] + public void RoundTripNETStandardDefinedSourceGenType() + { + MyPoco expected = new MyPoco() { Value = "Hello from NETStandard type."}; + + string json = JsonSerializer.Serialize(expected, NETStandardSerializerContext.Default.MyPoco); + MyPoco actual = JsonSerializer.Deserialize(json, NETStandardSerializerContext.Default.MyPoco); + Assert.Equal(expected.Value, actual.Value); + } + } +} diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj index 8df19c3e86607..8ac0061d4094b 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj @@ -4,5 +4,6 @@ + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.0.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.0.Tests.csproj index 1afcea0cc5834..82a98d9773af1 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.0.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.0.Tests.csproj @@ -4,5 +4,6 @@ + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets index b16e2ccc8f23e..2f977168e8043 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets @@ -74,6 +74,7 @@ +