Enable Regex compiler to be trimmed (dotnet/corefx#41075)
authorStephen Toub <stoub@microsoft.com>
Fri, 13 Sep 2019 00:21:41 +0000 (20:21 -0400)
committerGitHub <noreply@github.com>
Fri, 13 Sep 2019 00:21:41 +0000 (20:21 -0400)
Today an app that just does:
```C#
Console.WriteLine(Regex.IsMatch("12345", "0*[1-9][0-9]*"));
```
and is trimmed will end up publishing a 105K System.Text.RegularExpression.dll, including the regex compiler for when the RegexOptions.Compiled is used.  We can refactor the constructors such that the compiler only ends up getting rooted when one of the ctors that takes options is used.  After this PR, if you don't pass RegexOptions, the 105K drops to 85K.

Commit migrated from https://github.com/dotnet/corefx/commit/31a5bba0e79a5ae875f691d70ec3b9789691136d

src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Match.cs
src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs
src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Split.cs
src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs

index e7aece5..ec63dbd 100644 (file)
@@ -9,25 +9,19 @@ namespace System.Text.RegularExpressions
         /// <summary>
         /// Searches the input string for one or more occurrences of the text supplied in the given pattern.
         /// </summary>
-        public static bool IsMatch(string input, string pattern)
-        {
-            return IsMatch(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
-        }
+        public static bool IsMatch(string input, string pattern) =>
+            new Regex(pattern, addToCache: true).IsMatch(input);
 
         /// <summary>
         /// Searches the input string for one or more occurrences of the text
         /// supplied in the pattern parameter with matching options supplied in the options
         /// parameter.
         /// </summary>
-        public static bool IsMatch(string input, string pattern, RegexOptions options)
-        {
-            return IsMatch(input, pattern, options, s_defaultMatchTimeout);
-        }
+        public static bool IsMatch(string input, string pattern, RegexOptions options) =>
+            IsMatch(input, pattern, options, s_defaultMatchTimeout);
 
-        public static bool IsMatch(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
-        {
-            return new Regex(pattern, options, matchTimeout, true).IsMatch(input);
-        }
+        public static bool IsMatch(string input, string pattern, RegexOptions options, TimeSpan matchTimeout) =>
+            new Regex(pattern, options, matchTimeout, addToCache: true).IsMatch(input);
 
         /*
          * Returns true if the regex finds a match within the specified string
@@ -64,25 +58,19 @@ namespace System.Text.RegularExpressions
         /// Searches the input string for one or more occurrences of the text
         /// supplied in the pattern parameter.
         /// </summary>
-        public static Match Match(string input, string pattern)
-        {
-            return Match(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
-        }
+        public static Match Match(string input, string pattern) =>
+            new Regex(pattern, addToCache: true).Match(input);
 
         /// <summary>
         /// Searches the input string for one or more occurrences of the text
         /// supplied in the pattern parameter. Matching is modified with an option
         /// string.
         /// </summary>
-        public static Match Match(string input, string pattern, RegexOptions options)
-        {
-            return Match(input, pattern, options, s_defaultMatchTimeout);
-        }
+        public static Match Match(string input, string pattern, RegexOptions options) =>
+            Match(input, pattern, options, s_defaultMatchTimeout);
 
-        public static Match Match(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
-        {
-            return new Regex(pattern, options, matchTimeout, true).Match(input);
-        }
+        public static Match Match(string input, string pattern, RegexOptions options, TimeSpan matchTimeout) =>
+            new Regex(pattern, options, matchTimeout, addToCache: true).Match(input);
 
         /*
          * Finds the first match for the regular expression starting at the beginning
@@ -134,23 +122,17 @@ namespace System.Text.RegularExpressions
         /// <summary>
         /// Returns all the successful matches as if Match were called iteratively numerous times.
         /// </summary>
-        public static MatchCollection Matches(string input, string pattern)
-        {
-            return Matches(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
-        }
+        public static MatchCollection Matches(string input, string pattern) =>
+            new Regex(pattern, addToCache: true).Matches(input);
 
         /// <summary>
         /// Returns all the successful matches as if Match were called iteratively numerous times.
         /// </summary>
-        public static MatchCollection Matches(string input, string pattern, RegexOptions options)
-        {
-            return Matches(input, pattern, options, s_defaultMatchTimeout);
-        }
+        public static MatchCollection Matches(string input, string pattern, RegexOptions options) =>
+            Matches(input, pattern, options, s_defaultMatchTimeout);
 
-        public static MatchCollection Matches(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
-        {
-            return new Regex(pattern, options, matchTimeout, true).Matches(input);
-        }
+        public static MatchCollection Matches(string input, string pattern, RegexOptions options, TimeSpan matchTimeout) =>
+            new Regex(pattern, options, matchTimeout, addToCache: true).Matches(input);
 
         /*
          * Finds the first match for the regular expression starting at the beginning
index 2d4a403..5befa4c 100644 (file)
@@ -19,25 +19,19 @@ namespace System.Text.RegularExpressions
         /// Replaces all occurrences of the pattern with the <paramref name="replacement"/> pattern, starting at
         /// the first character in the input string.
         /// </summary>
-        public static string Replace(string input, string pattern, string replacement)
-        {
-            return Replace(input, pattern, replacement, RegexOptions.None, s_defaultMatchTimeout);
-        }
+        public static string Replace(string input, string pattern, string replacement) =>
+            new Regex(pattern, addToCache: true).Replace(input, replacement);
 
         /// <summary>
         /// Replaces all occurrences of
         /// the <paramref name="pattern "/>with the <paramref name="replacement "/>
         /// pattern, starting at the first character in the input string.
         /// </summary>
-        public static string Replace(string input, string pattern, string replacement, RegexOptions options)
-        {
-            return Replace(input, pattern, replacement, options, s_defaultMatchTimeout);
-        }
+        public static string Replace(string input, string pattern, string replacement, RegexOptions options) =>
+            Replace(input, pattern, replacement, options, s_defaultMatchTimeout);
 
-        public static string Replace(string input, string pattern, string replacement, RegexOptions options, TimeSpan matchTimeout)
-        {
-            return new Regex(pattern, options, matchTimeout, true).Replace(input, replacement);
-        }
+        public static string Replace(string input, string pattern, string replacement, RegexOptions options, TimeSpan matchTimeout) =>
+            new Regex(pattern, options, matchTimeout, true).Replace(input, replacement);
 
         /// <summary>
         /// Replaces all occurrences of the previously defined pattern with the
@@ -88,24 +82,18 @@ namespace System.Text.RegularExpressions
         /// Replaces all occurrences of the <paramref name="pattern"/> with the recent
         /// replacement pattern.
         /// </summary>
-        public static string Replace(string input, string pattern, MatchEvaluator evaluator)
-        {
-            return Replace(input, pattern, evaluator, RegexOptions.None, s_defaultMatchTimeout);
-        }
+        public static string Replace(string input, string pattern, MatchEvaluator evaluator) =>
+            new Regex(pattern, addToCache: true).Replace(input, evaluator);
 
         /// <summary>
         /// Replaces all occurrences of the <paramref name="pattern"/> with the recent
         /// replacement pattern, starting at the first character.
         /// </summary>
-        public static string Replace(string input, string pattern, MatchEvaluator evaluator, RegexOptions options)
-        {
-            return Replace(input, pattern, evaluator, options, s_defaultMatchTimeout);
-        }
+        public static string Replace(string input, string pattern, MatchEvaluator evaluator, RegexOptions options) =>
+            Replace(input, pattern, evaluator, options, s_defaultMatchTimeout);
 
-        public static string Replace(string input, string pattern, MatchEvaluator evaluator, RegexOptions options, TimeSpan matchTimeout)
-        {
-            return new Regex(pattern, options, matchTimeout, true).Replace(input, evaluator);
-        }
+        public static string Replace(string input, string pattern, MatchEvaluator evaluator, RegexOptions options, TimeSpan matchTimeout) =>
+            new Regex(pattern, options, matchTimeout, addToCache: true).Replace(input, evaluator);
 
         /// <summary>
         /// Replaces all occurrences of the previously defined pattern with the recent
index bd8d590..aab119d 100644 (file)
@@ -12,23 +12,17 @@ namespace System.Text.RegularExpressions
         /// Splits the <paramref name="input "/>string at the position defined
         /// by <paramref name="pattern"/>.
         /// </summary>
-        public static string[] Split(string input, string pattern)
-        {
-            return Split(input, pattern, RegexOptions.None, s_defaultMatchTimeout);
-        }
+        public static string[] Split(string input, string pattern) =>
+            new Regex(pattern, addToCache: true).Split(input);
 
         /// <summary>
         /// Splits the <paramref name="input "/>string at the position defined by <paramref name="pattern"/>.
         /// </summary>
-        public static string[] Split(string input, string pattern, RegexOptions options)
-        {
-            return Split(input, pattern, options, s_defaultMatchTimeout);
-        }
+        public static string[] Split(string input, string pattern, RegexOptions options) =>
+            Split(input, pattern, options, s_defaultMatchTimeout);
 
-        public static string[] Split(string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
-        {
-            return new Regex(pattern, options, matchTimeout, true).Split(input);
-        }
+        public static string[] Split(string input, string pattern, RegexOptions options, TimeSpan matchTimeout) =>
+            new Regex(pattern, options, matchTimeout, addToCache: true).Split(input);
 
         /// <summary>
         /// Splits the <paramref name="input"/> string at the position defined by a
index cbf9e27..8604d5d 100644 (file)
@@ -48,37 +48,37 @@ namespace System.Text.RegularExpressions
         /// Creates and compiles a regular expression object for the specified regular
         /// expression.
         /// </summary>
-        public Regex(string pattern)
-            : this(pattern, RegexOptions.None, s_defaultMatchTimeout, false)
-        {
-        }
+        public Regex(string pattern) =>
+            Init(pattern, RegexOptions.None, s_defaultMatchTimeout, addToCache: false);
+
+        /// <summary>
+        /// Creates and compiles a regular expression object for the specified regular
+        /// expression, and adds it to the cache.
+        /// </summary>
+        private Regex(string pattern, bool addToCache) =>
+            Init(pattern, RegexOptions.None, s_defaultMatchTimeout, addToCache);
 
         /// <summary>
         /// Creates and compiles a regular expression object for the
         /// specified regular expression with options that modify the pattern.
         /// </summary>
-        public Regex(string pattern, RegexOptions options)
-            : this(pattern, options, s_defaultMatchTimeout, false)
-        {
-        }
-
-        public Regex(string pattern, RegexOptions options, TimeSpan matchTimeout)
-            : this(pattern, options, matchTimeout, false)
-        {
-        }
-
-        protected Regex(SerializationInfo info, StreamingContext context)
-            : this(info.GetString("pattern"), (RegexOptions)info.GetInt32("options"))
-        {
-            throw new PlatformNotSupportedException();
-        }
-
-        void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
-        {
-            throw new PlatformNotSupportedException();
-        }
-
-        private Regex(string pattern, RegexOptions options, TimeSpan matchTimeout, bool addToCache)
+        public Regex(string pattern, RegexOptions options) =>
+            InitWithPossibleCompilation(pattern, options, s_defaultMatchTimeout, addToCache: false);
+
+        public Regex(string pattern, RegexOptions options, TimeSpan matchTimeout) =>
+            InitWithPossibleCompilation(pattern, options, matchTimeout, addToCache: false);
+
+        private Regex(string pattern, RegexOptions options, TimeSpan matchTimeout, bool addToCache) =>
+            InitWithPossibleCompilation(pattern, options, matchTimeout, addToCache);
+
+        /// <summary>Initializes the instance.</summary>
+        /// <remarks>
+        /// This is separated out of the constructor to allow the Regex ctor that doesn't
+        /// take a RegexOptions to avoid rooting the regex compiler, such that it can be trimmed away.
+        /// If <paramref name="options"/> may possibly include RegexOptions.Compiled, InitWithPossibleCompilation
+        /// must be invoked instead.
+        /// </remarks>
+        private CachedCodeEntry Init(string pattern, RegexOptions options, TimeSpan matchTimeout, bool addToCache)
         {
             if (pattern == null)
             {
@@ -153,6 +153,14 @@ namespace System.Text.RegularExpressions
                 _refsInitialized = true;
             }
 
+            return cached;
+        }
+
+        /// <summary>Initializes the instance.</summary>
+        private void InitWithPossibleCompilation(string pattern, RegexOptions options, TimeSpan matchTimeout, bool addToCache)
+        {
+            CachedCodeEntry cached = Init(pattern, options, matchTimeout, addToCache);
+
 #if FEATURE_COMPILED
             // if the compile option is set, then compile the code if it's not already
             if (UseOptionC() && factory == null)
@@ -169,6 +177,12 @@ namespace System.Text.RegularExpressions
 #endif
         }
 
+        protected Regex(SerializationInfo info, StreamingContext context) =>
+            throw new PlatformNotSupportedException();
+
+        void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) =>
+            throw new PlatformNotSupportedException();
+
         [CLSCompliant(false)]
         protected IDictionary Caps
         {