From 95a6eb3ac50eaa599018a97265725e135197159c Mon Sep 17 00:00:00 2001
From: serpentfabric <41795094+serpentfabric@users.noreply.github.com>
Date: Mon, 28 Dec 2020 14:36:59 -0500
Subject: [PATCH] jpath regex timeout support added for a single regex
expression, global umbrella for all regex calls, and support for allowing
regex calls to get compiled if necessary
require users to set a global cancellation token themselves, to avoid a potential unmanaged memory leak, which cleans up the code a bunch and fixes the failing tests
fix tests so cached regex's don't cause one test to fail using prior tests timeout...
handle the global timeout of multitoken regex internally and test for it
---
Src/Directory.Build.props | 2 +
.../Linq/JsonPath/JPathExecuteTests.cs | 77 +++++++++++++++++-
.../Newtonsoft.Json.Tests.csproj | 17 +++-
Src/Newtonsoft.Json/Linq/JToken.cs | 81 +++++++++++++++++--
Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs | 9 ++-
.../Linq/JsonPath/QueryExpression.cs | 20 ++++-
Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 6 +-
.../Utilities/MiscellaneousUtils.cs | 5 ++
8 files changed, 199 insertions(+), 18 deletions(-)
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
}
}