Skip to content

Commit

Permalink
Enable Regex compiler to be trimmed (dotnet/corefx#41075)
Browse files Browse the repository at this point in the history
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 dotnet/corefx@31a5bba
  • Loading branch information
stephentoub authored Sep 13, 2019
1 parent 59d799a commit c235d72
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,19 @@ public partial class Regex
/// <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
Expand Down Expand Up @@ -64,25 +58,19 @@ public bool IsMatch(string input, int startat)
/// 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
Expand Down Expand Up @@ -134,23 +122,17 @@ public Match Match(string input, int beginning, int length)
/// <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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,19 @@ public partial class Regex
/// 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
Expand Down Expand Up @@ -88,24 +82,18 @@ public string Replace(string input, string replacement, int count, int startat)
/// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,17 @@ public partial class Regex
/// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,37 +48,37 @@ protected Regex()
/// 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)
{
Expand Down Expand Up @@ -153,6 +153,14 @@ private Regex(string pattern, RegexOptions options, TimeSpan matchTimeout, bool
_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)
Expand All @@ -169,6 +177,12 @@ private Regex(string pattern, RegexOptions options, TimeSpan matchTimeout, bool
#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
{
Expand Down

0 comments on commit c235d72

Please sign in to comment.