Skip to content

Commit 0bf0f05

Browse files
committed
[GR-38321] [GR-68084] TRegex: further restrict quantifier unrolling to avoid NFA transition explosions.
PullRequest: graal/21643
2 parents 5aee576 + 005ab2b commit 0bf0f05

File tree

18 files changed

+196
-111
lines changed

18 files changed

+196
-111
lines changed

regex/src/com.oracle.truffle.regex.test/src/com/oracle/truffle/regex/tregex/test/JsTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,11 @@ public void generatedTests() {
688688
/* GENERATED CODE END - KEEP THIS MARKER FOR AUTOMATIC UPDATES */
689689
}
690690

691+
@Test
692+
public void quantifierUnrollNFAExplosion() {
693+
test("(?:\\3((?:[^]|.{0}|\\B|\ub9b5\\b|$){4,}){1,4})", "yim", "\ua3bb\n\n\u00a1\n\na\u2f77\n\n\ua3bb\n\n\u00a1\n\na\u2f77\n\n", 0, false);
694+
}
695+
691696
@Test
692697
public void overlappingBq() {
693698
testBoolean("(?=a{2,4})[ab]{4,68}c", "", NEVER_UNROLL_OPT, "aabbbbbbbbbbbbbbbbbbbbbbc", 0, true);

regex/src/com.oracle.truffle.regex.test/src/com/oracle/truffle/regex/tregex/test/OracleDBTests.java

Lines changed: 50 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -132,30 +132,10 @@ public void generatedTests() {
132132
expectSyntaxError("x{4294967296}", "", "x{4294967296}", 0, ErrorCode.InvalidQuantifier);
133133
expectSyntaxError("x{4294967297}", "", "x{4294967297}", 0, ErrorCode.InvalidQuantifier);
134134
test("x??", "", "x", 0, true, 0, 0);
135-
expectSyntaxError("x{2}+", "", "x", 0, ErrorCode.InvalidQuantifier);
136-
expectSyntaxError("x{2}+", "", "xx", 0, ErrorCode.InvalidQuantifier);
137-
expectSyntaxError("x{2}+", "", "xxx", 0, ErrorCode.InvalidQuantifier);
138-
expectSyntaxError("x{2}+", "", "xxxx", 0, ErrorCode.InvalidQuantifier);
139-
expectSyntaxError("x{2}*", "", "xxxx", 0, ErrorCode.InvalidQuantifier);
140-
expectSyntaxError("x{2}*?", "", "xxxx", 0, ErrorCode.InvalidQuantifier);
141-
expectSyntaxError("x{2}*???", "", "xxxx", 0, ErrorCode.InvalidQuantifier);
142135
test("\\A*x\\Z+", "", "x", 0, true, 0, 1);
143136
test("\\A*x\\Z+", "", "xx", 0, true, 1, 2);
144137
test("\\A+x\\Z+", "", "xx", 0, false);
145-
expectSyntaxError("x????", "", "x?", 0, ErrorCode.InvalidQuantifier);
146-
expectSyntaxError("x????", "", "xx?", 0, ErrorCode.InvalidQuantifier);
147-
expectSyntaxError("x??????", "", "x?", 0, ErrorCode.InvalidQuantifier);
148-
expectSyntaxError("x??????", "", "xx?", 0, ErrorCode.InvalidQuantifier);
149138
test("x{2}?", "", "xxxxx", 0, true, 0, 2);
150-
expectSyntaxError("x{2}??", "", "xxxxx", 0, ErrorCode.InvalidQuantifier);
151-
expectSyntaxError("x{2}+", "", "xxxxx", 0, ErrorCode.InvalidQuantifier);
152-
expectSyntaxError("x{2}*", "", "xxxxx", 0, ErrorCode.InvalidQuantifier);
153-
expectSyntaxError("x???", "", "x", 0, ErrorCode.InvalidQuantifier);
154-
expectSyntaxError("x{2}*??", "", "xxxx", 0, ErrorCode.InvalidQuantifier);
155-
expectSyntaxError("x???", "", "x?", 0, ErrorCode.InvalidQuantifier);
156-
expectSyntaxError("x???", "", "xx?", 0, ErrorCode.InvalidQuantifier);
157-
expectSyntaxError("x?????", "", "x?", 0, ErrorCode.InvalidQuantifier);
158-
expectSyntaxError("x?????", "", "xx?", 0, ErrorCode.InvalidQuantifier);
159139
test("(a{0,1})*b\\1", "", "aab", 0, true, 0, 3, 2, 2);
160140
test("(a{0,1})*b\\1", "", "aaba", 0, true, 0, 3, 2, 2);
161141
test("(a{0,1})*b\\1", "", "aabaa", 0, true, 0, 3, 2, 2);
@@ -994,7 +974,6 @@ public void generatedTests() {
994974
test("a(()|()|b|()|())*c", "", "abbc", 0, true, 0, 4, 3, 3, 3, 3, -1, -1, -1, -1, -1, -1);
995975
test("a(()|()|()|b|())*c", "", "abbc", 0, true, 0, 4, 3, 3, 3, 3, -1, -1, -1, -1, -1, -1);
996976
test("a(()|()|()|()|b)*c", "", "abbc", 0, true, 0, 4, 3, 3, 3, 3, -1, -1, -1, -1, -1, -1);
997-
expectSyntaxError("a??+", "", "aaa", 0, ErrorCode.InvalidQuantifier);
998977
test("()??()??()??()??()??()??()??()??\\3\\5\\7", "", "a", 0, true, 0, 0, -1, -1, -1, -1, 0, 0, -1, -1, 0, 0, -1, -1, 0, 0, -1, -1);
999978
test("()*", "", "a", 0, true, 0, 0, 0, 0);
1000979
test("(a|)*", "", "a", 0, true, 0, 1, 1, 1);
@@ -1017,24 +996,11 @@ public void generatedTests() {
1017996
expectSyntaxError("[y-\\{][y-\\{]", "", "I", 0, ErrorCode.InvalidCharacterClass);
1018997
test("a?", "", "aaa", 0, true, 0, 1);
1019998
test("a??", "", "aaa", 0, true, 0, 0);
1020-
expectSyntaxError("a???", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1021999
test("a+?", "", "aaa", 0, true, 0, 1);
1022-
expectSyntaxError("a+??", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1023-
expectSyntaxError("a??+", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1024-
expectSyntaxError("a?+", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1025-
expectSyntaxError("a?+?", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1026-
expectSyntaxError("a?+??", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1027-
expectSyntaxError("a?*??", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1028-
expectSyntaxError("(a?)*??", "", "aaa", 0, ErrorCode.InvalidQuantifier);
10291000
test("((a?)*)??", "", "aaa", 0, true, 0, 0, -1, -1, -1, -1);
10301001
test("((a?)*?)?", "", "aaa", 0, true, 0, 0, 0, 0, -1, -1);
1031-
expectSyntaxError("a?*?", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1032-
expectSyntaxError("a*??", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1033-
expectSyntaxError("a+*?", "", "aaa", 0, ErrorCode.InvalidQuantifier);
10341002
test("(a+)*?", "", "aaa", 0, true, 0, 0, -1, -1);
10351003
test("((a+)*)?", "", "aaa", 0, true, 0, 3, 0, 3, 0, 3);
1036-
expectSyntaxError("a+*??", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1037-
expectSyntaxError("a++?", "", "aaa", 0, ErrorCode.InvalidQuantifier);
10381004
expectSyntaxError("[[.\\a.]]", "", ".", 0, ErrorCode.InvalidCharacterClass);
10391005
test("[[...]]", "", ".", 0, true, 0, 1);
10401006
test("[[...]]", "", "[", 0, false);
@@ -1049,12 +1015,6 @@ public void generatedTests() {
10491015
test("[[...]a]a", "", "a", 0, false);
10501016
test("[[...]a]?a", "", "a", 0, true, 0, 1);
10511017
test("[[...]a]|a", "", "a", 0, true, 0, 1);
1052-
expectSyntaxError("a++?", "", "aaa", 0, ErrorCode.InvalidQuantifier);
1053-
expectSyntaxError("\\D|++?", "", "9", 0, ErrorCode.InvalidQuantifier);
1054-
expectSyntaxError("\\D|++?^", "", "9", 0, ErrorCode.InvalidQuantifier);
1055-
expectSyntaxError("\\S|\\D|++?^(3)", "", "9", 0, ErrorCode.InvalidQuantifier);
1056-
expectSyntaxError("\\S|\\D|++?^((3)|[R-_\\(/])t[[:alnum:]]c", "", "9", 0, ErrorCode.InvalidQuantifier);
1057-
expectSyntaxError("(\\d)|5+*?|[[:lower:]][[=l=]]^%", "", "\u0169\u2113%", 0, ErrorCode.InvalidQuantifier);
10581018
test("[[===]]", "", "=", 0, true, 0, 1);
10591019
expectSyntaxError("[[=\\==]]", "", "=", 0, ErrorCode.InvalidCharacterClass);
10601020
expectSyntaxError("[[=\\==]]", "", "\\", 0, ErrorCode.InvalidCharacterClass);
@@ -1099,23 +1059,12 @@ public void generatedTests() {
10991059
test("\\[[b-b]", "", "[b-b]", 0, true, 0, 2);
11001060
test("\\[c-b]", "", "[c-b]", 0, true, 0, 5);
11011061
expectSyntaxError("\\[[c-b]", "", "[c-b]", 0, ErrorCode.InvalidCharacterClass);
1102-
expectSyntaxError("()?*", "", "c", 0, ErrorCode.InvalidQuantifier);
1103-
expectSyntaxError("()?*|", "", "c", 0, ErrorCode.InvalidQuantifier);
1104-
expectSyntaxError("()?*||", "", "c", 0, ErrorCode.InvalidQuantifier);
1105-
expectSyntaxError("()?*||a", "", "b", 0, ErrorCode.InvalidQuantifier);
1106-
expectSyntaxError("()?*||^a\\Zb", "", "c", 0, ErrorCode.InvalidQuantifier);
11071062
test("ac??bc?", "", "abc", 0, true, 0, 3);
11081063
test("ac??bc?", "", "acbc", 0, true, 0, 4);
11091064
test("a?", "", "a", 0, true, 0, 1);
11101065
test("a??", "", "a", 0, true, 0, 0);
1111-
expectSyntaxError("a???", "", "a", 0, ErrorCode.InvalidQuantifier);
1112-
expectSyntaxError("(a)???", "", "a", 0, ErrorCode.InvalidQuantifier);
11131066
test("(a?)??", "", "a", 0, true, 0, 0, -1, -1);
11141067
test("(a??)?", "", "a", 0, true, 0, 0, 0, 0);
1115-
expectSyntaxError("(a???)", "", "a", 0, ErrorCode.InvalidQuantifier);
1116-
expectSyntaxError("a{0,1}??", "", "a", 0, ErrorCode.InvalidQuantifier);
1117-
expectSyntaxError("a??{0,1}", "", "a", 0, ErrorCode.InvalidQuantifier);
1118-
expectSyntaxError("a{0,1}?{0,1}", "", "a", 0, ErrorCode.InvalidQuantifier);
11191068
test("(a{0,1})*", "", "aaaaaa", 0, true, 0, 6, 6, 6);
11201069
test("(a{0,2})*", "", "aaaaaa", 0, true, 0, 6, 6, 6);
11211070
test("(a{1,2})*", "", "aaaaaa", 0, true, 0, 6, 4, 6);
@@ -1665,6 +1614,8 @@ public void generatedTests() {
16651614
test("(e?\\D[xg]){87,87}z", "",
16661615
"axaxeageagageaxeaxeaxageaxagageaxeaxagageagaxaxeagaxeaxagagaxeagageaxeaxeagageaxeaxagaxaxaxageageagageagaxaxaxageaxageaxeageaxaxaxaxaxagaxagageaxeageageageaxeaxeaxageaxaxeaxeagaxagageaxeageaxeaxaxeaxageaxaxeagaxageageaz",
16671616
0, false);
1617+
test("((b\\2{1400,1400})+|)*a", "", "a", 0, true, 0, 1, 0, 0, -1, -1);
1618+
test("\\S(\\w?\\W){8,9}\\Z", "", "-a--------- ---------", 0, true, 13, 22, 21, 22);
16681619
test("(a{1100,1100})\\1", "i", "a".repeat(2400), 0, true, 0, 2200, 0, 1100);
16691620
test("[a]\\S{213,213}bcdz", "", "a".repeat(215) + ("bcxd" + "a".repeat(213)).repeat(3), 0, false);
16701621

@@ -1700,4 +1651,52 @@ public void orcl38190286() {
17001651
test("[[:alpha:]]", "", Collections.emptyMap(), Encodings.UTF_16, "\uDDF2", 0, false);
17011652
test("[[:alpha:]]", "", Collections.emptyMap(), Encodings.UTF_16, "\uD839\uDDF2", 0, false);
17021653
}
1654+
1655+
@Test
1656+
public void bqTransitionExplosion() {
1657+
test("(a(b(b(b(b(b(b(b(b(b(b(b(b(b(b(b(b(b(b(b|)|)|)|)|)|)|)|)|)|)|)|)|)|)|)|)|)|)|){2,2}c)de", "", Map.of("regexDummyLang.QuantifierUnrollLimitGroup", "1"),
1658+
"abbbbbbbcdebbbbbbbf", 0, true, 0, 11, 0, 9, 8, 8, 2, 8, 3, 8, 4, 8, 5, 8, 6, 8, 7, 8, 8, 8);
1659+
}
1660+
1661+
@Test
1662+
public void testNestedQuantifierBailout() {
1663+
expectUnsupported("()?*");
1664+
expectUnsupported("()?*|");
1665+
expectUnsupported("()?*||");
1666+
expectUnsupported("()?*||a");
1667+
expectUnsupported("(a)???");
1668+
expectUnsupported("(a?)*??");
1669+
expectUnsupported("(a???)");
1670+
expectUnsupported("a*??");
1671+
expectUnsupported("a+*?");
1672+
expectUnsupported("a+*??");
1673+
expectUnsupported("a++?");
1674+
expectUnsupported("a+??");
1675+
expectUnsupported("a?*?");
1676+
expectUnsupported("a?*??");
1677+
expectUnsupported("a?+");
1678+
expectUnsupported("a?+?");
1679+
expectUnsupported("a?+??");
1680+
expectUnsupported("a??+");
1681+
expectUnsupported("a???");
1682+
expectUnsupported("a??{0,1}");
1683+
expectUnsupported("a{0,1}??");
1684+
expectUnsupported("a{0,1}?{0,1}");
1685+
expectUnsupported("()?*||^a\\Zb");
1686+
expectUnsupported("\\D|++?");
1687+
expectUnsupported("\\D|++?^");
1688+
expectUnsupported("(\\d)|5+*?|[[:lower:]][[=l=]]^%");
1689+
expectUnsupported("\\S|\\D|++?^(3)");
1690+
expectUnsupported("\\S|\\D|++?^((3)|[R-_\\(/])t[[:alnum:]]c");
1691+
expectUnsupported("x???");
1692+
expectUnsupported("x????");
1693+
expectUnsupported("x?????");
1694+
expectUnsupported("x??????");
1695+
expectUnsupported("x{2}*");
1696+
expectUnsupported("x{2}*?");
1697+
expectUnsupported("x{2}*??");
1698+
expectUnsupported("x{2}*???");
1699+
expectUnsupported("x{2}+");
1700+
expectUnsupported("x{2}??");
1701+
}
17031702
}

regex/src/com.oracle.truffle.regex.test/src/com/oracle/truffle/regex/tregex/test/RegexTestBase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ private static void validateResult(String pattern, String flags, Map<String, Str
260260
// print(pattern, input, fromIndex, result, groupCount, captureGroupBoundsAndLastGroup);
261261
}
262262

263-
void expectUnsupported(String pattern, String flags) {
264-
expectUnsupported(pattern, flags, Collections.emptyMap());
263+
void expectUnsupported(String pattern) {
264+
expectUnsupported(pattern, "", Collections.emptyMap());
265265
}
266266

267267
void expectUnsupported(String pattern, String flags, Map<String, String> options) {

regex/src/com.oracle.truffle.regex.test/src/com/oracle/truffle/regex/tregex/test/RubyTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,9 @@ public void nonRecursiveSubexpressionCalls() {
422422

423423
@Test
424424
public void recursiveSubexpressionCalls() {
425-
expectUnsupported("(a\\g<1>?)(b\\g<2>?)", "");
426-
expectUnsupported("(?<a>a\\g<b>?)(?<b>b\\g<a>?)", "");
427-
expectUnsupported("a\\g<0>?", "");
425+
expectUnsupported("(a\\g<1>?)(b\\g<2>?)");
426+
expectUnsupported("(?<a>a\\g<b>?)(?<b>b\\g<a>?)");
427+
expectUnsupported("a\\g<0>?");
428428
}
429429

430430
@Test
@@ -540,7 +540,7 @@ public void gr39214() {
540540

541541
@Test
542542
public void gr41489() {
543-
expectUnsupported("\\((?>[^)(]+|\\g<0>)*\\)", "");
543+
expectUnsupported("\\((?>[^)(]+|\\g<0>)*\\)");
544544
}
545545

546546
@Test

regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/oracledb/OracleDBRegexParser.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.oracle.truffle.regex.RegexSource;
4949
import com.oracle.truffle.regex.RegexSyntaxException;
5050
import com.oracle.truffle.regex.RegexSyntaxException.ErrorCode;
51+
import com.oracle.truffle.regex.UnsupportedRegexException;
5152
import com.oracle.truffle.regex.charset.ClassSetContents;
5253
import com.oracle.truffle.regex.charset.CodePointSet;
5354
import com.oracle.truffle.regex.charset.CodePointSetAccumulator;
@@ -169,7 +170,7 @@ public RegexAST parse() throws RegexSyntaxException {
169170
break;
170171
case quantifier:
171172
if (prevKind == Token.Kind.quantifier) {
172-
throw syntaxError(OracleDBErrorMessages.NESTED_QUANTIFIER, ErrorCode.InvalidQuantifier);
173+
throw new UnsupportedRegexException(OracleDBErrorMessages.NESTED_QUANTIFIER);
173174
}
174175
if (astBuilder.getCurTerm() == null || prevKind == Token.Kind.captureGroupBegin) {
175176
// quantifiers without target are ignored

regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/TRegexOptions.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,12 @@ public class TRegexOptions {
183183
*/
184184
public static final int TRegexQuantifierUnrollLimitGroup = 6;
185185

186+
/**
187+
* Quantified groups whose node count is greater that this threshold will not be considered for
188+
* quantifier unrolling.
189+
*/
190+
public static final int TRegexQuantifierUnrollLimitGroupNodeCount = 100;
191+
186192
/**
187193
* Bailout threshold for number of capture groups.
188194
*/

regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/automaton/StateTransitionCanonicalizer.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242

4343
import java.util.Arrays;
4444
import java.util.Iterator;
45+
import java.util.Objects;
46+
47+
import org.graalvm.collections.EconomicSet;
4548

4649
import com.oracle.truffle.regex.charset.CodePointSet;
4750
import com.oracle.truffle.regex.tregex.buffer.CompilationBuffer;
@@ -69,6 +72,7 @@ public abstract class StateTransitionCanonicalizer<SI extends StateIndex<? super
6972
private boolean anyArgConstraints;
7073
private final IntArrayBuffer stack = new IntArrayBuffer();
7174
private final IntArrayBuffer skipStack = new IntArrayBuffer();
75+
private final EconomicSet<ConstraintDeduplicationKey> constraintDeduplicationSet = EconomicSet.create();
7276

7377
private static final int INITIAL_CAPACITY = 8;
7478

@@ -287,6 +291,7 @@ private void calcDisjointTransitions(CompilationBuffer compilationBuffer) {
287291
for (int i : intersectingArgs[iStage1]) {
288292
addToStack(argTransitions.get(i), matcher, argConstraints.get(i), argOperations.get(i), resultLength);
289293
}
294+
constraintDeduplicationSet.clear();
290295
outer: while (!stack.isEmpty()) {
291296
int i = stack.pop();
292297
T argTransition = argTransitions.get(i);
@@ -301,30 +306,36 @@ private void calcDisjointTransitions(CompilationBuffer compilationBuffer) {
301306
if (constraintMerge == null) {
302307
continue;
303308
}
304-
assert TransitionConstraint.isNormalized(constraintMerge.middle());
305309

306310
// if the slot already contains an unconditional transition to the final state,
307311
// we don't have to check any other constraints.
308312
if (!(shouldPruneAfterFinalState() && leadsToFinalState.get(j))) {
309313
if (constraintMerge.lhs().length == 0) {
310314
addTransitionTo(j, argTransition, argOperation);
311315
} else {
316+
assert TransitionConstraint.isNormalized(constraintMerge.lhs()[0]);
312317
constraintBuilder[j] = constraintMerge.lhs()[0];
313318
for (int k = 1; k < constraintMerge.lhs().length; ++k) {
314319
duplicateSlot(j, matcher, constraintMerge.lhs()[k]);
315320
resultLength++;
316321
}
317-
318322
duplicateSlot(j, matcher, constraintMerge.middle());
319323
addTransitionTo(resultLength, argTransition, argOperation);
320324
resultLength++;
321325
}
322326
}
323327
for (long[] rhs : constraintMerge.rhs()) {
324-
addToStack(argTransition, matcher, rhs, argOperation, j + 1);
328+
// TransitionConstraint.intersectAndSubtract may yield the same constraint
329+
// formula for the same transition multiple times when intersecting a large
330+
// set of transitions. We drop those duplicates here, otherwise they would
331+
// increase this stage's run time exponentially.
332+
if (constraintDeduplicationSet.add(new ConstraintDeduplicationKey(argTransition.getId(), rhs))) {
333+
addToStack(argTransition, matcher, rhs, argOperation, j + 1);
334+
}
325335
}
326336
continue outer;
327337
}
338+
assert TransitionConstraint.isNormalized(argConstraint);
328339
createSlot();
329340
matcherBuilders[resultLength] = matcher;
330341
constraintBuilder[resultLength] = argConstraint;
@@ -336,6 +347,7 @@ private void calcDisjointTransitions(CompilationBuffer compilationBuffer) {
336347
}
337348

338349
private void duplicateSlot(int i, CodePointSet matcher, long[] constraints) {
350+
assert TransitionConstraint.isNormalized(constraints);
339351
createSlot();
340352
targetStateSets[resultLength] = targetStateSets[i].copy();
341353
transitionLists[resultLength].addAll(transitionLists[i]);
@@ -473,6 +485,22 @@ private TB[] mergeSameTargets(CompilationBuffer compilationBuffer) {
473485
return resultBuffer2.toArray(createResultArray(resultBuffer2.length()));
474486
}
475487

488+
private record ConstraintDeduplicationKey(int transitionID, long[] constraints) {
489+
490+
@Override
491+
public boolean equals(Object obj) {
492+
if (!(obj instanceof ConstraintDeduplicationKey o)) {
493+
return false;
494+
}
495+
return transitionID == o.transitionID && Arrays.equals(constraints, o.constraints);
496+
}
497+
498+
@Override
499+
public int hashCode() {
500+
return Objects.hash(transitionID, Arrays.hashCode(constraints));
501+
}
502+
}
503+
476504
protected abstract TB createTransitionBuilder(T[] transitions, StateSet<SI, S> targetStateSet, CodePointSet matcherBuilder, long[] constraints, long[] operations);
477505

478506
/**

regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/nfa/NFAStateTransition.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ public int hashCode() {
148148
return getId();
149149
}
150150

151+
@Override
152+
public String toString() {
153+
return String.format("%d - %s -> %d", source.getId(), codePointSet, target.getId());
154+
}
155+
151156
@TruffleBoundary
152157
@Override
153158
public JsonValue toJson() {

0 commit comments

Comments
 (0)