| //@ skip if $hostOS != "darwin" or $architecture == "arm" or $architecture == "x86" or not $jitTests |
| // Test that throw an OOM exception when compiling a pathological, but valid nested RegExp. |
| |
| var failures = []; |
| |
| class TestAndExpectedException |
| { |
| constructor(func, exception) |
| { |
| this.func = func; |
| this.exception = exception; |
| } |
| |
| runTest() |
| { |
| try { |
| this.func(); |
| failures.push("Running " + this.func + ", expected OOM exception, but didn't get one"); |
| } catch (e) { |
| let errStr = e.toString(); |
| if (errStr != this.exception) |
| failures.push("Running " + this.func + ", expected: \"" + this.exception + "\" but got \"" + errStr + "\""); |
| } |
| } |
| } |
| |
| function recurseAndTest(depth, testList) |
| { |
| // Probe stack depth |
| try { |
| let result = recurseAndTest(depth + 1, testList); |
| if (result == 0) { |
| // Call the test functions with a nearly full stack. |
| for (const test of testList) |
| test.runTest(); |
| |
| return 1; |
| } else if (result < 0) |
| return result + 1; |
| else |
| return result; |
| } catch (e) { |
| // Go up a several frames and then call the test functions |
| return -24; |
| } |
| |
| return 1; |
| } |
| |
| let deepRE = new RegExp("((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))"); |
| let deepGlobalRE = new RegExp(deepRE, "g"); |
| |
| let matchLen = 401; // The number of parens plus 1 for the whole match. |
| |
| let regExpOOMError = "Error: Out of memory: Invalid regular expression: too many nested disjunctions"; |
| |
| testList = []; |
| |
| // Test that all RegExp related APIs that compile RE's properly handle OOM. |
| testList.push(new TestAndExpectedException(() => { deepRE.exec("x"); }, regExpOOMError)); |
| testList.push(new TestAndExpectedException(() => { deepRE.test("x"); }, regExpOOMError)); |
| testList.push(new TestAndExpectedException(() => { "x".match(deepRE); }, regExpOOMError)); |
| testList.push(new TestAndExpectedException(() => { "x".match(deepGlobalRE); }, regExpOOMError)); |
| testList.push(new TestAndExpectedException(() => { "x".replace(deepGlobalRE, ""); }, regExpOOMError)); |
| testList.push(new TestAndExpectedException(() => { "x".replace(deepGlobalRE, "X"); }, regExpOOMError)); |
| testList.push(new TestAndExpectedException(() => { "x".replace(deepGlobalRE, () => { return "X" }); }, regExpOOMError)); |
| testList.push(new TestAndExpectedException(() => { "x".search(deepRE); }, regExpOOMError)); |
| |
| recurseAndTest(1, testList); |
| |
| if (failures.length) { |
| print("Got the following failures:"); |
| for (const failure of failures) |
| print(failure); |
| throw "Got failures"; |
| } |
| |
| // Test that the RegExp works correctly with RegExp.exec() and RegExp.test() when there is sufficient stack space to compile it. |
| let m = deepRE.exec("x"); |
| let matched = true; |
| if (m.length != matchLen) |
| matched = false |
| else { |
| for (i = 0; i < matchLen; i++) { |
| if (m[i] != "x") |
| matched = false; |
| } |
| } |
| |
| if (!matched) { |
| let expectedMatch = []; |
| for (i = 0; i < matchLen; i++) |
| expectedMatch[i] = "x"; |
| |
| throw "Expected RegExp.exec(...) to be [" + expectedMatch + "] but got [" + m + "]"; |
| } |
| |
| if (!deepRE.test("x")) |
| throw "Expected RegExp.test(...) to be true, but was false"; |