diff --git a/Src/Directory.Build.props b/Src/Directory.Build.props
index 11ecc418..cfaf3745 100644
--- a/Src/Directory.Build.props
+++ b/Src/Directory.Build.props
@@ -22,5 +22,7 @@
4.4.0
2.3.1
2.3.1
+ 32.0.2
+ 5.1.0
diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs
index 69c1a815..6b3a512e 100644
--- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs
+++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs
@@ -31,6 +31,13 @@ using System.Numerics;
#endif
using Newtonsoft.Json.Linq.JsonPath;
using Newtonsoft.Json.Tests.Bson;
+#if HAVE_REGEX_TIMEOUTS
+using Bogus;
+using Nito.AsyncEx;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Threading;
+#endif
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
@@ -44,7 +51,6 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Utilities.LinqBridge;
#else
using System.Linq;
-
#endif
namespace Newtonsoft.Json.Tests.Linq.JsonPath
@@ -71,6 +77,75 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
Assert.AreEqual(jObj, dd);
}
+#if HAVE_REGEX_TIMEOUTS
+ [Test]
+ public void BacktrackingRegex_SingleMatch_TimeoutRespected()
+ {
+ var RegexBacktrackingPattern = "(?(.*?))[|].*(?(.*?))[|].*(?(.*?))[|].*(?[1-3])[|].*(?(.*?))[|].*[|].*[|].*(?(.*?))[|].*[|].*(?(.*?))[|].*(?(.*))";
+
+ var faker = new Faker();
+ var regexBacktrackingData = new JArray();
+
+ for (var i = 0; i < 1000; i++)
+ {
+ var value = $"{faker.Date.Past()}|1|{faker.Lorem.Words()}|3|{faker.Lorem.Sentences(3, ". ")}|||.\\{faker.Lorem.Word()}.cpp||{faker.Random.UShort()}|-1";
+ regexBacktrackingData.Add(new JObject(new JProperty("b", value)));
+ }
+
+ Xunit.Assert.Throws(() =>
+ {
+ var tokens = regexBacktrackingData.SelectTokens(
+ $"[?(@.b =~ /{RegexBacktrackingPattern}/)]",
+ errorWhenNoMatch: false,
+ singleRegexMatchTimeout: TimeSpan.FromSeconds(.01)).ToArray();
+ });
+ }
+
+ [Test]
+ public void BacktrackingRegex_GlobalMatch_TimeoutRespected()
+ {
+ var faker = new Faker();
+ var regexBacktrackingData = new JArray();
+ for (var i = 0; i < 1000; i++)
+ {
+ var value = $"{faker.Date.Past()}|1|{faker.Lorem.Words()}|3|{faker.Lorem.Sentences(3, ". ")}|||.\\{faker.Lorem.Word()}.cpp||{faker.Random.UShort()}|-1";
+ regexBacktrackingData.Add(new JObject(new JProperty("b", value)));
+ }
+ var RegexBacktrackingPattern = "(?(.*?))[|].*(?(.*?))[|].*(?(.*?))[|].*(?[1-3])[|].*(?(.*?))[|].*[|].*[|].*(?(.*?))[|].*[|].*(?(.*?))[|].*(?(.*))";
+ var jpathExpression = $"[?(@.b =~ /{RegexBacktrackingPattern}/c)]";
+
+ var exceptionThrow = Xunit.Assert.ThrowsAny(() =>
+ {
+ var tokens = regexBacktrackingData.SelectTokens(
+ jpathExpression,
+ errorWhenNoMatch: false,
+ singleRegexMatchTimeout: TimeSpan.FromSeconds(2),
+ globalRegexMatchTimeout: TimeSpan.FromSeconds(.5)).ToArray();
+ });
+ Assert.IsTrue(exceptionThrow is OperationCanceledException or RegexMatchTimeoutException);
+ }
+
+ [Test]
+ public async Task BacktrackingRegexCanBeVerySlowWithoutTimeouts()
+ {
+ var RegexBacktrackingPattern = "(?(.*?))[|].*(?(.*?))[|].*(?(.*?))[|].*(?[1-3])[|].*(?(.*?))[|].*[|].*[|].*(?(.*?))[|].*[|].*(?(.*?))[|].*(?(.*))";
+ var faker = new Faker();
+ var regexBacktrackingData = new JArray();
+
+ for (var i = 0; i < 10; i++)
+ {
+ var value = $"{faker.Date.Past()}|1|{faker.Lorem.Words()}|3|{faker.Lorem.Sentences(3, ". ")}|||.\\{faker.Lorem.Word()}.cpp||{faker.Random.UShort()}|-1";
+ regexBacktrackingData.Add(new JObject(new JProperty("b", value)));
+ }
+
+ var selectTokenTask = Task.Run(() => regexBacktrackingData.SelectTokens($"[?(@.b =~ /{RegexBacktrackingPattern}/)]").ToArray());
+ using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(.3));
+ using var ctts = new CancellationTokenTaskSource(cts.Token);
+ var finishedTask = await Task.WhenAny(selectTokenTask, ctts.Task);
+ Assert.AreEqual(finishedTask, ctts.Task);
+ }
+#endif
+
[Test]
public void GreaterThanWithIntegerParameterAndStringValue()
{
diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
index 2d619e32..7989fe9f 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
@@ -2,7 +2,7 @@
net46;net40;net35;net20;net5.0;netcoreapp3.1;netcoreapp2.1
$(TestFrameworks)
- 8.0
+ 9.0
1.0
James Newton-King
Newtonsoft
@@ -76,6 +76,7 @@
+
Json.NET Tests .NET 4.0
@@ -121,11 +122,14 @@
+
+
+
Json.NET Tests .NET Standard 2.0
.NETStandard,Version=v2.0
- NETSTANDARD2_0;DNXCORE50;PORTABLE;HAVE_BENCHMARKS;$(AdditionalConstants)
+ NETSTANDARD2_0;DNXCORE50;PORTABLE;HAVE_BENCHMARKS;HAVE_REGEX_TIMEOUTS;$(AdditionalConstants)
@@ -142,11 +146,13 @@
+
+
Json.NET Tests .NET Standard 1.3
.NETStandard,Version=v1.3
- NETSTANDARD1_3;DNXCORE50;PORTABLE;HAVE_BENCHMARKS;$(AdditionalConstants)
+ NETSTANDARD1_3;DNXCORE50;PORTABLE;HAVE_BENCHMARKS;HAVE_REGEX_TIMEOUTS;$(AdditionalConstants)
@@ -162,10 +168,13 @@
+
+
+
Json.NET Tests .NET Standard 1.0
.NETStandard,Version=v1.0
- NETSTANDARD1_0;DNXCORE50;PORTABLE;$(AdditionalConstants)
+ NETSTANDARD1_0;DNXCORE50;PORTABLE;HAVE_REGEX_TIMEOUTS;$(AdditionalConstants)
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs
index 79aa9124..252f846c 100644
--- a/Src/Newtonsoft.Json/Linq/JToken.cs
+++ b/Src/Newtonsoft.Json/Linq/JToken.cs
@@ -44,6 +44,7 @@ using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json.Utilities.LinqBridge;
#else
using System.Linq;
+using System.Threading;
#endif
namespace Newtonsoft.Json.Linq
@@ -485,7 +486,7 @@ namespace Newtonsoft.Json.Linq
return (Array.IndexOf(validTypes, o.Type) != -1) || (nullable && (o.Type == JTokenType.Null || o.Type == JTokenType.Undefined));
}
-#region Cast from operators
+ #region Cast from operators
///
/// Performs an explicit conversion from to .
///
@@ -1497,9 +1498,9 @@ namespace Newtonsoft.Json.Linq
return ConvertUtils.ToBigInteger(v.Value);
}
#endif
-#endregion
+ #endregion
-#region Cast to operators
+ #region Cast to operators
///
/// Performs an implicit conversion from to .
///
@@ -1863,7 +1864,7 @@ namespace Newtonsoft.Json.Linq
{
return new JValue(value);
}
-#endregion
+ #endregion
IEnumerator IEnumerable.GetEnumerator()
{
@@ -2336,6 +2337,35 @@ namespace Newtonsoft.Json.Linq
return token;
}
+#if HAVE_REGEX_TIMEOUTS
+ ///
+ /// Selects a using a JSONPath expression. Selects the token that matches the object path.
+ ///
+ ///
+ /// A that contains a JSONPath expression.
+ ///
+ /// A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.
+ /// the time after which a single call to regex.ismatch must complete, default is forever
+ /// A .
+ public JToken? SelectToken(string path, bool errorWhenNoMatch, TimeSpan? singleMatchTimeout = default)
+ {
+ JPath p = new JPath(path, singleMatchTimeout);
+
+ JToken? token = null;
+ foreach (JToken t in p.Evaluate(this, this, errorWhenNoMatch))
+ {
+ if (token != null)
+ {
+ throw new JsonException("Path returned multiple tokens.");
+ }
+
+ token = t;
+ }
+
+ return token;
+ }
+#endif
+
///
/// Selects a collection of elements using a JSONPath expression.
///
@@ -2358,10 +2388,51 @@ namespace Newtonsoft.Json.Linq
/// An of that contains the selected elements.
public IEnumerable SelectTokens(string path, bool errorWhenNoMatch)
{
- JPath p = new JPath(path);
+ var p = new JPath(path);
return p.Evaluate(this, this, errorWhenNoMatch);
}
+#if HAVE_REGEX_TIMEOUTS
+ ///
+ /// Selects a collection of elements using a JSONPath expression.
+ ///
+ ///
+ /// A that contains a JSONPath expression.
+ ///
+ /// A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.
+ ///
+ /// for every token that matches a jpath, the time a regex is permitted to run
+ /// against that token before timeout
+ ///
+ ///
+ /// the time this method should wait for the given jpath regex to match all tokens.
+ /// worst case expected execution time roughly globalRegexMatchTimeout + Min(singleRegexMatchTimeout, globalRegexMatchTimeout)
+ ///
+ /// if single call timeout exceeded"
+ /// An of that contains the selected elements.
+ public IEnumerable SelectTokens(string path,
+ bool errorWhenNoMatch,
+ TimeSpan? singleRegexMatchTimeout = default,
+ TimeSpan? globalRegexMatchTimeout = default)
+ {
+ var singleTimeout = singleRegexMatchTimeout ?? Timeout.InfiniteTimeSpan;
+ var globalTimeout = globalRegexMatchTimeout ?? Timeout.InfiniteTimeSpan;
+ if (globalTimeout != Timeout.InfiniteTimeSpan && globalTimeout < singleTimeout)
+ {
+ singleTimeout = globalTimeout;
+ }
+
+ using var cts = new CancellationTokenSource(globalTimeout);
+ var p = new JPath(path, singleTimeout);
+ var results = p.Evaluate(this, this, errorWhenNoMatch);
+ foreach (var result in results)
+ {
+ cts.Token.ThrowIfCancellationRequested();
+ yield return result;
+ }
+ }
+#endif
+
#if HAVE_DYNAMIC
///
/// Returns the responsible for binding operations performed on this object.
diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs
index f18fb6c9..d5bc4e89 100644
--- a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs
+++ b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs
@@ -27,6 +27,8 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json.Linq.JsonPath
@@ -37,14 +39,16 @@ namespace Newtonsoft.Json.Linq.JsonPath
private readonly string _expression;
public List Filters { get; }
+ public readonly TimeSpan? RegexSingleMatchTimeout = default;
private int _currentIndex;
- public JPath(string expression)
+ public JPath(string expression, TimeSpan? regexSingleMatchTimeout = default)
{
ValidationUtils.ArgumentNotNull(expression, nameof(expression));
_expression = expression;
Filters = new List();
+ RegexSingleMatchTimeout = regexSingleMatchTimeout;
ParseMain();
}
@@ -509,7 +513,8 @@ namespace Newtonsoft.Json.Linq.JsonPath
right = ParseSide();
}
- BooleanQueryExpression booleanExpression = new BooleanQueryExpression(op, left, right);
+
+ var booleanExpression = new BooleanQueryExpression(op, left, right, RegexSingleMatchTimeout);
if (_expression[_currentIndex] == ')')
{
diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs
index f7bf1d5f..f9aa84d1 100644
--- a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs
+++ b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
+using System.Threading;
#if !HAVE_LINQ
using Newtonsoft.Json.Utilities.LinqBridge;
#else
@@ -83,11 +84,17 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
public readonly object Left;
public readonly object? Right;
+ public readonly TimeSpan? SingleMatchTimeout;
- public BooleanQueryExpression(QueryOperator @operator, object left, object? right) : base(@operator)
+ public BooleanQueryExpression(QueryOperator @operator,
+ object left,
+ object? right,
+ TimeSpan? regexSingleMatchTimeout = default)
+ : base(@operator)
{
Left = left;
Right = right;
+ SingleMatchTimeout = regexSingleMatchTimeout;
}
private IEnumerable GetResult(JToken root, JToken t, object? o)
@@ -143,7 +150,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
switch (Operator)
{
case QueryOperator.RegexEquals:
- if (RegexEquals(leftValue, rightValue))
+ if (RegexEquals(leftValue, rightValue, SingleMatchTimeout))
{
return true;
}
@@ -215,7 +222,9 @@ namespace Newtonsoft.Json.Linq.JsonPath
return false;
}
- private static bool RegexEquals(JValue input, JValue pattern)
+ private static bool RegexEquals(JValue input,
+ JValue pattern,
+ TimeSpan? regexMatchTimeout = default)
{
if (input.Type != JTokenType.String || pattern.Type != JTokenType.String)
{
@@ -228,7 +237,12 @@ namespace Newtonsoft.Json.Linq.JsonPath
string patternText = regexText.Substring(1, patternOptionDelimiterIndex - 1);
string optionsText = regexText.Substring(patternOptionDelimiterIndex + 1);
+#if HAVE_REGEX_TIMEOUTS
+ var timeout = regexMatchTimeout ?? Regex.InfiniteMatchTimeout;
+ return Regex.IsMatch((string)input.Value!, patternText, MiscellaneousUtils.GetRegexOptions(optionsText), timeout);
+#else
return Regex.IsMatch((string)input.Value!, patternText, MiscellaneousUtils.GetRegexOptions(optionsText));
+#endif
}
internal static bool EqualsWithStringCoercion(JValue value, JValue queryValue)
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
index ea973f48..f6cc6cc7 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
@@ -66,7 +66,7 @@
Json.NET .NET Standard 1.0
- NETSTANDARD1_0;PORTABLE;HAVE_ASYNC;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DYNAMIC;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_ISET;HAVE_LINQ;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_BINDER;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_OBSOLETE_FORMATTER_ASSEMBLY_STYLE;$(AdditionalConstants)
+ NETSTANDARD1_0;PORTABLE;HAVE_ASYNC;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DYNAMIC;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_ISET;HAVE_LINQ;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_BINDER;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_OBSOLETE_FORMATTER_ASSEMBLY_STYLE;HAVE_REGEX_TIMEOUTS;$(AdditionalConstants)
@@ -74,10 +74,10 @@
Json.NET .NET Standard 1.3
- NETSTANDARD1_3;PORTABLE;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_SERIALIZATION;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DYNAMIC;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_BINDER;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_OBSOLETE_FORMATTER_ASSEMBLY_STYLE;HAVE_XML_DOCUMENT;HAVE_CONCURRENT_DICTIONARY;HAVE_ICONVERTIBLE;$(AdditionalConstants)
+ NETSTANDARD1_3;PORTABLE;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_SERIALIZATION;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DYNAMIC;HAVE_EXPRESSIONS;HAVE_FSHARP_TYPES;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_REFLECTION_BINDER;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_OBSOLETE_FORMATTER_ASSEMBLY_STYLE;HAVE_XML_DOCUMENT;HAVE_CONCURRENT_DICTIONARY;HAVE_ICONVERTIBLE;HAVE_REGEX_TIMEOUTS;HAVE_REGEX_TIMEOUTS;$(AdditionalConstants)
Json.NET .NET Standard 2.0
- NETSTANDARD2_0;HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FAST_REVERSE;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;HAVE_CONCURRENT_DICTIONARY;$(AdditionalConstants)
+ NETSTANDARD2_0;HAVE_ADO_NET;HAVE_APP_DOMAIN;HAVE_ASYNC;HAVE_BIG_INTEGER;HAVE_BINARY_FORMATTER;HAVE_BINARY_SERIALIZATION;HAVE_BINARY_EXCEPTION_SERIALIZATION;HAVE_CHAR_TO_LOWER_WITH_CULTURE;HAVE_CHAR_TO_STRING_WITH_CULTURE;HAVE_COM_ATTRIBUTES;HAVE_COMPONENT_MODEL;HAVE_CONCURRENT_COLLECTIONS;HAVE_COVARIANT_GENERICS;HAVE_DATA_CONTRACTS;HAVE_DATE_TIME_OFFSET;HAVE_DB_NULL_TYPE_CODE;HAVE_DYNAMIC;HAVE_EMPTY_TYPES;HAVE_ENTITY_FRAMEWORK;HAVE_EXPRESSIONS;HAVE_FAST_REVERSE;HAVE_FSHARP_TYPES;HAVE_FULL_REFLECTION;HAVE_GUID_TRY_PARSE;HAVE_HASH_SET;HAVE_ICLONEABLE;HAVE_ICONVERTIBLE;HAVE_IGNORE_DATA_MEMBER_ATTRIBUTE;HAVE_INOTIFY_COLLECTION_CHANGED;HAVE_INOTIFY_PROPERTY_CHANGING;HAVE_ISET;HAVE_LINQ;HAVE_MEMORY_BARRIER;HAVE_METHOD_IMPL_ATTRIBUTE;HAVE_NON_SERIALIZED_ATTRIBUTE;HAVE_READ_ONLY_COLLECTIONS;HAVE_SECURITY_SAFE_CRITICAL_ATTRIBUTE;HAVE_SERIALIZATION_BINDER_BIND_TO_NAME;HAVE_STREAM_READER_WRITER_CLOSE;HAVE_STRING_JOIN_WITH_ENUMERABLE;HAVE_TIME_SPAN_PARSE_WITH_CULTURE;HAVE_TIME_SPAN_TO_STRING_WITH_CULTURE;HAVE_TIME_ZONE_INFO;HAVE_TRACE_WRITER;HAVE_TYPE_DESCRIPTOR;HAVE_UNICODE_SURROGATE_DETECTION;HAVE_VARIANT_TYPE_PARAMETERS;HAVE_VERSION_TRY_PARSE;HAVE_XLINQ;HAVE_XML_DOCUMENT;HAVE_XML_DOCUMENT_TYPE;HAVE_CONCURRENT_DICTIONARY;HAVE_REGEX_TIMEOUTS;$(AdditionalConstants)
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs
index 137ec445..d61ea5d7 100644
--- a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs
+++ b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs
@@ -165,6 +165,11 @@ namespace Newtonsoft.Json.Utilities
case 'x':
options |= RegexOptions.ExplicitCapture;
break;
+#if HAVE_REGEX_TIMEOUTS && (NETSTANDARD1_3 || NETSTANDARD2_0)
+ case 'c':
+ options |= RegexOptions.Compiled;
+ break;
+#endif
}
}