@@ -949,6 +949,111 @@ static Object matchInRegion(
949
949
}
950
950
}
951
951
952
+ public abstract static class MatchNode extends RubyBaseNode {
953
+
954
+ public abstract Object execute (RubyRegexp regexp , Object string , Matcher matcher , int from , int to ,
955
+ boolean onlyMatchAtStart , int start , boolean createMatchData );
956
+
957
+ // Creating a MatchData will store a copy of the source string. It's tempting to use a rope here, but a bit
958
+ // inconvenient because we can't work with ropes directly in Ruby and some MatchData methods are nicely
959
+ // implemented using the source string data. We mustn't allow the source string's contents to change, however, so we must ensure that we have
960
+ // a private copy of that string. Since the source string would otherwise be a reference to string held outside
961
+ // the MatchData object, it would be possible for the source string to be modified externally.
962
+ //
963
+ // Ex. x = "abc"; x =~ /(.*)/; x.upcase!
964
+ //
965
+ // Without a private copy, the MatchData's source could be modified to be upcased when it should remain the
966
+ // same as when the MatchData was created.
967
+ @ Specialization
968
+ Object match (
969
+ RubyRegexp regexp ,
970
+ Object string ,
971
+ Matcher matcher ,
972
+ int from ,
973
+ int to ,
974
+ boolean onlyMatchAtStart ,
975
+ int start ,
976
+ boolean createMatchData ,
977
+ @ Cached LazyDispatchNode stringDupNode ,
978
+ @ Cached InlinedConditionProfile createMatchDataProfile ,
979
+ @ Cached InlinedConditionProfile mismatchProfile ,
980
+ @ Cached InlinedConditionProfile nonZeroStart ,
981
+ @ Cached FixupMatchDataStartNode fixupMatchDataStartNode ) {
982
+ if (getContext ().getOptions ().REGEXP_INSTRUMENT_MATCH ) {
983
+ TruffleRegexpNodes .instrumentMatch (
984
+ MATCHED_REGEXPS_JONI ,
985
+ regexp ,
986
+ string ,
987
+ onlyMatchAtStart ,
988
+ getContext ().getOptions ().REGEXP_INSTRUMENT_MATCH_DETAILED );
989
+ }
990
+
991
+ int match = runMatch (matcher , from , to , onlyMatchAtStart );
992
+
993
+ if (createMatchDataProfile .profile (this , createMatchData )) {
994
+ if (mismatchProfile .profile (this , match == Matcher .FAILED )) {
995
+ return nil ;
996
+ }
997
+
998
+ assert match >= 0 ;
999
+
1000
+ final MultiRegion region = getMatcherEagerRegion (matcher );
1001
+ assert assertValidRegion (region );
1002
+
1003
+ var dupedString = stringDupNode .get (this ).call (string , "dup" );
1004
+ RubyMatchData result = new RubyMatchData (
1005
+ coreLibrary ().matchDataClass ,
1006
+ getLanguage ().matchDataShape ,
1007
+ regexp ,
1008
+ dupedString ,
1009
+ region );
1010
+ AllocationTracing .trace (result , this );
1011
+
1012
+ if (nonZeroStart .profile (this , start != 0 )) {
1013
+ fixupMatchDataStartNode .execute (this , result , start );
1014
+ }
1015
+
1016
+ return result ;
1017
+ } else {
1018
+ return match != Matcher .FAILED ;
1019
+ }
1020
+ }
1021
+
1022
+ @ TruffleBoundary
1023
+ private int runMatch (Matcher matcher , int from , int to , boolean onlyMatchAtStart ) {
1024
+ // Keep status as RUN because MRI has an uninterruptible Regexp engine
1025
+ if (onlyMatchAtStart ) {
1026
+ return getContext ().getThreadManager ().runUntilResultKeepStatus (this ,
1027
+ unused -> matcher .matchInterruptible (from , to , Option .DEFAULT ), null );
1028
+ } else {
1029
+ return getContext ().getThreadManager ().runUntilResultKeepStatus (this ,
1030
+ unused -> matcher .searchInterruptible (from , to , Option .DEFAULT ), null );
1031
+ }
1032
+ }
1033
+
1034
+ /** Is equivalent to {@link org.graalvm.shadowed.org.joni.Matcher#getEagerRegion()} but returns MultiRegion
1035
+ * instead of abstract Region class */
1036
+ private MultiRegion getMatcherEagerRegion (Matcher matcher ) {
1037
+ Region eagerRegion = matcher .getEagerRegion ();
1038
+
1039
+ if (eagerRegion instanceof SingleRegion singleRegion ) {
1040
+ return new MultiRegion (singleRegion .getBeg (0 ), singleRegion .getEnd (0 ));
1041
+ } else if (eagerRegion instanceof MultiRegion multiRegion ) {
1042
+ return multiRegion ;
1043
+ } else {
1044
+ throw CompilerDirectives .shouldNotReachHere ();
1045
+ }
1046
+ }
1047
+
1048
+ private boolean assertValidRegion (MultiRegion region ) {
1049
+ for (int i = 0 ; i < region .getNumRegs (); i ++) {
1050
+ assert region .getBeg (i ) >= 0 || region .getBeg (i ) == RubyMatchData .MISSING ;
1051
+ assert region .getEnd (i ) >= 0 || region .getEnd (i ) == RubyMatchData .MISSING ;
1052
+ }
1053
+ return true ;
1054
+ }
1055
+ }
1056
+
952
1057
@ GenerateCached (false )
953
1058
@ GenerateInline
954
1059
public abstract static class LazyMatchInRegionJoniNode extends RubyBaseNode {
@@ -1182,111 +1287,6 @@ Object isLinearTime(RubyRegexp regexp,
1182
1287
}
1183
1288
}
1184
1289
1185
- public abstract static class MatchNode extends RubyBaseNode {
1186
-
1187
- public abstract Object execute (RubyRegexp regexp , Object string , Matcher matcher ,
1188
- int from , int to , boolean onlyMatchAtStart , int start , boolean createMatchData );
1189
-
1190
- // Creating a MatchData will store a copy of the source string. It's tempting to use a rope here, but a bit
1191
- // inconvenient because we can't work with ropes directly in Ruby and some MatchData methods are nicely
1192
- // implemented using the source string data. We mustn't allow the source string's contents to change, however, so we must ensure that we have
1193
- // a private copy of that string. Since the source string would otherwise be a reference to string held outside
1194
- // the MatchData object, it would be possible for the source string to be modified externally.
1195
- //
1196
- // Ex. x = "abc"; x =~ /(.*)/; x.upcase!
1197
- //
1198
- // Without a private copy, the MatchData's source could be modified to be upcased when it should remain the
1199
- // same as when the MatchData was created.
1200
- @ Specialization
1201
- Object match (
1202
- RubyRegexp regexp ,
1203
- Object string ,
1204
- Matcher matcher ,
1205
- int from ,
1206
- int to ,
1207
- boolean onlyMatchAtStart ,
1208
- int start ,
1209
- boolean createMatchData ,
1210
- @ Cached LazyDispatchNode stringDupNode ,
1211
- @ Cached InlinedConditionProfile createMatchDataProfile ,
1212
- @ Cached InlinedConditionProfile mismatchProfile ,
1213
- @ Cached InlinedConditionProfile nonZeroStart ,
1214
- @ Cached FixupMatchDataStartNode fixupMatchDataStartNode ) {
1215
- if (getContext ().getOptions ().REGEXP_INSTRUMENT_MATCH ) {
1216
- TruffleRegexpNodes .instrumentMatch (
1217
- MATCHED_REGEXPS_JONI ,
1218
- regexp ,
1219
- string ,
1220
- onlyMatchAtStart ,
1221
- getContext ().getOptions ().REGEXP_INSTRUMENT_MATCH_DETAILED );
1222
- }
1223
-
1224
- int match = runMatch (matcher , from , to , onlyMatchAtStart );
1225
-
1226
- if (createMatchDataProfile .profile (this , createMatchData )) {
1227
- if (mismatchProfile .profile (this , match == Matcher .FAILED )) {
1228
- return nil ;
1229
- }
1230
-
1231
- assert match >= 0 ;
1232
-
1233
- final MultiRegion region = getMatcherEagerRegion (matcher );
1234
- assert assertValidRegion (region );
1235
-
1236
- var dupedString = stringDupNode .get (this ).call (string , "dup" );
1237
- RubyMatchData result = new RubyMatchData (
1238
- coreLibrary ().matchDataClass ,
1239
- getLanguage ().matchDataShape ,
1240
- regexp ,
1241
- dupedString ,
1242
- region );
1243
- AllocationTracing .trace (result , this );
1244
-
1245
- if (nonZeroStart .profile (this , start != 0 )) {
1246
- fixupMatchDataStartNode .execute (this , result , start );
1247
- }
1248
-
1249
- return result ;
1250
- } else {
1251
- return match != Matcher .FAILED ;
1252
- }
1253
- }
1254
-
1255
- @ TruffleBoundary
1256
- private int runMatch (Matcher matcher , int from , int to , boolean onlyMatchAtStart ) {
1257
- // Keep status as RUN because MRI has an uninterruptible Regexp engine
1258
- if (onlyMatchAtStart ) {
1259
- return getContext ().getThreadManager ().runUntilResultKeepStatus (this ,
1260
- unused -> matcher .matchInterruptible (from , to , Option .DEFAULT ), null );
1261
- } else {
1262
- return getContext ().getThreadManager ().runUntilResultKeepStatus (this ,
1263
- unused -> matcher .searchInterruptible (from , to , Option .DEFAULT ), null );
1264
- }
1265
- }
1266
-
1267
- /** Is equivalent to {@link org.graalvm.shadowed.org.joni.Matcher#getEagerRegion()} but returns MultiRegion
1268
- * instead of abstract Region class */
1269
- private MultiRegion getMatcherEagerRegion (Matcher matcher ) {
1270
- Region eagerRegion = matcher .getEagerRegion ();
1271
-
1272
- if (eagerRegion instanceof SingleRegion singleRegion ) {
1273
- return new MultiRegion (singleRegion .getBeg (0 ), singleRegion .getEnd (0 ));
1274
- } else if (eagerRegion instanceof MultiRegion multiRegion ) {
1275
- return multiRegion ;
1276
- } else {
1277
- throw CompilerDirectives .shouldNotReachHere ();
1278
- }
1279
- }
1280
-
1281
- private boolean assertValidRegion (MultiRegion region ) {
1282
- for (int i = 0 ; i < region .getNumRegs (); i ++) {
1283
- assert region .getBeg (i ) >= 0 || region .getBeg (i ) == RubyMatchData .MISSING ;
1284
- assert region .getEnd (i ) >= 0 || region .getEnd (i ) == RubyMatchData .MISSING ;
1285
- }
1286
- return true ;
1287
- }
1288
- }
1289
-
1290
1290
static final class MatchInfo {
1291
1291
1292
1292
private final RubyRegexp regex ;
0 commit comments