Skip to content

Commit e392856

Browse files
committed
Make regexp execution loop interruptible #1189
Uses Thread.currentThread.isInterrupted() so that the interruption flag remains set to true, we only terminate the RegExp evaluation loop, but other (potentially third-party calling) code may still have to check for the interrupted flag to stop its execution as well. I also added a test with a long-running regexp that fails without the interrupt check.
1 parent e1f9159 commit e392856

File tree

2 files changed

+27
-0
lines changed

2 files changed

+27
-0
lines changed

src/org/mozilla/javascript/regexp/NativeRegExp.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,6 +1878,9 @@ private static boolean executeREBytecode(REGlobalData gData, String input, int e
18781878

18791879
for (; ; ) {
18801880

1881+
if (Thread.currentThread().isInterrupted()) {
1882+
throw new RuntimeException("Received interrupt while executing regexp");
1883+
}
18811884
if (reopIsSimple(op)) {
18821885
int match = simpleMatch(gData, input, op, program, pc, end, true);
18831886
result = match >= 0;

testsrc/org/mozilla/javascript/tests/NativeRegExpTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@
55
package org.mozilla.javascript.tests;
66

77
import static org.junit.Assert.assertEquals;
8+
import static org.junit.Assert.assertThrows;
9+
import static org.junit.Assert.assertTrue;
810

911
import org.junit.Test;
1012
import org.mozilla.javascript.Context;
1113
import org.mozilla.javascript.Scriptable;
1214
import org.mozilla.javascript.ScriptableObject;
1315

16+
import java.util.concurrent.ExecutionException;
17+
import java.util.concurrent.ExecutorService;
18+
import java.util.concurrent.Executors;
19+
import java.util.concurrent.Future;
20+
import java.util.concurrent.TimeUnit;
21+
import java.util.concurrent.TimeoutException;
22+
1423
public class NativeRegExpTest {
1524

1625
@Test
@@ -49,6 +58,21 @@ public void ignoreCase() throws Exception {
4958
testEvaluate("i-false-true-false-false", "/foo/i;");
5059
}
5160

61+
/** @throws Exception if an error occurs */
62+
@Test
63+
public void interruptLongRunningRegExpEvaluation() throws Exception {
64+
ExecutorService executorService = Executors.newSingleThreadExecutor();
65+
try {
66+
Future<?> future = executorService.submit(() -> test("false", "/(.*){1,32000}[bc]/.test(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");"));
67+
assertThrows(TimeoutException.class, () -> future.get(1, TimeUnit.SECONDS));
68+
executorService.shutdownNow();
69+
assertTrue(executorService.awaitTermination(30, TimeUnit.SECONDS));
70+
}
71+
finally {
72+
executorService.shutdown();
73+
}
74+
}
75+
5276
/** @throws Exception if an error occurs */
5377
@Test
5478
public void multilineCtor() throws Exception {

0 commit comments

Comments
 (0)