8312976: MatchResult produces StringIndexOutOfBoundsException for groups outside match

Reviewed-by: alanb, smarks
This commit is contained in:
Raffaello Giulietti 2023-08-04 07:11:18 +00:00
parent 5d232959c2
commit 61c58fdd00
2 changed files with 88 additions and 8 deletions

View File

@ -274,13 +274,40 @@ public final class Matcher implements MatchResult {
* @since 1.5 * @since 1.5
*/ */
public MatchResult toMatchResult() { public MatchResult toMatchResult() {
String capturedText = hasMatch() int minStart;
? text.subSequence(first, last).toString() String capturedText;
: null; if (hasMatch()) {
minStart = minStart();
capturedText = text.subSequence(minStart, maxEnd()).toString();
} else {
minStart = -1;
capturedText = null;
}
return new ImmutableMatchResult(first, last, groupCount(), return new ImmutableMatchResult(first, last, groupCount(),
groups.clone(), capturedText, groups.clone(), capturedText,
namedGroups() namedGroups(), minStart);
); }
private int minStart() {
int r = text.length();
for (int group = 0; group <= groupCount(); ++group) {
int start = groups[group * 2];
if (start >= 0) {
r = Math.min(r, start);
}
}
return r;
}
private int maxEnd() {
int r = 0;
for (int group = 0; group <= groupCount(); ++group) {
int end = groups[group * 2 + 1];
if (end >= 0) {
r = Math.max(r, end);
}
}
return r;
} }
private static class ImmutableMatchResult implements MatchResult { private static class ImmutableMatchResult implements MatchResult {
@ -290,16 +317,18 @@ public final class Matcher implements MatchResult {
private final int[] groups; private final int[] groups;
private final String text; private final String text;
private final Map<String, Integer> namedGroups; private final Map<String, Integer> namedGroups;
private final int minStart;
ImmutableMatchResult(int first, int last, int groupCount, ImmutableMatchResult(int first, int last, int groupCount,
int[] groups, String text, int[] groups, String text,
Map<String, Integer> namedGroups) { Map<String, Integer> namedGroups, int minStart) {
this.first = first; this.first = first;
this.last = last; this.last = last;
this.groupCount = groupCount; this.groupCount = groupCount;
this.groups = groups; this.groups = groups;
this.text = text; this.text = text;
this.namedGroups = namedGroups; this.namedGroups = namedGroups;
this.minStart = minStart;
} }
@Override @Override
@ -345,7 +374,7 @@ public final class Matcher implements MatchResult {
checkGroup(group); checkGroup(group);
if ((groups[group * 2] == -1) || (groups[group * 2 + 1] == -1)) if ((groups[group * 2] == -1) || (groups[group * 2 + 1] == -1))
return null; return null;
return text.substring(groups[group * 2] - first, groups[group * 2 + 1] - first); return text.substring(groups[group * 2] - minStart, groups[group * 2 + 1] - minStart);
} }
@Override @Override

View File

@ -23,8 +23,12 @@
import jdk.test.lib.RandomFactory; import jdk.test.lib.RandomFactory;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.regex.MatchResult; import java.util.regex.MatchResult;
@ -33,10 +37,11 @@ import java.util.regex.Pattern;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.provider.Arguments.arguments;
/* /*
* @test * @test
* @bug 8132995 * @bug 8132995 8312976
* @key randomness * @key randomness
* *
* @summary Tests to exercise the optimization described in the bug report. * @summary Tests to exercise the optimization described in the bug report.
@ -179,4 +184,50 @@ public class ImmutableMatchResultTest {
testResultsStream(CharBuffer.wrap(inResults)); testResultsStream(CharBuffer.wrap(inResults));
} }
static Arguments[] testGroupsOutsideMatch() {
return new Arguments[]{
arguments("(?<=(\\d{3}))\\D*(?=(\\d{4}))", "-1234abcxyz5678-"),
arguments("(?<=(\\d{3}))\\D*(?=(\\1))", "-1234abcxyz2348-"),
arguments("(?<!(\\d{4}))\\D+(?=(\\d{4}))", "123abcxyz5678-"),
};
}
@ParameterizedTest
@MethodSource
void testGroupsOutsideMatch(String pattern, String text) {
char[] data = text.toCharArray();
Matcher m = Pattern.compile(pattern)
.matcher(CharBuffer.wrap(data));
assertEquals(2, m.groupCount());
assertTrue(m.find());
int start = m.start();
int end = m.end();
String group = m.group();
int prefixStart = m.start(1);
int prefixEnd = m.end(1);
String prefixGroup = m.group(1);
int suffixStart = m.start(2);
int suffixEnd = m.end(2);
String suffixGroup = m.group(2);
MatchResult mr = m.toMatchResult();
Arrays.fill(data, '*'); // spoil original input
assertEquals(start, mr.start());
assertEquals(end, mr.end());
assertEquals(group, mr.group());
assertEquals(prefixStart, mr.start(1));
assertEquals(prefixEnd, mr.end(1));
assertEquals(prefixGroup, mr.group(1));
assertEquals(suffixStart, mr.start(2));
assertEquals(suffixEnd, mr.end(2));
assertEquals(suffixGroup, mr.group(2));
}
} }