From 1db9a3ea9ee162af15cb67df104a3dddd5bd9b46 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 7 Jul 2022 15:37:15 -0400 Subject: [PATCH 1/2] Use file-scoped types in RegexGenerator output --- eng/Versions.props | 2 +- .../gen/RegexGenerator.Emitter.cs | 16 +++++--- .../gen/RegexGenerator.cs | 38 +++++-------------- ...ystem.Text.RegularExpressions.Tests.csproj | 9 ++++- 4 files changed, 30 insertions(+), 35 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 1a8c79e722118d..9332d0618bad53 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -49,7 +49,7 @@ - 4.4.0-1.22328.22 + 4.4.0-1.22356.12 2.0.0-preview.4.22252.4 diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs index d1d5ecbe0b36f7..821bbc95438d6f 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs @@ -26,7 +26,7 @@ namespace System.Text.RegularExpressions.Generator public partial class RegexGenerator { /// Emits the definition of the partial method. This method just delegates to the property cache on the generated Regex-derived type. - private static void EmitRegexPartialMethod(RegexMethod regexMethod, IndentedTextWriter writer, string generatedClassName) + private static void EmitRegexPartialMethod(RegexMethod regexMethod, IndentedTextWriter writer) { // Emit the namespace. RegexType? parent = regexMethod.DeclaringType; @@ -59,7 +59,7 @@ private static void EmitRegexPartialMethod(RegexMethod regexMethod, IndentedText writer.WriteLine("/// "); writer.WriteLine("/// "); writer.WriteLine($"[global::System.CodeDom.Compiler.{s_generatedCodeAttribute}]"); - writer.WriteLine($"{regexMethod.Modifiers} global::System.Text.RegularExpressions.Regex {regexMethod.MethodName}() => global::{GeneratedNamespace}.{generatedClassName}.{regexMethod.GeneratedName}.Instance;"); + writer.WriteLine($"{regexMethod.Modifiers} global::System.Text.RegularExpressions.Regex {regexMethod.MethodName}() => global::{GeneratedNamespace}.{regexMethod.GeneratedName}.Instance;"); // Unwind all scopes while (writer.Indent != 0) @@ -75,7 +75,8 @@ private static void EmitRegexLimitedBoilerplate( { writer.WriteLine($"/// Caches a instance for the {rm.MethodName} method."); writer.WriteLine($"/// A custom Regex-derived type could not be generated because {reason}."); - writer.WriteLine($"internal sealed class {rm.GeneratedName} : Regex"); + writer.WriteLine($"[{s_generatedCodeAttribute}]"); + writer.WriteLine($"file sealed class {rm.GeneratedName} : Regex"); writer.WriteLine($"{{"); writer.WriteLine($" /// Cached, thread-safe singleton instance."); writer.Write($" internal static readonly Regex Instance = "); @@ -94,10 +95,15 @@ private static void EmitRegexLimitedBoilerplate( /// Emits the Regex-derived type for a method whose RunnerFactory implementation was generated into . private static void EmitRegexDerivedImplementation( - IndentedTextWriter writer, RegexMethod rm, string runnerFactoryImplementation) + IndentedTextWriter writer, RegexMethod rm, string runnerFactoryImplementation, bool allowUnsafe) { writer.WriteLine($"/// Custom -derived type for the {rm.MethodName} method."); - writer.WriteLine($"internal sealed class {rm.GeneratedName} : Regex"); + writer.WriteLine($"[{s_generatedCodeAttribute}]"); + if (allowUnsafe) + { + writer.WriteLine($"[SkipLocalsInit]"); + } + writer.WriteLine($"file sealed class {rm.GeneratedName} : Regex"); writer.WriteLine($"{{"); writer.WriteLine($" /// Cached, thread-safe singleton instance."); writer.WriteLine($" internal static readonly {rm.GeneratedName} Instance = new();"); diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs index 388984725f6a3a..5f71b9d4e63d77 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs @@ -1,21 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; [assembly: System.Resources.NeutralResourcesLanguage("en-us")] @@ -79,10 +74,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context) Dictionary requiredHelpers = new(); var sw = new StringWriter(); var writer = new IndentedTextWriter(sw); - writer.Indent += 3; + writer.Indent += 2; writer.WriteLine(); EmitRegexDerivedTypeRunnerFactory(writer, regexMethod, requiredHelpers); - writer.Indent -= 3; + writer.Indent -= 2; return (regexMethod, sw.ToString(), requiredHelpers); }) .Collect(); @@ -138,7 +133,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // used to name them. The boilerplate code generation that happens here is minimal when compared to // the work required to generate the actual matching code for the regex. int id = 0; - string generatedClassName = $"__{ComputeStringHash(compilationDataAndResults.Right.AssemblyName ?? ""):x}"; // To minimize generated code in the event of duplicated regexes, we only emit one derived Regex type per unique // expression/options/timeout. A Dictionary<(expression, options, timeout), RegexMethod> is used to deduplicate, where the value of the @@ -186,17 +180,13 @@ public void Initialize(IncrementalGeneratorInitializationContext context) emittedExpressions.Add(key, regexMethod); } - EmitRegexPartialMethod(regexMethod, writer, generatedClassName); + EmitRegexPartialMethod(regexMethod, writer); writer.WriteLine(); } } // At this point we've emitted all the partial method definitions, but we still need to emit the actual regex-derived implementations. // These are all emitted inside of our generated class. - // TODO https://github.com/dotnet/csharplang/issues/5529: - // When C# provides a mechanism for shielding generated code from the rest of the project, it should be employed - // here for the generated class. At that point, the generated class wrapper can be removed, and all of the types - // generated inside of it (one for each regex as well as the helpers type) should be shielded. writer.WriteLine($"namespace {GeneratedNamespace}"); writer.WriteLine($"{{"); @@ -213,17 +203,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) writer.WriteLine($" using System.Text.RegularExpressions;"); writer.WriteLine($" using System.Threading;"); writer.WriteLine($""); - if (compilationDataAndResults.Right.AllowUnsafe) - { - writer.WriteLine($" [SkipLocalsInit]"); - } - writer.WriteLine($" [{s_generatedCodeAttribute}]"); - writer.WriteLine($" [EditorBrowsable(EditorBrowsableState.Never)]"); - writer.WriteLine($" internal static class {generatedClassName}"); - writer.WriteLine($" {{"); // Emit each Regex-derived type. - writer.Indent += 2; + writer.Indent++; foreach (object? result in results) { if (result is ValueTuple limitedSupportResult) @@ -238,19 +220,20 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { if (!regexImpl.Item1.IsDuplicate) { - EmitRegexDerivedImplementation(writer, regexImpl.Item1, regexImpl.Item2); + EmitRegexDerivedImplementation(writer, regexImpl.Item1, regexImpl.Item2, compilationDataAndResults.Right.AllowUnsafe); writer.WriteLine(); } } } - writer.Indent -= 2; + writer.Indent--; // If any of the Regex-derived types asked for helper methods, emit those now. if (requiredHelpers.Count != 0) { - writer.Indent += 2; + writer.Indent++; writer.WriteLine($"/// Helper methods used by generated -derived implementations."); - writer.WriteLine($"private static class {HelpersTypeName}"); + writer.WriteLine($"[{s_generatedCodeAttribute}]"); + writer.WriteLine($"file static class {HelpersTypeName}"); writer.WriteLine($"{{"); writer.Indent++; bool sawFirst = false; @@ -269,10 +252,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } writer.Indent--; writer.WriteLine($"}}"); - writer.Indent -= 2; + writer.Indent--; } - writer.WriteLine($" }}"); writer.WriteLine($"}}"); // Save out the source diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj index 8461fe54fa618d..6e2853be6e8310 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj @@ -1,4 +1,4 @@ - + true @@ -8,6 +8,13 @@ true true true + + + 4.4.0-1.22356.23 + 4.4.0-1.22356.23 + 4.4.0-1.22356.23 + 4.4.0-1.22356.23 + From 1faae9e9681f9339b7775c004ac7326ec500647f Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 7 Jul 2022 18:06:06 -0400 Subject: [PATCH 2/2] Update System.Text.RegularExpressions.Tests.csproj --- .../System.Text.RegularExpressions.Tests.csproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj index 6e2853be6e8310..bd4295604ecf6e 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj +++ b/src/libraries/System.Text.RegularExpressions/tests/FunctionalTests/System.Text.RegularExpressions.Tests.csproj @@ -9,11 +9,8 @@ true true - + 4.4.0-1.22356.23 - 4.4.0-1.22356.23 - 4.4.0-1.22356.23 - 4.4.0-1.22356.23