@@ -175,8 +175,27 @@ static void AppendHashtableContents(IndentedTextWriter writer, IEnumerable<Dicti
175175 }
176176
177177 /// <summary>Emits the code for the RunnerFactory. This is the actual logic for the regular expression.</summary>
178- private static void EmitRegexDerivedTypeRunnerFactory ( IndentedTextWriter writer , RegexMethod rm , Dictionary < string , string [ ] > requiredHelpers )
178+ private static void EmitRegexDerivedTypeRunnerFactory ( IndentedTextWriter writer , RegexMethod rm , Dictionary < string , string [ ] > requiredHelpers , bool checkOverflow )
179179 {
180+ void EnterCheckOverflow ( )
181+ {
182+ if ( checkOverflow )
183+ {
184+ writer . WriteLine ( $ "unchecked") ;
185+ writer . WriteLine ( $ "{{") ;
186+ writer . Indent ++ ;
187+ }
188+ }
189+
190+ void ExitCheckOverflow ( )
191+ {
192+ if ( checkOverflow )
193+ {
194+ writer . Indent -- ;
195+ writer . WriteLine ( $ "}}") ;
196+ }
197+ }
198+
180199 writer . WriteLine ( $ "/// <summary>Provides a factory for creating <see cref=\" RegexRunner\" /> instances to be used by methods on <see cref=\" Regex\" />.</summary>") ;
181200 writer . WriteLine ( $ "private sealed class RunnerFactory : RegexRunnerFactory") ;
182201 writer . WriteLine ( $ "{{") ;
@@ -211,7 +230,9 @@ private static void EmitRegexDerivedTypeRunnerFactory(IndentedTextWriter writer,
211230 writer . WriteLine ( $ " protected override void Scan(ReadOnlySpan<char> inputSpan)") ;
212231 writer . WriteLine ( $ " {{") ;
213232 writer . Indent += 3 ;
233+ EnterCheckOverflow ( ) ;
214234 ( bool needsTryFind , bool needsTryMatch ) = EmitScan ( writer , rm ) ;
235+ ExitCheckOverflow ( ) ;
215236 writer . Indent -= 3 ;
216237 writer . WriteLine ( $ " }}") ;
217238 if ( needsTryFind )
@@ -223,7 +244,9 @@ private static void EmitRegexDerivedTypeRunnerFactory(IndentedTextWriter writer,
223244 writer . WriteLine ( $ " private bool TryFindNextPossibleStartingPosition(ReadOnlySpan<char> inputSpan)") ;
224245 writer . WriteLine ( $ " {{") ;
225246 writer . Indent += 3 ;
247+ EnterCheckOverflow ( ) ;
226248 EmitTryFindNextPossibleStartingPosition ( writer , rm , requiredHelpers ) ;
249+ ExitCheckOverflow ( ) ;
227250 writer . Indent -= 3 ;
228251 writer . WriteLine ( $ " }}") ;
229252 }
@@ -236,7 +259,9 @@ private static void EmitRegexDerivedTypeRunnerFactory(IndentedTextWriter writer,
236259 writer . WriteLine ( $ " private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)") ;
237260 writer . WriteLine ( $ " {{") ;
238261 writer . Indent += 3 ;
239- EmitTryMatchAtCurrentPosition ( writer , rm , requiredHelpers ) ;
262+ EnterCheckOverflow ( ) ;
263+ EmitTryMatchAtCurrentPosition ( writer , rm , requiredHelpers , checkOverflow ) ;
264+ ExitCheckOverflow ( ) ;
240265 writer . Indent -= 3 ;
241266 writer . WriteLine ( $ " }}") ;
242267 }
@@ -291,51 +316,52 @@ private static void AddIsWordCharHelper(Dictionary<string, string[]> requiredHel
291316 }
292317
293318 /// <summary>Adds the IsBoundary helper to the required helpers collection.</summary>
294- private static void AddIsBoundaryHelper ( Dictionary < string , string [ ] > requiredHelpers )
319+ private static void AddIsBoundaryHelper ( Dictionary < string , string [ ] > requiredHelpers , bool checkOverflow )
295320 {
296321 const string IsBoundary = nameof ( IsBoundary ) ;
297322 if ( ! requiredHelpers . ContainsKey ( IsBoundary ) )
298323 {
324+ string uncheckedKeyword = checkOverflow ? "unchecked" : "" ;
299325 requiredHelpers . Add ( IsBoundary , new string [ ]
300326 {
301- "/// <summary>Determines whether the specified index is a boundary.</summary>" ,
302- "[MethodImpl(MethodImplOptions.AggressiveInlining)]" ,
303- "internal static bool IsBoundary(ReadOnlySpan<char> inputSpan, int index)" ,
304- " {",
305- " int indexMinus1 = index - 1;" ,
306- " return ((uint)indexMinus1 < (uint)inputSpan.Length && IsBoundaryWordChar(inputSpan[indexMinus1])) !=" ,
307- " ((uint)index < (uint)inputSpan.Length && IsBoundaryWordChar(inputSpan[index]));" ,
308- "" ,
309- " static bool IsBoundaryWordChar(char ch) => IsWordChar(ch) || (ch == '\\ u200C' | ch == '\\ u200D');" ,
310- " }",
327+ $ "/// <summary>Determines whether the specified index is a boundary.</summary>",
328+ $ "[MethodImpl(MethodImplOptions.AggressiveInlining)]",
329+ $ "internal static bool IsBoundary(ReadOnlySpan<char> inputSpan, int index)",
330+ $ "{ {",
331+ $ " int indexMinus1 = index - 1;",
332+ $ " return { uncheckedKeyword } ((uint)indexMinus1 < (uint)inputSpan.Length && IsBoundaryWordChar(inputSpan[indexMinus1])) !=",
333+ $ " { uncheckedKeyword } ((uint)index < (uint)inputSpan.Length && IsBoundaryWordChar(inputSpan[index]));",
334+ $ "" ,
335+ $ " static bool IsBoundaryWordChar(char ch) => IsWordChar(ch) || (ch == '\\ u200C' | ch == '\\ u200D');",
336+ $ "} }",
311337 } ) ;
312338
313339 AddIsWordCharHelper ( requiredHelpers ) ;
314340 }
315341 }
316342
317343 /// <summary>Adds the IsECMABoundary helper to the required helpers collection.</summary>
318- private static void AddIsECMABoundaryHelper ( Dictionary < string , string [ ] > requiredHelpers )
344+ private static void AddIsECMABoundaryHelper ( Dictionary < string , string [ ] > requiredHelpers , bool checkOverflow )
319345 {
320346 const string IsECMABoundary = nameof ( IsECMABoundary ) ;
321347 if ( ! requiredHelpers . ContainsKey ( IsECMABoundary ) )
322348 {
349+ string uncheckedKeyword = checkOverflow ? "unchecked" : "" ;
323350 requiredHelpers . Add ( IsECMABoundary , new string [ ]
324351 {
325- "/// <summary>Determines whether the specified index is a boundary (ECMAScript).</summary>" ,
326- "[MethodImpl(MethodImplOptions.AggressiveInlining)]" ,
327- "internal static bool IsECMABoundary(ReadOnlySpan<char> inputSpan, int index)" ,
328- "{" ,
329- " int indexMinus1 = index - 1;" ,
330- " return ((uint)indexMinus1 < (uint)inputSpan.Length && IsECMAWordChar(inputSpan[indexMinus1])) !=" ,
331- " ((uint)index < (uint)inputSpan.Length && IsECMAWordChar(inputSpan[index]));" ,
332- "" ,
333- " static bool IsECMAWordChar(char ch) =>" ,
334- " ((((uint)ch - 'A') & ~0x20) < 26) || // ASCII letter" ,
335- " (((uint)ch - '0') < 10) || // digit" ,
336- " ch == '_' || // underscore" ,
337- " ch == '\\ u0130'; // latin capital letter I with dot above" ,
338- "}" ,
352+ $ "/// <summary>Determines whether the specified index is a boundary (ECMAScript).</summary>",
353+ $ "[MethodImpl(MethodImplOptions.AggressiveInlining)]",
354+ $ "internal static bool IsECMABoundary(ReadOnlySpan<char> inputSpan, int index)",
355+ $ "{{",
356+ $ " int indexMinus1 = index - 1;",
357+ $ " return { uncheckedKeyword } ((uint)indexMinus1 < (uint)inputSpan.Length && IsECMAWordChar(inputSpan[indexMinus1])) !=",
358+ $ " { uncheckedKeyword } ((uint)index < (uint)inputSpan.Length && IsECMAWordChar(inputSpan[index]));",
359+ $ "" ,
360+ $ " static bool IsECMAWordChar(char ch) =>",
361+ $ " char.IsAsciiLetterOrDigit(ch) ||",
362+ $ " ch == '_' ||",
363+ $ " ch == '\\ u0130'; // latin capital letter I with dot above",
364+ $ "}}",
339365 } ) ;
340366 }
341367 }
@@ -429,7 +455,7 @@ FindNextStartingPositionMode.LeadingAnchor_RightToLeft_Start or
429455 /// <summary>Emits the body of the TryFindNextPossibleStartingPosition.</summary>
430456 private static void EmitTryFindNextPossibleStartingPosition ( IndentedTextWriter writer , RegexMethod rm , Dictionary < string , string [ ] > requiredHelpers )
431457 {
432- RegexOptions options = ( RegexOptions ) rm . Options ;
458+ RegexOptions options = rm . Options ;
433459 RegexTree regexTree = rm . Tree ;
434460 bool rtl = ( options & RegexOptions . RightToLeft ) != 0 ;
435461
@@ -1025,7 +1051,7 @@ void EmitLiteralAfterAtomicLoop()
10251051 }
10261052
10271053 /// <summary>Emits the body of the TryMatchAtCurrentPosition.</summary>
1028- private static void EmitTryMatchAtCurrentPosition ( IndentedTextWriter writer , RegexMethod rm , Dictionary < string , string [ ] > requiredHelpers )
1054+ private static void EmitTryMatchAtCurrentPosition ( IndentedTextWriter writer , RegexMethod rm , Dictionary < string , string [ ] > requiredHelpers , bool checkOverflow )
10291055 {
10301056 // In .NET Framework and up through .NET Core 3.1, the code generated for RegexOptions.Compiled was effectively an unrolled
10311057 // version of what RegexInterpreter would process. The RegexNode tree would be turned into a series of opcodes via
@@ -2668,14 +2694,14 @@ void EmitBoundary(RegexNode node)
26682694 call = node . Kind is RegexNodeKind . Boundary ?
26692695 $ "!{ HelpersTypeName } .IsBoundary" :
26702696 $ "{ HelpersTypeName } .IsBoundary";
2671- AddIsBoundaryHelper ( requiredHelpers ) ;
2697+ AddIsBoundaryHelper ( requiredHelpers , checkOverflow ) ;
26722698 }
26732699 else
26742700 {
26752701 call = node . Kind is RegexNodeKind . ECMABoundary ?
26762702 $ "!{ HelpersTypeName } .IsECMABoundary" :
26772703 $ "{ HelpersTypeName } .IsECMABoundary";
2678- AddIsECMABoundaryHelper ( requiredHelpers ) ;
2704+ AddIsECMABoundaryHelper ( requiredHelpers , checkOverflow ) ;
26792705 }
26802706
26812707 using ( EmitBlock ( writer , $ "if ({ call } (inputSpan, pos{ ( sliceStaticPos > 0 ? $ " + { sliceStaticPos } " : "" ) } ))") )
0 commit comments