From 2eb10e0f6089eed0d4cd3483f350db30a34c5cb6 Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Thu, 27 Sep 2012 20:38:42 +0400 Subject: [PATCH] SONAR-3708 Improve exception message from CodeReader in case of StackOverflowError --- .../java/org/sonar/channel/CodeReader.java | 25 +++++++++------ .../java/org/sonar/channel/RegexChannel.java | 26 +++++----------- .../org/sonar/channel/CodeReaderTest.java | 31 ++++++++++++++++--- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/sonar-channel/src/main/java/org/sonar/channel/CodeReader.java b/sonar-channel/src/main/java/org/sonar/channel/CodeReader.java index f1f85e8f206..4ca2328186e 100644 --- a/sonar-channel/src/main/java/org/sonar/channel/CodeReader.java +++ b/sonar-channel/src/main/java/org/sonar/channel/CodeReader.java @@ -48,7 +48,7 @@ public class CodeReader extends CodeBuffer { /** * Creates a code reader with specific configuration parameters. * Note that this constructor will read everything from reader and will close it. - * + * * @param code * the Reader to read code from * @param configuration @@ -60,7 +60,7 @@ public class CodeReader extends CodeBuffer { /** * Creates a code reader with specific configuration parameters. - * + * * @param code * the code itself * @param configuration @@ -72,7 +72,7 @@ public class CodeReader extends CodeBuffer { /** * Read and consume the next character - * + * * @param appendable * the read character is appended to appendable */ @@ -86,7 +86,7 @@ public class CodeReader extends CodeBuffer { /** * Read without consuming the next characters - * + * * @param length * number of character to read * @return array of characters @@ -104,7 +104,7 @@ public class CodeReader extends CodeBuffer { /** * Read without consuming the next characters until a condition is reached (EndMatcher) - * + * * @param matcher * the EndMatcher used to stop the reading * @param appendable @@ -114,7 +114,7 @@ public class CodeReader extends CodeBuffer { int index = 0; char nextChar = charAt(index); try { - while ( !matcher.match(nextChar) && nextChar != -1) { + while (!matcher.match(nextChar) && nextChar != -1) { appendable.append(nextChar); nextChar = charAt(++index); } @@ -142,7 +142,7 @@ public class CodeReader extends CodeBuffer { try { do { appendable.append((char) pop()); - } while ( !matcher.match(peek()) && peek() != -1); + } while (!matcher.match(peek()) && peek() != -1); } catch (IOException e) { throw new ChannelException(e.getMessage(), e); } @@ -150,7 +150,7 @@ public class CodeReader extends CodeBuffer { /** * Read and consume the next characters according to a given regular expression - * + * * @param matcher * the regular expression matcher * @param appendable @@ -164,7 +164,7 @@ public class CodeReader extends CodeBuffer { /** * Read and consume the next characters according to a given regular expression. Moreover the character sequence immediately following the * desired characters must also match a given regular expression. - * + * * @param matcher * the Matcher used to try consuming next characters * @param afterMatcher @@ -180,7 +180,7 @@ public class CodeReader extends CodeBuffer { if (afterMatcher != null) { afterMatcher.reset(this); afterMatcher.region(matcher.end(), length()); - if ( !afterMatcher.lookingAt()) { + if (!afterMatcher.lookingAt()) { return -1; } } @@ -190,6 +190,11 @@ public class CodeReader extends CodeBuffer { } return matcher.end(); } + } catch (StackOverflowError e) { + throw new ChannelException("Unable to apply regular expression '" + matcher.pattern().pattern() + + "' at line " + getCursor().getLine() + " and column " + getCursor().getColumn() + + ", because it led to a stack overflow error." + + " This error may be due to an inefficient use of alternations - see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507"); } catch (IndexOutOfBoundsException e) { return -1; } catch (IOException e) { diff --git a/sonar-channel/src/main/java/org/sonar/channel/RegexChannel.java b/sonar-channel/src/main/java/org/sonar/channel/RegexChannel.java index 1272c0be542..145230f1473 100644 --- a/sonar-channel/src/main/java/org/sonar/channel/RegexChannel.java +++ b/sonar-channel/src/main/java/org/sonar/channel/RegexChannel.java @@ -29,46 +29,36 @@ public abstract class RegexChannel extends Channel { private final StringBuilder tmpBuilder = new StringBuilder(); private final Matcher matcher; - private final String regex; /** * Create a RegexChannel object with the required regular expression - * + * * @param regex * regular expression to be used to try matching the next characters in the stream */ public RegexChannel(String regex) { matcher = Pattern.compile(regex).matcher(""); - this.regex = regex; } @Override public final boolean consume(CodeReader code, OUTPUT output) { - try { - if (code.popTo(matcher, tmpBuilder) > 0) { - consume(tmpBuilder, output); - tmpBuilder.delete(0, tmpBuilder.length()); - return true; - } - return false; - } catch (StackOverflowError e) { - throw new IllegalArgumentException( - "The regular expression " - + regex - + " has led to a stack overflow error. " - + "This error is certainly due to an inefficient use of alternations. See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507", - e); + if (code.popTo(matcher, tmpBuilder) > 0) { + consume(tmpBuilder, output); + tmpBuilder.delete(0, tmpBuilder.length()); + return true; } + return false; } /** * The consume method is called each time the regular expression used to create the RegexChannel object matches the next characters in the * character streams. - * + * * @param token * the token consumed in the character stream and matching the regular expression * @param the * OUPUT object which can be optionally fed */ protected abstract void consume(CharSequence token, OUTPUT output); + } diff --git a/sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java b/sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java index f0cd762825a..d52ae92b2aa 100644 --- a/sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java +++ b/sonar-channel/src/test/java/org/sonar/channel/CodeReaderTest.java @@ -19,18 +19,23 @@ */ package org.sonar.channel; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; import java.io.StringReader; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.junit.Test; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; public class CodeReaderTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Test public void testPopWithAppendable() { CodeReader reader = new CodeReader("package org.sonar;"); @@ -95,6 +100,24 @@ public class CodeReaderTest { assertEquals( -1, reader.popTo(Pattern.compile("\\w+").matcher(new String()), token)); } + @Test + public void test() { + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + for (int i = 0; i < 10000; i++) { + sb.append(Integer.toHexString(i)); + } + CodeReader reader = new CodeReader(sb.toString()); + reader.pop(); + reader.pop(); + + thrown.expect(ChannelException.class); + thrown.expectMessage("Unable to apply regular expression '([a-fA-F]|\\d)+' at line 2 and column 1," + + " because it led to a stack overflow error." + + " This error may be due to an inefficient use of alternations - see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507"); + reader.popTo(Pattern.compile("([a-fA-F]|\\d)+").matcher(""), new StringBuilder()); + } + @Test public void testPopToWithRegexAndFollowingMatcher() { Matcher digitMatcher = Pattern.compile("\\d+").matcher(new String()); -- 2.39.5