@@ -175,8 +175,27 @@ static void AppendHashtableContents(IndentedTextWriter writer, IEnumerable<Dicti
175
175
}
176
176
177
177
/// <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 )
179
179
{
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
+
180
199
writer . WriteLine ( $ "/// <summary>Provides a factory for creating <see cref=\" RegexRunner\" /> instances to be used by methods on <see cref=\" Regex\" />.</summary>") ;
181
200
writer . WriteLine ( $ "private sealed class RunnerFactory : RegexRunnerFactory") ;
182
201
writer . WriteLine ( $ "{{") ;
@@ -211,7 +230,9 @@ private static void EmitRegexDerivedTypeRunnerFactory(IndentedTextWriter writer,
211
230
writer . WriteLine ( $ " protected override void Scan(ReadOnlySpan<char> inputSpan)") ;
212
231
writer . WriteLine ( $ " {{") ;
213
232
writer . Indent += 3 ;
233
+ EnterCheckOverflow ( ) ;
214
234
( bool needsTryFind , bool needsTryMatch ) = EmitScan ( writer , rm ) ;
235
+ ExitCheckOverflow ( ) ;
215
236
writer . Indent -= 3 ;
216
237
writer . WriteLine ( $ " }}") ;
217
238
if ( needsTryFind )
@@ -223,7 +244,9 @@ private static void EmitRegexDerivedTypeRunnerFactory(IndentedTextWriter writer,
223
244
writer . WriteLine ( $ " private bool TryFindNextPossibleStartingPosition(ReadOnlySpan<char> inputSpan)") ;
224
245
writer . WriteLine ( $ " {{") ;
225
246
writer . Indent += 3 ;
247
+ EnterCheckOverflow ( ) ;
226
248
EmitTryFindNextPossibleStartingPosition ( writer , rm , requiredHelpers ) ;
249
+ ExitCheckOverflow ( ) ;
227
250
writer . Indent -= 3 ;
228
251
writer . WriteLine ( $ " }}") ;
229
252
}
@@ -236,7 +259,9 @@ private static void EmitRegexDerivedTypeRunnerFactory(IndentedTextWriter writer,
236
259
writer . WriteLine ( $ " private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)") ;
237
260
writer . WriteLine ( $ " {{") ;
238
261
writer . Indent += 3 ;
239
- EmitTryMatchAtCurrentPosition ( writer , rm , requiredHelpers ) ;
262
+ EnterCheckOverflow ( ) ;
263
+ EmitTryMatchAtCurrentPosition ( writer , rm , requiredHelpers , checkOverflow ) ;
264
+ ExitCheckOverflow ( ) ;
240
265
writer . Indent -= 3 ;
241
266
writer . WriteLine ( $ " }}") ;
242
267
}
@@ -291,51 +316,52 @@ private static void AddIsWordCharHelper(Dictionary<string, string[]> requiredHel
291
316
}
292
317
293
318
/// <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 )
295
320
{
296
321
const string IsBoundary = nameof ( IsBoundary ) ;
297
322
if ( ! requiredHelpers . ContainsKey ( IsBoundary ) )
298
323
{
324
+ string uncheckedKeyword = checkOverflow ? "unchecked" : "" ;
299
325
requiredHelpers . Add ( IsBoundary , new string [ ]
300
326
{
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
+ $ "} }",
311
337
} ) ;
312
338
313
339
AddIsWordCharHelper ( requiredHelpers ) ;
314
340
}
315
341
}
316
342
317
343
/// <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 )
319
345
{
320
346
const string IsECMABoundary = nameof ( IsECMABoundary ) ;
321
347
if ( ! requiredHelpers . ContainsKey ( IsECMABoundary ) )
322
348
{
349
+ string uncheckedKeyword = checkOverflow ? "unchecked" : "" ;
323
350
requiredHelpers . Add ( IsECMABoundary , new string [ ]
324
351
{
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
+ $ "}}",
339
365
} ) ;
340
366
}
341
367
}
@@ -429,7 +455,7 @@ FindNextStartingPositionMode.LeadingAnchor_RightToLeft_Start or
429
455
/// <summary>Emits the body of the TryFindNextPossibleStartingPosition.</summary>
430
456
private static void EmitTryFindNextPossibleStartingPosition ( IndentedTextWriter writer , RegexMethod rm , Dictionary < string , string [ ] > requiredHelpers )
431
457
{
432
- RegexOptions options = ( RegexOptions ) rm . Options ;
458
+ RegexOptions options = rm . Options ;
433
459
RegexTree regexTree = rm . Tree ;
434
460
bool rtl = ( options & RegexOptions . RightToLeft ) != 0 ;
435
461
@@ -1025,7 +1051,7 @@ void EmitLiteralAfterAtomicLoop()
1025
1051
}
1026
1052
1027
1053
/// <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 )
1029
1055
{
1030
1056
// In .NET Framework and up through .NET Core 3.1, the code generated for RegexOptions.Compiled was effectively an unrolled
1031
1057
// 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)
2668
2694
call = node . Kind is RegexNodeKind . Boundary ?
2669
2695
$ "!{ HelpersTypeName } .IsBoundary" :
2670
2696
$ "{ HelpersTypeName } .IsBoundary";
2671
- AddIsBoundaryHelper ( requiredHelpers ) ;
2697
+ AddIsBoundaryHelper ( requiredHelpers , checkOverflow ) ;
2672
2698
}
2673
2699
else
2674
2700
{
2675
2701
call = node . Kind is RegexNodeKind . ECMABoundary ?
2676
2702
$ "!{ HelpersTypeName } .IsECMABoundary" :
2677
2703
$ "{ HelpersTypeName } .IsECMABoundary";
2678
- AddIsECMABoundaryHelper ( requiredHelpers ) ;
2704
+ AddIsECMABoundaryHelper ( requiredHelpers , checkOverflow ) ;
2679
2705
}
2680
2706
2681
2707
using ( EmitBlock ( writer , $ "if ({ call } (inputSpan, pos{ ( sliceStaticPos > 0 ? $ " + { sliceStaticPos } " : "" ) } ))") )
0 commit comments