Skip to content

Commit 61c58fd

Browse files
committed
8312976: MatchResult produces StringIndexOutOfBoundsException for groups outside match
Reviewed-by: alanb, smarks
1 parent 5d23295 commit 61c58fd

File tree

2 files changed

+88
-8
lines changed

2 files changed

+88
-8
lines changed

src/java.base/share/classes/java/util/regex/Matcher.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -274,13 +274,40 @@ public Pattern pattern() {
274274
* @since 1.5
275275
*/
276276
public MatchResult toMatchResult() {
277-
String capturedText = hasMatch()
278-
? text.subSequence(first, last).toString()
279-
: null;
277+
int minStart;
278+
String capturedText;
279+
if (hasMatch()) {
280+
minStart = minStart();
281+
capturedText = text.subSequence(minStart, maxEnd()).toString();
282+
} else {
283+
minStart = -1;
284+
capturedText = null;
285+
}
280286
return new ImmutableMatchResult(first, last, groupCount(),
281287
groups.clone(), capturedText,
282-
namedGroups()
283-
);
288+
namedGroups(), minStart);
289+
}
290+
291+
private int minStart() {
292+
int r = text.length();
293+
for (int group = 0; group <= groupCount(); ++group) {
294+
int start = groups[group * 2];
295+
if (start >= 0) {
296+
r = Math.min(r, start);
297+
}
298+
}
299+
return r;
300+
}
301+
302+
private int maxEnd() {
303+
int r = 0;
304+
for (int group = 0; group <= groupCount(); ++group) {
305+
int end = groups[group * 2 + 1];
306+
if (end >= 0) {
307+
r = Math.max(r, end);
308+
}
309+
}
310+
return r;
284311
}
285312

286313
private static class ImmutableMatchResult implements MatchResult {
@@ -290,16 +317,18 @@ private static class ImmutableMatchResult implements MatchResult {
290317
private final int[] groups;
291318
private final String text;
292319
private final Map<String, Integer> namedGroups;
320+
private final int minStart;
293321

294322
ImmutableMatchResult(int first, int last, int groupCount,
295323
int[] groups, String text,
296-
Map<String, Integer> namedGroups) {
324+
Map<String, Integer> namedGroups, int minStart) {
297325
this.first = first;
298326
this.last = last;
299327
this.groupCount = groupCount;
300328
this.groups = groups;
301329
this.text = text;
302330
this.namedGroups = namedGroups;
331+
this.minStart = minStart;
303332
}
304333

305334
@Override
@@ -345,7 +374,7 @@ public String group(int group) {
345374
checkGroup(group);
346375
if ((groups[group * 2] == -1) || (groups[group * 2 + 1] == -1))
347376
return null;
348-
return text.substring(groups[group * 2] - first, groups[group * 2 + 1] - first);
377+
return text.substring(groups[group * 2] - minStart, groups[group * 2 + 1] - minStart);
349378
}
350379

351380
@Override

test/jdk/java/util/regex/ImmutableMatchResultTest.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@
2323

2424
import jdk.test.lib.RandomFactory;
2525
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.params.ParameterizedTest;
27+
import org.junit.jupiter.params.provider.Arguments;
28+
import org.junit.jupiter.params.provider.MethodSource;
2629

2730
import java.nio.CharBuffer;
31+
import java.util.Arrays;
2832
import java.util.List;
2933
import java.util.Random;
3034
import java.util.regex.MatchResult;
@@ -33,10 +37,11 @@
3337

3438
import static org.junit.jupiter.api.Assertions.assertEquals;
3539
import static org.junit.jupiter.api.Assertions.assertTrue;
40+
import static org.junit.jupiter.params.provider.Arguments.arguments;
3641

3742
/*
3843
* @test
39-
* @bug 8132995
44+
* @bug 8132995 8312976
4045
* @key randomness
4146
*
4247
* @summary Tests to exercise the optimization described in the bug report.
@@ -179,4 +184,50 @@ void testResultsStreamCharBuffer() {
179184
testResultsStream(CharBuffer.wrap(inResults));
180185
}
181186

187+
static Arguments[] testGroupsOutsideMatch() {
188+
return new Arguments[]{
189+
arguments("(?<=(\\d{3}))\\D*(?=(\\d{4}))", "-1234abcxyz5678-"),
190+
arguments("(?<=(\\d{3}))\\D*(?=(\\1))", "-1234abcxyz2348-"),
191+
arguments("(?<!(\\d{4}))\\D+(?=(\\d{4}))", "123abcxyz5678-"),
192+
};
193+
}
194+
195+
@ParameterizedTest
196+
@MethodSource
197+
void testGroupsOutsideMatch(String pattern, String text) {
198+
char[] data = text.toCharArray();
199+
Matcher m = Pattern.compile(pattern)
200+
.matcher(CharBuffer.wrap(data));
201+
202+
assertEquals(2, m.groupCount());
203+
assertTrue(m.find());
204+
205+
int start = m.start();
206+
int end = m.end();
207+
String group = m.group();
208+
209+
int prefixStart = m.start(1);
210+
int prefixEnd = m.end(1);
211+
String prefixGroup = m.group(1);
212+
213+
int suffixStart = m.start(2);
214+
int suffixEnd = m.end(2);
215+
String suffixGroup = m.group(2);
216+
217+
MatchResult mr = m.toMatchResult();
218+
Arrays.fill(data, '*'); // spoil original input
219+
220+
assertEquals(start, mr.start());
221+
assertEquals(end, mr.end());
222+
assertEquals(group, mr.group());
223+
224+
assertEquals(prefixStart, mr.start(1));
225+
assertEquals(prefixEnd, mr.end(1));
226+
assertEquals(prefixGroup, mr.group(1));
227+
228+
assertEquals(suffixStart, mr.start(2));
229+
assertEquals(suffixEnd, mr.end(2));
230+
assertEquals(suffixGroup, mr.group(2));
231+
}
232+
182233
}

0 commit comments

Comments
 (0)