diff options
author | Shevek <[email protected]> | 2014-09-09 04:16:03 -0700 |
---|---|---|
committer | Shevek <[email protected]> | 2014-09-09 04:16:03 -0700 |
commit | b6a44c31015a71f4624b3ab0b36503a90023c244 (patch) | |
tree | 70e953547493a674053926a1c2895678474bb3c0 | |
parent | 09e951892e640874756690d3e9f7d07613b4f67b (diff) |
Fix #16: NPE on unterminated ifdef.
-rw-r--r-- | src/main/java/org/anarres/cpp/MacroTokenSource.java | 7 | ||||
-rw-r--r-- | src/main/java/org/anarres/cpp/Preprocessor.java | 40 | ||||
-rw-r--r-- | src/test/java/org/anarres/cpp/PreprocessorTest.java | 23 |
3 files changed, 55 insertions, 15 deletions
diff --git a/src/main/java/org/anarres/cpp/MacroTokenSource.java b/src/main/java/org/anarres/cpp/MacroTokenSource.java index 516e4f2..92317eb 100644 --- a/src/main/java/org/anarres/cpp/MacroTokenSource.java +++ b/src/main/java/org/anarres/cpp/MacroTokenSource.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.util.Iterator; import java.util.List; +import javax.annotation.Nonnull; import static org.anarres.cpp.Token.*; /* This source should always be active, since we don't expand macros @@ -50,7 +51,11 @@ import static org.anarres.cpp.Token.*; } /* XXX Called from Preprocessor [ugly]. */ - /* pp */ static void escape(StringBuilder buf, CharSequence cs) { + /* pp */ static void escape(@Nonnull StringBuilder buf, @Nonnull CharSequence cs) { + if (buf == null) + throw new NullPointerException("Buffer was null."); + if (cs == null) + throw new NullPointerException("CharSequence was null."); for (int i = 0; i < cs.length(); i++) { char c = cs.charAt(i); switch (c) { diff --git a/src/main/java/org/anarres/cpp/Preprocessor.java b/src/main/java/org/anarres/cpp/Preprocessor.java index 0ad1538..f96eb4a 100644 --- a/src/main/java/org/anarres/cpp/Preprocessor.java +++ b/src/main/java/org/anarres/cpp/Preprocessor.java @@ -495,6 +495,7 @@ public class Preprocessor implements Closeable { * @see #push_source(Source,boolean) * @see #pop_source() */ + @CheckForNull protected Source getSource() { return source; } @@ -552,18 +553,30 @@ public class Preprocessor implements Closeable { pop_source(false); } + @Nonnull + private Token next_source() { + if (inputs.isEmpty()) + return new Token(EOF); + Source s = inputs.remove(0); + push_source(s, true); + return line_token(s.getLine(), s.getName(), " 1"); + } + /* Source tokens */ private Token source_token; /* XXX Make this include the NL, and make all cpp directives eat * their own NL. */ @Nonnull - private Token line_token(int line, String name, String extra) { + private Token line_token(int line, @CheckForNull String name, @Nonnull String extra) { StringBuilder buf = new StringBuilder(); buf.append("#line ").append(line) .append(" \""); /* XXX This call to escape(name) is correct but ugly. */ - MacroTokenSource.escape(buf, name); + if (name == null) + buf.append("<unnamed-source>"); + else + MacroTokenSource.escape(buf, name); buf.append("\"").append(extra).append("\n"); return new Token(P_LINE, line, 0, buf.toString(), null); } @@ -583,13 +596,10 @@ public class Preprocessor implements Closeable { for (;;) { Source s = getSource(); if (s == null) { - if (inputs.isEmpty()) - return new Token(EOF); - Source t = inputs.remove(0); - push_source(t, true); - if (getFeature(Feature.LINEMARKERS)) - return line_token(t.getLine(), t.getName(), " 1"); - continue; + Token t = next_source(); + if (t.getType() == P_LINE && !getFeature(Feature.LINEMARKERS)) + continue; + return t; } Token tok = s.token(); /* XXX Refactor with skipline() */ @@ -1675,13 +1685,21 @@ public class Preprocessor implements Closeable { for (;;) { Token tok; if (!isActive()) { + Source s = getSource(); + if (s == null) { + Token t = next_source(); + if (t.getType() == P_LINE && !getFeature(Feature.LINEMARKERS)) + continue; + return t; + } + try { /* XXX Tell lexer to ignore warnings. */ - source.setActive(false); + s.setActive(false); tok = source_token(); } finally { /* XXX Tell lexer to stop ignoring warnings. */ - source.setActive(true); + s.setActive(true); } switch (tok.getType()) { case HASH: diff --git a/src/test/java/org/anarres/cpp/PreprocessorTest.java b/src/test/java/org/anarres/cpp/PreprocessorTest.java index c8bd7a1..7103cba 100644 --- a/src/test/java/org/anarres/cpp/PreprocessorTest.java +++ b/src/test/java/org/anarres/cpp/PreprocessorTest.java @@ -4,6 +4,8 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Before; import org.junit.Test; import static org.anarres.cpp.Token.*; @@ -11,6 +13,8 @@ import static org.junit.Assert.*; public class PreprocessorTest { + private static final Log LOG = LogFactory.getLog(PreprocessorTest.class); + private OutputStreamWriter writer; private Preprocessor p; @@ -153,18 +157,31 @@ public class PreprocessorTest { Token t; do { t = p.token(); - System.out.println("Remaining token " + t); + LOG.warn("Remaining token " + t); + } while (t.getType() != EOF); + } + + @Test + public void testPreprocessorUnterminated() throws Exception { + testInput("#ifndef X\na\n#else\nb\n"); // Bug #16 + + writer.close(); + + Token t; + do { + t = p.token(); + LOG.warn("Remaining token " + t); } while (t.getType() != EOF); } private void testInput(String in, Object... out) throws Exception { - System.out.print("Input: " + in); + LOG.info("Input: " + in); writer.write(in); writer.flush(); for (int i = 0; i < out.length; i++) { Token t = p.token(); - System.out.println(t); + LOG.info(t); Object v = out[i]; if (v instanceof String) { if (t.getType() != STRING) |