From 03d967e83d572a29540af8f1ff12f651fab41a06 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Thu, 30 Sep 2021 18:11:51 -0400 Subject: [PATCH] Obsolete Regex.CompileToAssembly (#59734) * Obsolete Regex.CompileToAssembly It's never worked on .NET Core and for .NET 7 we'll now have the regex source generator. * Remove argument validation from CompileToAssembly Per PR feedback --- .../Common/src/System/Obsoletions.cs | 3 + src/libraries/Directory.Build.targets | 3 +- .../ref/System.Text.RegularExpressions.cs | 4 + .../src/Resources/Strings.resx | 2 +- .../src/System.Text.RegularExpressions.csproj | 6 +- .../System/Text/RegularExpressions/Regex.cs | 21 +- .../RegexAssemblyCompiler.cs | 271 ------------------ .../RegexCompilationInfo.cs | 1 + .../Text/RegularExpressions/RegexCompiler.cs | 49 +--- .../RegularExpressions/RegexLWCGCompiler.cs | 2 +- .../tests/Regex.CompileToAssembly.Tests.cs | 117 ++------ ...ystem.Text.RegularExpressions.Tests.csproj | 3 +- 12 files changed, 44 insertions(+), 438 deletions(-) delete mode 100644 src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs diff --git a/src/libraries/Common/src/System/Obsoletions.cs b/src/libraries/Common/src/System/Obsoletions.cs index 78cecfdbfb231..10fab423b8e32 100644 --- a/src/libraries/Common/src/System/Obsoletions.cs +++ b/src/libraries/Common/src/System/Obsoletions.cs @@ -116,5 +116,8 @@ internal static class Obsoletions internal const string SignerInfoCounterSigMessage = "ComputeCounterSignature without specifying a CmsSigner is obsolete and is not supported. Use the overload that accepts a CmsSigner."; internal const string SignerInfoCounterSigDiagId = "SYSLIB0035"; + + internal const string RegexCompileToAssemblyMessage = "Regex.CompileToAssembly is obsolete and not supported. Use RegexGeneratorAttribute with the regular expression source generator instead."; + internal const string RegexCompileToAssemblyDiagId = "SYSLIB0036"; } } diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 5d1d8c2d3e42d..1a4e24c2223ef 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -33,8 +33,9 @@ SYSLIB0023: RNGCryptoServiceProvider. SYSLIB0025: SuppressIldasmAttribute. SYSLIB0032: HandleProcessCorruptedStateExceptionsAttribute. + SYSLIB0036: Regex.CompileToAssembly --> - $(NoWarn);SYSLIB0003;SYSLIB0004;SYSLIB0015;SYSLIB0017;SYSLIB0021;SYSLIB0022;SYSLIB0023;SYSLIB0025;SYSLIB0032 + $(NoWarn);SYSLIB0003;SYSLIB0004;SYSLIB0015;SYSLIB0017;SYSLIB0021;SYSLIB0022;SYSLIB0023;SYSLIB0025;SYSLIB0032;SYSLIB0036 This operation is only allowed once per object. - This platform does not support writing compiled regular expressions to an assembly. + This platform does not support writing compiled regular expressions to an assembly. Use RegexGeneratorAttribute with the regular expression source generator instead. Quantifier {x,y} following nothing. diff --git a/src/libraries/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj b/src/libraries/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj index 0294128f3eb62..a8f8b7bd4f69f 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj +++ b/src/libraries/System.Text.RegularExpressions/src/System.Text.RegularExpressions.csproj @@ -21,7 +21,6 @@ - @@ -48,10 +47,11 @@ - - + + + diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs index 7395d6f54e999..69130ec6c0346 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs @@ -199,30 +199,17 @@ protected IDictionary? CapNames private static RegexRunnerFactory Compile(string pattern, RegexCode code, RegexOptions options, bool hasTimeout) => RegexCompiler.Compile(pattern, code, options, hasTimeout); + [Obsolete(Obsoletions.RegexCompileToAssemblyMessage, DiagnosticId = Obsoletions.RegexCompileToAssemblyDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname) => CompileToAssembly(regexinfos, assemblyname, null, null); + [Obsolete(Obsoletions.RegexCompileToAssemblyMessage, DiagnosticId = Obsoletions.RegexCompileToAssemblyDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname, CustomAttributeBuilder[]? attributes) => CompileToAssembly(regexinfos, assemblyname, attributes, null); - public static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname, CustomAttributeBuilder[]? attributes, string? resourceFile) - { - if (assemblyname is null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.assemblyname); - } - - if (regexinfos is null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.regexinfos); - } - -#if DEBUG // until it can be fully implemented - RegexCompiler.CompileToAssembly(regexinfos, assemblyname, attributes, resourceFile); -#else + [Obsolete(Obsoletions.RegexCompileToAssemblyMessage, DiagnosticId = Obsoletions.RegexCompileToAssemblyDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] + public static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname, CustomAttributeBuilder[]? attributes, string? resourceFile) => throw new PlatformNotSupportedException(SR.PlatformNotSupported_CompileToAssembly); -#endif - } /// /// Escapes a minimal set of metacharacters (\, *, +, ?, |, {, [, (, ), ^, $, ., #, and diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs deleted file mode 100644 index 8b35c6e1a0881..0000000000000 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexAssemblyCompiler.cs +++ /dev/null @@ -1,271 +0,0 @@ -// 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; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Reflection.Emit; -using System.Threading; - -#if DEBUG // until it can be fully implemented -namespace System.Text.RegularExpressions -{ - /// Compiles a Regex to an assembly that can be saved to disk. - internal sealed class RegexAssemblyCompiler : RegexCompiler - { - /// Type count used to augment generated type names to create unique names. - private static int s_typeCount; - - private AssemblyBuilder _assembly; - private ModuleBuilder _module; - - internal RegexAssemblyCompiler(AssemblyName an, CustomAttributeBuilder[]? attribs, string? resourceFile) : - base(persistsAssembly: true) - { - if (resourceFile != null) - { - // Unmanaged resources are not supported: _assembly.DefineUnmanagedResource(resourceFile); - throw new PlatformNotSupportedException(); - } - - _assembly = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); // TODO https://github.com/dotnet/runtime/issues/30153: AssemblyBuilderAccess.Save - _module = _assembly.DefineDynamicModule(an.Name + ".dll"); - if (attribs != null) - { - foreach (CustomAttributeBuilder attr in attribs) - { - _assembly.SetCustomAttribute(attr); - } - } - } - - internal void GenerateRegexType(string pattern, RegexOptions options, string name, bool isPublic, RegexCode code, TimeSpan matchTimeout) - { - // Store arguments into the base type's fields - _options = options; - _code = code; - _codes = code.Codes; - _strings = code.Strings; - _leadingCharClasses = code.LeadingCharClasses; - _boyerMoorePrefix = code.BoyerMoorePrefix; - _leadingAnchor = code.LeadingAnchor; - _trackcount = code.TrackCount; - - // Pick a name for the class. - string typenumString = ((uint)Interlocked.Increment(ref s_typeCount)).ToString(); - - // Generate the RegexRunner-derived type. - TypeBuilder regexRunnerTypeBuilder = DefineType(_module, $"{name}Runner{typenumString}", isPublic: false, isSealed: true, typeof(RegexRunner)); - _ilg = DefineMethod(regexRunnerTypeBuilder, "Go", null); - GenerateGo(); - _ilg = DefineMethod(regexRunnerTypeBuilder, "FindFirstChar", typeof(bool)); - GenerateFindFirstChar(); - _ilg = DefineMethod(regexRunnerTypeBuilder, "InitTrackCount", null); - GenerateInitTrackCount(); - Type runnerType = regexRunnerTypeBuilder.CreateType()!; - - // Generate the RegexRunnerFactory-derived type. - TypeBuilder regexRunnerFactoryTypeBuilder = DefineType(_module, $"{name}Factory{typenumString}", isPublic: false, isSealed: true, typeof(RegexRunnerFactory)); - _ilg = DefineMethod(regexRunnerFactoryTypeBuilder, "CreateInstance", typeof(RegexRunner)); - GenerateCreateInstance(runnerType); - Type regexRunnerFactoryType = regexRunnerFactoryTypeBuilder.CreateType()!; - - // Generate the Regex-derived type. - TypeBuilder regexTypeBuilder = DefineType(_module, name, isPublic, isSealed: false, typeof(Regex)); - ConstructorBuilder defaultCtorBuilder = regexTypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, Type.EmptyTypes); - _ilg = defaultCtorBuilder.GetILGenerator(); - GenerateRegexDefaultCtor(pattern, options, regexRunnerFactoryType, code, matchTimeout); - if (matchTimeout != Regex.InfiniteMatchTimeout) - { - // We only generate a constructor with a timeout parameter if the regex information supplied has a non-infinite timeout. - // If it has an infinite timeout, then the generated code is not going to respect the timeout. This is a difference from netfx, - // due to the fact that we now special-case an infinite timeout in the code generator to avoid spitting unnecessary code - // and paying for the checks at run time. - _ilg = regexTypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(TimeSpan) }).GetILGenerator(); - GenerateRegexTimeoutCtor(defaultCtorBuilder, regexTypeBuilder); - } - regexTypeBuilder.CreateType(); - } - - private void GenerateInitTrackCount() - { - // this.runtrackcount = _trackcount; - // return; - Ldthis(); - Ldc(_trackcount); - Stfld(s_runtrackcountField); - Ret(); - } - - /// Generates a very simple factory method. - private void GenerateCreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) - { - // return new Type(); - Newobj(type.GetConstructor(Type.EmptyTypes)!); - Ret(); - } - - private void GenerateRegexDefaultCtor( - string pattern, - RegexOptions options, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type regexRunnerFactoryType, - RegexCode code, - TimeSpan matchTimeout) - { - // Call the base ctor and store pattern, options, and factory. - // base.ctor(); - // base.pattern = pattern; - // base.options = options; - // base.factory = new DerivedRegexRunnerFactory(); - Ldthis(); - _ilg!.Emit(OpCodes.Call, typeof(Regex).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, Array.Empty())!); - Ldthis(); - Ldstr(pattern); - Stfld(RegexField(nameof(Regex.pattern))); - Ldthis(); - Ldc((int)options); - Stfld(RegexField(nameof(Regex.roptions))); - Ldthis(); - Newobj(regexRunnerFactoryType.GetConstructor(Type.EmptyTypes)!); - Stfld(RegexField(nameof(Regex.factory))); - - // Store the timeout (no need to validate as it should have happened in RegexCompilationInfo) - if (matchTimeout == Regex.InfiniteMatchTimeout) - { - // base.internalMatchTimeout = Regex.InfiniteMatchTimeout; - _ilg.Emit(OpCodes.Ldsfld, RegexField(nameof(Regex.InfiniteMatchTimeout))); - } - else - { - // base.internalMatchTimeout = TimeSpan.FromTick(matchTimeout.Ticks); - Ldthis(); - LdcI8(matchTimeout.Ticks); - Call(typeof(TimeSpan).GetMethod(nameof(TimeSpan.FromTicks), BindingFlags.Public | BindingFlags.Static)!); - } - Stfld(RegexField(nameof(Regex.internalMatchTimeout))); - - // Set capsize, caps, capnames, capslist. - Ldthis(); - Ldc(code.CapSize); - Stfld(RegexField(nameof(Regex.capsize))); - if (code.Caps != null) - { - // Caps = new Hashtable {{0, 0}, {1, 1}, ... }; - GenerateCreateHashtable(RegexField(nameof(Regex.caps)), code.Caps); - } - if (code.Tree.CapNames != null) - { - // CapNames = new Hashtable {{"0", 0}, {"1", 1}, ...}; - GenerateCreateHashtable(RegexField(nameof(Regex.capnames)), code.Tree.CapNames); - } - if (code.Tree.CapsList != null) - { - // capslist = new string[...]; - // capslist[0] = "0"; - // capslist[1] = "1"; - // ... - Ldthis(); - Ldc(code.Tree.CapsList.Length); - _ilg.Emit(OpCodes.Newarr, typeof(string)); // create new string array - FieldInfo capslistField = RegexField(nameof(Regex.capslist)); - Stfld(capslistField); - for (int i = 0; i < code.Tree.CapsList.Length; i++) - { - Ldthisfld(capslistField); - Ldc(i); - Ldstr(code.Tree.CapsList[i]); - _ilg.Emit(OpCodes.Stelem_Ref); - } - } - - // InitializeReferences(); - // return; - Ldthis(); - Call(typeof(Regex).GetMethod("InitializeReferences", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!); - Ret(); - } - - private void GenerateRegexTimeoutCtor(ConstructorBuilder defaultCtorBuilder, TypeBuilder regexTypeBuilder) - { - // base.ctor(); - // ValidateMatchTimeout(timeSpan); - // base.internalMatchTimeout = timeSpan; - Ldthis(); - _ilg!.Emit(OpCodes.Call, defaultCtorBuilder); - _ilg.Emit(OpCodes.Ldarg_1); - Call(typeof(Regex).GetMethod(nameof(Regex.ValidateMatchTimeout), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)!); - Ldthis(); - _ilg.Emit(OpCodes.Ldarg_1); - Stfld(RegexField(nameof(Regex.internalMatchTimeout))); - Ret(); - } - - internal void GenerateCreateHashtable(FieldInfo field, Hashtable ht) - { - // hashtable = new Hashtable(); - Ldthis(); - Newobj(typeof(Hashtable).GetConstructor(Type.EmptyTypes)!); - Stfld(field); - - // hashtable.Add(key1, value1); - // hashtable.Add(key2, value2); - // ... - MethodInfo addMethod = typeof(Hashtable).GetMethod(nameof(Hashtable.Add), BindingFlags.Public | BindingFlags.Instance)!; - IDictionaryEnumerator en = ht.GetEnumerator(); - while (en.MoveNext()) - { - Ldthisfld(field); - - if (en.Key is int key) - { - Ldc(key); - _ilg!.Emit(OpCodes.Box, typeof(int)); - } - else - { - Ldstr((string)en.Key); - } - - Ldc((int)en.Value!); - _ilg!.Emit(OpCodes.Box, typeof(int)); - Callvirt(addMethod); - } - } - - /// Gets the named instance field from the Regex type. - private static FieldInfo RegexField(string fieldname) => - typeof(Regex).GetField(fieldname, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)!; - - /// Saves the assembly to a file in the current directory based on the assembly's name. - internal void Save() - { - // Save the assembly to the current directory. - string fileName = _assembly.GetName().Name + ".dll"; - - // TODO https://github.com/dotnet/runtime/issues/30153: _assembly.Save(fileName) - throw new PlatformNotSupportedException(SR.PlatformNotSupported_CompileToAssembly); - } - - /// Begins the definition of a new type with a specified base class - private static TypeBuilder DefineType( - ModuleBuilder moduleBuilder, - string typeName, - bool isPublic, - bool isSealed, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type inheritFromClass) - { - TypeAttributes attrs = TypeAttributes.Class | TypeAttributes.BeforeFieldInit | (isPublic ? TypeAttributes.Public : TypeAttributes.NotPublic); - if (isSealed) - { - attrs |= TypeAttributes.Sealed; - } - - return moduleBuilder.DefineType(typeName, attrs, inheritFromClass); - } - - /// Begins the definition of a new method (no args) with a specified return value. - private static ILGenerator DefineMethod(TypeBuilder typeBuilder, string methname, Type? returnType) => - typeBuilder.DefineMethod(methname, MethodAttributes.Family | MethodAttributes.Virtual, returnType, null).GetILGenerator(); - } -} -#endif diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs index 4383e37e5880d..850846cf288d3 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs @@ -5,6 +5,7 @@ namespace System.Text.RegularExpressions { + [Obsolete(Obsoletions.RegexCompileToAssemblyMessage, DiagnosticId = Obsoletions.RegexCompileToAssemblyDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] public class RegexCompilationInfo { private string _pattern; diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs index d2e72d46a6fe0..fabb0020108dd 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs @@ -70,8 +70,6 @@ internal abstract class RegexCompiler private static readonly MethodInfo s_textInfoToLowerMethod = typeof(TextInfo).GetMethod("ToLower", new Type[] { typeof(char) })!; protected ILGenerator? _ilg; - /// true if the compiled code is saved for later use, potentially on a different machine. - private readonly bool _persistsAssembly; // tokens representing local variables private LocalBuilder? _runtextbegLocal; @@ -123,11 +121,6 @@ internal abstract class RegexCompiler private const int Uniquecount = 10; private const int LoopTimeoutCheckCount = 2048; // A conservative value to guarantee the correct timeout handling. - protected RegexCompiler(bool persistsAssembly) - { - _persistsAssembly = persistsAssembly; - } - private static FieldInfo RegexRunnerField(string fieldname) => typeof(RegexRunner).GetField(fieldname, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)!; private static MethodInfo RegexRunnerMethod(string methname) => typeof(RegexRunner).GetMethod(methname, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)!; @@ -139,40 +132,6 @@ protected RegexCompiler(bool persistsAssembly) internal static RegexRunnerFactory Compile(string pattern, RegexCode code, RegexOptions options, bool hasTimeout) => new RegexLWCGCompiler().FactoryInstanceFromCode(pattern, code, options, hasTimeout); -#if DEBUG // until it can be fully implemented - /// - /// Entry point to dynamically compile a regular expression. The expression is compiled to - /// an assembly saved to a file. - /// - internal static void CompileToAssembly(RegexCompilationInfo[] regexinfos, AssemblyName assemblyname, CustomAttributeBuilder[]? attributes, string? resourceFile) - { - var c = new RegexAssemblyCompiler(assemblyname, attributes, resourceFile); - - for (int i = 0; i < regexinfos.Length; i++) - { - if (regexinfos[i] is null) - { - throw new ArgumentNullException(nameof(regexinfos), SR.ArgumentNull_ArrayWithNullElements); - } - - string pattern = regexinfos[i].Pattern; - - RegexOptions options = regexinfos[i].Options | RegexOptions.Compiled; // ensure compiled is set; it enables more optimization specific to compilation - - string fullname = regexinfos[i].Namespace.Length == 0 ? - regexinfos[i].Name : - regexinfos[i].Namespace + "." + regexinfos[i].Name; - - RegexTree tree = RegexParser.Parse(pattern, options, (options & RegexOptions.CultureInvariant) != 0 ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture); - RegexCode code = RegexWriter.Write(tree); - - c.GenerateRegexType(pattern, options, fullname, regexinfos[i].IsPublic, code, regexinfos[i].MatchTimeout); - } - - c.Save(); - } -#endif - /// /// Keeps track of an operation that needs to be referenced in the backtrack-jump /// switch table, and that needs backtracking code to be emitted (if flags != 0) @@ -2523,11 +2482,11 @@ void EmitMultiChar(RegexNode node, bool emitLengthCheck = true) // If we're doing a case-insensitive comparison, we need to lower case each character, // so we just go character-by-character. But if we're not, we try to process multiple // characters at a time; this is helpful not only for throughput but also in reducing - // the amount of IL and asm that results from this unrolling. Also, this optimization + // the amount of IL and asm that results from this unrolling. This optimization // is subject to endianness issues if the generated code is used on a machine with a - // different endianness; for now, we simply disable the optimization if the generated - // code is being saved. TODO https://github.com/dotnet/runtime/issues/30153. - if (!caseInsensitive && !_persistsAssembly) + // different endianness, but that's not a concern when the code is emitted by the + // same process that then uses it. + if (!caseInsensitive) { // On 64-bit, process 4 characters at a time until the string isn't at least 4 characters long. if (IntPtr.Size == 8) diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs index 4c8fe4f91902f..8ed30bbcb266b 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs @@ -29,7 +29,7 @@ internal sealed class RegexLWCGCompiler : RegexCompiler /// Id number to use for the next compiled regex. private static int s_regexCount; - public RegexLWCGCompiler() : base(persistsAssembly: false) + public RegexLWCGCompiler() { } diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.CompileToAssembly.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.CompileToAssembly.Tests.cs index 5e7eacd51b980..6057602714ae1 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/Regex.CompileToAssembly.Tests.cs +++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.CompileToAssembly.Tests.cs @@ -2,115 +2,36 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; -using System.Linq; using System.Reflection; using System.Reflection.Emit; using Xunit; namespace System.Text.RegularExpressions.Tests { + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] public class RegexCompileToAssemblyTests : FileCleanupTestBase { [Fact] - public void CompileToAssembly_InvalidArgs_Throws() - { - AssertExtensions.Throws("assemblyname", () => Regex.CompileToAssembly(null, null)); - AssertExtensions.Throws("assemblyname", () => Regex.CompileToAssembly(null, null, null)); - AssertExtensions.Throws("assemblyname", () => Regex.CompileToAssembly(null, null, null, null)); - - AssertExtensions.Throws("regexinfos", () => Regex.CompileToAssembly(null, new AssemblyName("abcd"))); - AssertExtensions.Throws("regexinfos", () => Regex.CompileToAssembly(null, new AssemblyName("abcd"), null)); - AssertExtensions.Throws("regexinfos", () => Regex.CompileToAssembly(null, new AssemblyName("abcd"), null, null)); - - // We currently build more code for CompileToAssembly into debug builds, which changes this particular exception type based on Debug vs Release. - // Until that changes, for the tests just allow them both. - AssertThrows(() => Regex.CompileToAssembly(new RegexCompilationInfo[] { null }, new AssemblyName("abcd"))); - AssertThrows(() => Regex.CompileToAssembly(new RegexCompilationInfo[] { new RegexCompilationInfo("abc", RegexOptions.None, "abc", "", true), null }, new AssemblyName("abcd"))); - AssertThrows(() => Regex.CompileToAssembly(new RegexCompilationInfo[] { null }, new AssemblyName("abcd"), new CustomAttributeBuilder[0])); - - static void AssertThrows(Action action) - { - Exception e = Record.Exception(action); - Assert.NotNull(e); - Assert.True(e is TException1 || e is TException2); - } - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] public void CompileToAssembly_PNSE() { - Assert.Throws(() => - Regex.CompileToAssembly( - new[] { new RegexCompilationInfo("abcd", RegexOptions.None, "abcd", "SomeNamespace", true) }, - new AssemblyName("abcd"))); - - Assert.Throws(() => - Regex.CompileToAssembly( - new[] { new RegexCompilationInfo("abcd", RegexOptions.CultureInvariant, "abcd", "", true, TimeSpan.FromMinutes(1)) }, - new AssemblyName("abcdWithTimeout"))); - - Assert.Throws(() => - Regex.CompileToAssembly( - new[] { new RegexCompilationInfo("(?ab)cd", RegexOptions.None, "abcd", "", true, TimeSpan.FromMinutes(1)) }, - new AssemblyName("abcdWithNamedCapture"))); - - Assert.Throws(() => - Regex.CompileToAssembly( - new[] { new RegexCompilationInfo(".*\\B(\\d+)(?SUCCESS)\\B.*", RegexOptions.None, "withCaptures", "", true) }, - new AssemblyName("withCaptures"))); - - Assert.Throws(() => - Regex.CompileToAssembly( - new[] { new RegexCompilationInfo("abcd", RegexOptions.None, "abcd", "", true) }, - new AssemblyName("abcdWithCustomAttribute"), - new[] { new CustomAttributeBuilder(typeof(AssemblyCompanyAttribute).GetConstructor(new[] { typeof(string) }), new[] { "TestCompany" }) })); - } - - [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - public void CompileToAssembly_ResourceFile_PNSE() - { - Assert.Throws(() => - Regex.CompileToAssembly( - new[] { new RegexCompilationInfo("abcd", RegexOptions.None, "abcd", "", true) }, - new AssemblyName("abcdWithUnsupportedResourceFile"), - attributes: null, - "unsupportedResourceFile")); - } - - [Fact] - [SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)] - public void CompileToAssembly_CanRunGeneratedRegex() - { - (RegexCompilationInfo rci, string validInput, string invalidInput)[] regexes = new[] - { - (new RegexCompilationInfo("abcd", RegexOptions.None, "Namespace1", "Type1", ispublic: true), "123abcd123", "123abed123"), - (new RegexCompilationInfo("(a|b|cde)+", RegexOptions.None, "Namespace2.Sub", "Type2", ispublic: true), "abcde", "cd"), - }; - - string assemblyName = Path.GetRandomFileName(); - - string cwd = Environment.CurrentDirectory; - Environment.CurrentDirectory = TestDirectory; - try - { - Regex.CompileToAssembly( - regexes.Select(r => r.rci).ToArray(), - new AssemblyName(assemblyName)); - } - finally - { - Environment.CurrentDirectory = cwd; - } - - Assembly a = Assembly.LoadFrom(Path.Combine(TestDirectory, assemblyName + ".dll")); - foreach ((RegexCompilationInfo rci, string validInput, string invalidInput) in regexes) - { - Regex r = (Regex)Activator.CreateInstance(a.GetType($"{rci.Namespace}.{rci.Name}")); - Assert.True(r.IsMatch(validInput)); - Assert.False(r.IsMatch(invalidInput)); - } + Assert.Throws(() => Regex.CompileToAssembly(null, null)); + Assert.Throws(() => Regex.CompileToAssembly(null, null, null)); + Assert.Throws(() => Regex.CompileToAssembly(null, null, null, null)); + + Assert.Throws(() => Regex.CompileToAssembly( + new[] { new RegexCompilationInfo("abcd", RegexOptions.None, "abcd", "SomeNamespace", true) }, + new AssemblyName("abcd"))); + + Assert.Throws(() => Regex.CompileToAssembly( + new[] { new RegexCompilationInfo("abcd", RegexOptions.None, "abcd", "SomeNamespace", true) }, + new AssemblyName("abcd"), + new[] { new CustomAttributeBuilder(typeof(AssemblyCompanyAttribute).GetConstructor(new[] { typeof(string) }), new[] { "TestCompany" }) })); + + Assert.Throws(() => Regex.CompileToAssembly( + new[] { new RegexCompilationInfo("abcd", RegexOptions.None, "abcd", "SomeNamespace", true) }, + new AssemblyName("abcd"), + new[] { new CustomAttributeBuilder(typeof(AssemblyCompanyAttribute).GetConstructor(new[] { typeof(string) }), new[] { "TestCompany" }) }, + "resourceFile")); } } } diff --git a/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj b/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj index b76a84b8b8f2e..ef512554bd238 100644 --- a/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj +++ b/src/libraries/System.Text.RegularExpressions/tests/System.Text.RegularExpressions.Tests.csproj @@ -2,7 +2,8 @@ true - $(NoWarn);xUnit2008 + + $(NoWarn);xUnit2008;SYSLIB0036 $(NetCoreAppCurrent);net48 true true