From be5e620bdffb79676e34aa7aaa707670b9fd38d5 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 27 Aug 2024 20:52:05 -0700 Subject: [PATCH 1/3] tuple support in net461 --- .../Basic.Reference.Assemblies.Net461.csproj | 1 + .../Generated.cs | 30 +++++++++++++++++++ .../Generated.targets | 4 +++ Src/Generate/Program.cs | 5 +++- 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/Src/Basic.Reference.Assemblies.Net461/Basic.Reference.Assemblies.Net461.csproj b/Src/Basic.Reference.Assemblies.Net461/Basic.Reference.Assemblies.Net461.csproj index 9ef9ece..e0386b9 100644 --- a/Src/Basic.Reference.Assemblies.Net461/Basic.Reference.Assemblies.Net461.csproj +++ b/Src/Basic.Reference.Assemblies.Net461/Basic.Reference.Assemblies.Net461.csproj @@ -8,6 +8,7 @@ + diff --git a/Src/Basic.Reference.Assemblies.Net461/Generated.cs b/Src/Basic.Reference.Assemblies.Net461/Generated.cs index 8668cb1..323197c 100644 --- a/Src/Basic.Reference.Assemblies.Net461/Generated.cs +++ b/Src/Basic.Reference.Assemblies.Net461/Generated.cs @@ -4476,6 +4476,11 @@ public static class ExtraReferenceInfos /// The for System.Threading.Tasks.Extensions.dll /// public static ReferenceInfo SystemThreadingTasksExtensions => new ReferenceInfo("System.Threading.Tasks.Extensions.dll", Resources.SystemThreadingTasksExtensions, Net461.ExtraReferences.SystemThreadingTasksExtensions, global::System.Guid.Parse("bc890e4e-a34f-463c-8fd9-60f43c8beb88")); + + /// + /// The for System.ValueTuple.dll + /// + public static ReferenceInfo SystemValueTuple => new ReferenceInfo("System.ValueTuple.dll", Resources.SystemValueTuple, Net461.ExtraReferences.SystemValueTuple, global::System.Guid.Parse("1aa5ee86-d143-43bd-94ec-c0749ff98222")); private static ImmutableArray _all; public static ImmutableArray All { @@ -4486,6 +4491,7 @@ public static ImmutableArray All _all = [ SystemThreadingTasksExtensions, + SystemValueTuple, ]; } return _all; @@ -4517,6 +4523,23 @@ public static PortableExecutableReference SystemThreadingTasksExtensions } } + private static PortableExecutableReference? _SystemValueTuple; + + /// + /// The for System.ValueTuple.dll + /// + public static PortableExecutableReference SystemValueTuple + { + get + { + if (_SystemValueTuple is null) + { + _SystemValueTuple = AssemblyMetadata.CreateFromImage(Resources.SystemValueTuple).GetReference(filePath: "System.ValueTuple.dll", display: "System.ValueTuple (net461)"); + } + return _SystemValueTuple; + } + } + private static ImmutableArray _all; public static ImmutableArray All { @@ -4527,6 +4550,7 @@ public static ImmutableArray All _all = [ SystemThreadingTasksExtensions, + SystemValueTuple, ]; } return _all; @@ -5649,6 +5673,12 @@ public static class Resources public static byte[] SystemThreadingTasksExtensions => ResourceLoader.GetOrCreateResource(ref _SystemThreadingTasksExtensions, "net461.System.Threading.Tasks.Extensions"); private static byte[]? _SystemThreadingTasksExtensions; + /// + /// The image bytes for System.ValueTuple.dll + /// + public static byte[] SystemValueTuple => ResourceLoader.GetOrCreateResource(ref _SystemValueTuple, "net461.System.ValueTuple"); + private static byte[]? _SystemValueTuple; + } } diff --git a/Src/Basic.Reference.Assemblies.Net461/Generated.targets b/Src/Basic.Reference.Assemblies.Net461/Generated.targets index 40fd5f5..ef17a87 100644 --- a/Src/Basic.Reference.Assemblies.Net461/Generated.targets +++ b/Src/Basic.Reference.Assemblies.Net461/Generated.targets @@ -740,5 +740,9 @@ net461.System.Threading.Tasks.Extensions Resources\net461\System.Threading.Tasks.Extensions.dll + + net461.System.ValueTuple + Resources\net461\System.ValueTuple.dll + diff --git a/Src/Generate/Program.cs b/Src/Generate/Program.cs index 077a67b..be25fa6 100644 --- a/Src/Generate/Program.cs +++ b/Src/Generate/Program.cs @@ -166,7 +166,10 @@ void Net461() var content = GetGeneratedContent( "Net461", [@"microsoft.netframework.referenceassemblies.net461\1.0.3\build\.NETFramework\v4.6.1"], - [@"system.threading.tasks.extensions\4.5.4\lib\net461"]); + [ + @"system.threading.tasks.extensions\4.5.4\lib\net461", + @"system.valuetuple\4.5.0\lib\net461" + ]); var targetDir = Path.Combine(srcPath, "Basic.Reference.Assemblies.Net461"); File.WriteAllText(Path.Combine(targetDir, "Generated.cs"), content.CodeContent, encoding); File.WriteAllText(Path.Combine(targetDir, "Generated.targets"), content.TargetsContent, encoding); From 42dbd7866500b33ec49342eac6f2b1271b0154e1 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 27 Aug 2024 21:14:46 -0700 Subject: [PATCH 2/3] ability to run as a sanity test --- .github/workflows/dotnet.yml | 2 +- ...asic.Reference.Assemblies.UnitTests.csproj | 2 +- .../CompilationUtil.cs | 82 +++++++++++++++++++ .../Extensions.cs | 3 + .../SanityUnitTests.cs | 17 ++++ 5 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 1d7971b..139cf26 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -29,7 +29,7 @@ jobs: run: dotnet pack -p:PackageOutputPath="${GITHUB_WORKSPACE}/packages" -p:IncludeSymbols=false -p:RepositoryCommit=${GITHUB_SHA} -p:PackageVersion="0.0.0.1" - name: Test - run: dotnet test --no-build --verbosity normal + run: dotnet test --framework net8.0 --no-build --verbosity normal - name: Upload Binary Log uses: actions/upload-artifact@v3 diff --git a/Src/Basic.Reference.Assemblies.UnitTests/Basic.Reference.Assemblies.UnitTests.csproj b/Src/Basic.Reference.Assemblies.UnitTests/Basic.Reference.Assemblies.UnitTests.csproj index 47b5d0f..1d88aa5 100644 --- a/Src/Basic.Reference.Assemblies.UnitTests/Basic.Reference.Assemblies.UnitTests.csproj +++ b/Src/Basic.Reference.Assemblies.UnitTests/Basic.Reference.Assemblies.UnitTests.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0;net472 false LatestMajor diff --git a/Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs b/Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs new file mode 100644 index 0000000..a2198b7 --- /dev/null +++ b/Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs @@ -0,0 +1,82 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Basic.Reference.Assemblies.UnitTests; +internal static class CompilationUtil +{ + public static MemoryStream CompileToLibrary(string code, string assemblyName) + { + var references = +#if NET + Net80.References.All; +#else + Net461.References.All; +#endif + var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + var compilation = CSharpCompilation.Create( + assemblyName, + [CSharpSyntaxTree.ParseText(code)], + references, + options); + + var peStream = new MemoryStream(); + var emitResult = compilation.Emit(peStream); + if (!emitResult.Success) + { + throw new Exception(GetMessage(emitResult.Diagnostics)); + } + + peStream.Position = 0; + return peStream; + + static string GetMessage(IEnumerable diagnostics) + { + var builder = new StringBuilder(); + builder.AppendLine("Compilation failed with the following errors:"); + foreach (var d in diagnostics) + { + builder.AppendLine(d.ToString()); + } + return builder.ToString(); + } + } + + public static Assembly CompileToLibraryAndLoad(string code, string assemblyName) + { + var stream = CompileToLibrary(code, assemblyName); + return Load(stream, assemblyName); + } + + /// + /// Compile and run the code expecting to find a static Lib.Go method + /// + public static string? CompileAndRun(string code, string assemblyName) + { + var assembly = CompileToLibraryAndLoad(code, assemblyName); + var libType = assembly + .GetTypes() + .Where(x => x.Name == "Lib") + .Single(); + var method = libType.GetMethod("Go", BindingFlags.Static | BindingFlags.NonPublic); + var obj = method!.Invoke(null, null); + return (string?)obj; + } + + public static Assembly Load(MemoryStream stream, string assemblyName) + { + stream.Position = 0; +#if NET + var alc = new System.Runtime.Loader.AssemblyLoadContext(assemblyName); + return alc.LoadFromStream(stream); +#else + return Assembly.Load(stream.ToArray()); +#endif + } +} diff --git a/Src/Basic.Reference.Assemblies.UnitTests/Extensions.cs b/Src/Basic.Reference.Assemblies.UnitTests/Extensions.cs index dd5861a..e4f8518 100644 --- a/Src/Basic.Reference.Assemblies.UnitTests/Extensions.cs +++ b/Src/Basic.Reference.Assemblies.UnitTests/Extensions.cs @@ -1,7 +1,10 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; diff --git a/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs b/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs index ea551b2..75a3008 100644 --- a/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs +++ b/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs @@ -176,4 +176,21 @@ static void Main() Assert.True(emitResult.Success); Assert.Empty(emitResult.Diagnostics); } + + [Fact] + public void RunTuple() + { + var source = """ + static class Lib + { + public static string Go() + { + var tuple = (1, 2); + return tuple.ToString(); + } + } + """; + var actual = CompilationUtil.CompileAndRun(source, nameof(RunTuple)); + Assert.Equal("(1, 2)", actual); + } } From 269df570d6b63f261c9be9ac179902b78a4652a1 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 27 Aug 2024 21:23:56 -0700 Subject: [PATCH 3/3] fixed the test --- .../CompilationUtil.cs | 19 +++++++------------ .../SanityUnitTests.cs | 12 +++++++++++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs b/Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs index a2198b7..79be56c 100644 --- a/Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs +++ b/Src/Basic.Reference.Assemblies.UnitTests/CompilationUtil.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Operations; using System; using System.Collections.Generic; using System.IO; @@ -11,14 +12,8 @@ namespace Basic.Reference.Assemblies.UnitTests; internal static class CompilationUtil { - public static MemoryStream CompileToLibrary(string code, string assemblyName) + public static MemoryStream CompileToLibrary(string code, string assemblyName, IEnumerable references) { - var references = -#if NET - Net80.References.All; -#else - Net461.References.All; -#endif var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); var compilation = CSharpCompilation.Create( assemblyName, @@ -48,23 +43,23 @@ static string GetMessage(IEnumerable diagnostics) } } - public static Assembly CompileToLibraryAndLoad(string code, string assemblyName) + public static Assembly CompileToLibraryAndLoad(string code, string assemblyName, IEnumerable references) { - var stream = CompileToLibrary(code, assemblyName); + var stream = CompileToLibrary(code, assemblyName, references); return Load(stream, assemblyName); } /// /// Compile and run the code expecting to find a static Lib.Go method /// - public static string? CompileAndRun(string code, string assemblyName) + public static string? CompileAndRun(string code, string assemblyName, IEnumerable references) { - var assembly = CompileToLibraryAndLoad(code, assemblyName); + var assembly = CompileToLibraryAndLoad(code, assemblyName, references); var libType = assembly .GetTypes() .Where(x => x.Name == "Lib") .Single(); - var method = libType.GetMethod("Go", BindingFlags.Static | BindingFlags.NonPublic); + var method = libType.GetMethod("Go", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); var obj = method!.Invoke(null, null); return (string?)obj; } diff --git a/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs b/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs index 75a3008..438644c 100644 --- a/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs +++ b/Src/Basic.Reference.Assemblies.UnitTests/SanityUnitTests.cs @@ -13,6 +13,12 @@ namespace Basic.Reference.Assemblies.UnitTests; public class SanityUnitTests(ITestOutputHelper outputHelper) { public ITestOutputHelper TestOutputHelper { get; } = outputHelper; + public bool IsCoreClr => +#if NET + true; +#else + false; +#endif [Theory] [MemberData(nameof(TestData.ApplicationReferences), MemberType = typeof(TestData))] @@ -190,7 +196,11 @@ public static string Go() } } """; - var actual = CompilationUtil.CompileAndRun(source, nameof(RunTuple)); + + var references = IsCoreClr + ? Net80.References.All + : [.. Net461.References.All, .. Net461.ExtraReferences.All]; + var actual = CompilationUtil.CompileAndRun(source, nameof(RunTuple), references); Assert.Equal("(1, 2)", actual); } }