aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShevek <[email protected]>2014-09-09 04:16:03 -0700
committerShevek <[email protected]>2014-09-09 04:16:03 -0700
commitb6a44c31015a71f4624b3ab0b36503a90023c244 (patch)
tree70e953547493a674053926a1c2895678474bb3c0
parent09e951892e640874756690d3e9f7d07613b4f67b (diff)
Fix #16: NPE on unterminated ifdef.
-rw-r--r--src/main/java/org/anarres/cpp/MacroTokenSource.java7
-rw-r--r--src/main/java/org/anarres/cpp/Preprocessor.java40
-rw-r--r--src/test/java/org/anarres/cpp/PreprocessorTest.java23
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)