summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-09-17 14:36:22 -0700
committerCliff Click <cliffc@acm.org>2026-09-17 14:36:22 -0700
commit0c99d74c85b937e5d566a7f2899d75025d9c54ea (patch)
tree9f4834d3fb1ec936fbea575d241f05a53c09336e
parent410ee209ba1db89959f8e6be9cdaf2350e70fd27 (diff)
downloadSimple-0c99d74c85b937e5d566a7f2899d75025d9c54ea.tar.gz
Simple-0c99d74c85b937e5d566a7f2899d75025d9c54ea.zip
Get makefiles and fuzzer testing uniform across chapters
-rw-r--r--Makefile16
-rw-r--r--README.md12
-rw-r--r--build-support/chapter-release.mk7
-rw-r--r--build-support/early-chapter.mk45
-rw-r--r--chapter01/Makefile1
-rw-r--r--chapter02/Makefile1
-rw-r--r--chapter03/Makefile1
-rw-r--r--chapter04/Makefile3
-rw-r--r--chapter05/Makefile1
-rw-r--r--chapter06/Makefile3
-rw-r--r--chapter07/Makefile3
-rw-r--r--chapter08/Makefile5
-rw-r--r--chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter09/Makefile5
-rw-r--r--chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter10/Makefile5
-rw-r--r--chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter11/Makefile5
-rw-r--r--chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter12/Makefile5
-rw-r--r--chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter13/Makefile5
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter14/Makefile5
-rw-r--r--chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter15/Makefile5
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java23
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter16/Makefile3
-rw-r--r--chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter17/Makefile3
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter18/Makefile3
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java29
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter19/Makefile3
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter21/Makefile2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter22/Makefile6
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter23/Makefile6
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter24/Makefile6
-rw-r--r--chapter24/README.md18
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java27
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java14
-rw-r--r--chapter25/Makefile3
-rw-r--r--chapter25/README.md210
-rw-r--r--docs/chapter-backports.md176
65 files changed, 1042 insertions, 197 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..0947e7fa
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+# Run chapter targets from the repository root. Override CHAPTERS for a review batch:
+# make tests CHAPTERS="chapter21 chapter22"
+# make -k tests # continue through failures, but return a failing status
+.DEFAULT_GOAL := tests
+CHAPTERS ?= $(sort $(patsubst %/pom.xml,%,$(wildcard chapter[0-9][0-9]/pom.xml)))
+ACTIONS := tests tags release lib
+CHAPTER_TARGETS := $(foreach action,$(ACTIONS),$(addsuffix /$(action),$(CHAPTERS)))
+
+# Keep baseline/review runs sequential, including when invoked with -j.
+.NOTPARALLEL:
+.PHONY: $(ACTIONS) tag $(CHAPTER_TARGETS)
+tag: tags
+$(foreach action,$(ACTIONS),$(eval $(action): $(addsuffix /$(action),$(CHAPTERS))))
+
+$(CHAPTER_TARGETS):
+ +$(MAKE) -C $(dir $@) $(notdir $@)
diff --git a/README.md b/README.md
index e77a6d7a..9698e35a 100644
--- a/README.md
+++ b/README.md
@@ -68,4 +68,14 @@ The Simple language is styled after a subset of C or Java.
* [Chapter 22](chapter22/README.md): A Simple Hello, World!
* [Chapter 23](chapter23/README.md): Methods and Types Revisited
* [Chapter 24](chapter24/README.md): Chained conditionals and SCCP
-* [Chapter 25](chapter25/README.md): Modules and Separate Compilation
+* [Chapter 25](chapter25/README.md): Modules, Separate Compilation, and SSA Construction with Incomplete Types
+
+## Building across chapters
+
+The top-level Makefile runs each chapter's `tests`, `tags` (also `tag`),
+`release`, or `lib` target. Start with `make lib` on a fresh checkout, then
+`make tests`.
+
+The [chapter backport queue](docs/chapter-backports.md) records proposed small
+corrections and the per-chapter test/review workflow. Larger architectural moves
+remain deferred while that workflow is established.
diff --git a/build-support/chapter-release.mk b/build-support/chapter-release.mk
new file mode 100644
index 00000000..74e216f4
--- /dev/null
+++ b/build-support/chapter-release.mk
@@ -0,0 +1,7 @@
+# Jar packaging for chapters predating the existing release target.
+.PHONY: release
+release: build/release/simple.jar
+
+build/release/simple.jar: $(main_classes) $(test_classes)
+ @mkdir -p build/release
+ jar cf $@ -C $(CLZDIR)/main . -C $(CLZDIR)/test .
diff --git a/build-support/early-chapter.mk b/build-support/early-chapter.mk
new file mode 100644
index 00000000..939b7782
--- /dev/null
+++ b/build-support/early-chapter.mk
@@ -0,0 +1,45 @@
+# Chapters 1-3 and 5 previously only had Maven builds.
+# Included with the chapter as the working directory.
+SHELL := /bin/bash
+.DEFAULT_GOAL := tests
+.DELETE_ON_ERROR:
+SIMPLE := com/seaofnodes/simple
+SRC := src/main/java
+TST := src/test/java
+CLZDIR := build/classes
+SEP := $(if $(filter Windows_NT,$(OS)),;,:)
+CTAGS ?= ctags
+JAVAC_ARGS ?= -g
+main_javas := $(wildcard $(SRC)/$(SIMPLE)/*.java $(SRC)/$(SIMPLE)/*/*.java)
+test_javas := $(wildcard $(TST)/$(SIMPLE)/*.java $(TST)/$(SIMPLE)/*/*.java)
+test_cp := $(patsubst $(TST)/$(SIMPLE)/%.java,com.seaofnodes.simple.%,$(wildcard $(TST)/$(SIMPLE)/*Test.java))
+jars := lib/junit-4.12.jar$(SEP)lib/hamcrest-core-1.3.jar
+main_classes := $(CLZDIR)/main/.mtag
+test_classes := $(CLZDIR)/test/.ttag
+
+$(main_classes): $(main_javas)
+ @mkdir -p $(CLZDIR)/main
+ javac $(JAVAC_ARGS) -d $(CLZDIR)/main $(main_javas)
+ @touch $@
+
+$(test_classes): $(test_javas) $(main_classes) lib/junit-4.12.jar lib/hamcrest-core-1.3.jar
+ @mkdir -p $(CLZDIR)/test
+ javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -d $(CLZDIR)/test $(test_javas)
+ @touch $@
+
+.PHONY: tests tags lib
+tests: $(test_classes)
+ java -ea -cp "$(CLZDIR)/main$(SEP)$(CLZDIR)/test$(SEP)$(jars)" org.junit.runner.JUnitCore $(test_cp)
+
+tags:
+ $(CTAGS) -e --recurse=yes --fields=+fksaiS $(SRC) $(TST)
+
+lib: lib/junit-4.12.jar lib/hamcrest-core-1.3.jar
+lib/junit-4.12.jar:
+ @mkdir -p lib
+ wget -O $@ https://repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar
+lib/hamcrest-core-1.3.jar:
+ @mkdir -p lib
+ wget -O $@ https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
+
+include ../build-support/chapter-release.mk
diff --git a/chapter01/Makefile b/chapter01/Makefile
new file mode 100644
index 00000000..86e9ebba
--- /dev/null
+++ b/chapter01/Makefile
@@ -0,0 +1 @@
+include ../build-support/early-chapter.mk
diff --git a/chapter02/Makefile b/chapter02/Makefile
new file mode 100644
index 00000000..86e9ebba
--- /dev/null
+++ b/chapter02/Makefile
@@ -0,0 +1 @@
+include ../build-support/early-chapter.mk
diff --git a/chapter03/Makefile b/chapter03/Makefile
new file mode 100644
index 00000000..86e9ebba
--- /dev/null
+++ b/chapter03/Makefile
@@ -0,0 +1 @@
+include ../build-support/early-chapter.mk
diff --git a/chapter04/Makefile b/chapter04/Makefile
index c7724bc7..7f55b343 100644
--- a/chapter04/Makefile
+++ b/chapter04/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter05/Makefile b/chapter05/Makefile
new file mode 100644
index 00000000..86e9ebba
--- /dev/null
+++ b/chapter05/Makefile
@@ -0,0 +1 @@
+include ../build-support/early-chapter.mk
diff --git a/chapter06/Makefile b/chapter06/Makefile
index 56f69e12..cb362b42 100644
--- a/chapter06/Makefile
+++ b/chapter06/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -90,3 +91,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter07/Makefile b/chapter07/Makefile
index 56f69e12..cb362b42 100644
--- a/chapter07/Makefile
+++ b/chapter07/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -90,3 +91,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter08/Makefile b/chapter08/Makefile
index 3c271042..742a9aef 100644
--- a/chapter08/Makefile
+++ b/chapter08/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -66,7 +67,7 @@ $(test_classes): $(CLZDIR)/test/%class: $(TST)/%java $(main_classes)
JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
- $(JVM) org.junit.runner.JUnitCore $(test_cp)
+ $(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
.PHONY: clean
clean:
@@ -90,3 +91,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 82a6a472..33434b6c 100644
--- a/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter08/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.FuzzerWrap#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Test
@Ignore
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<100000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 5fcd8764..861c8b3a 100644
--- a/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter08/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -125,4 +125,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, false).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter09/Makefile b/chapter09/Makefile
index b5c21b38..f141d0f6 100644
--- a/chapter09/Makefile
+++ b/chapter09/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
@echo "testing " $(test_cp)
- @$(JVM) org.junit.runner.JUnitCore $(test_cp)
+ @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
fuzzer: $(default_targets)
@echo "testing " $(test_cp)
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index bb3bec1c..019140ad 100644
--- a/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter09/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Ignore;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.FuzzerWrap#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Test
@Ignore
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 936fb41e..b816ed86 100644
--- a/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter09/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -167,4 +167,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, false).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter10/Makefile b/chapter10/Makefile
index c7724bc7..ed0a03c9 100644
--- a/chapter10/Makefile
+++ b/chapter10/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
@echo "testing " $(test_cp)
- @$(JVM) org.junit.runner.JUnitCore $(test_cp)
+ @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
fuzzer: $(default_targets)
@echo "fuzzing " $(test_cp)
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index bb3bec1c..019140ad 100644
--- a/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter10/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Ignore;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.FuzzerWrap#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Test
@Ignore
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter10/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter11/Makefile b/chapter11/Makefile
index c7724bc7..ed0a03c9 100644
--- a/chapter11/Makefile
+++ b/chapter11/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
@echo "testing " $(test_cp)
- @$(JVM) org.junit.runner.JUnitCore $(test_cp)
+ @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
fuzzer: $(default_targets)
@echo "fuzzing " $(test_cp)
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 699711fc..52c2a014 100644
--- a/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter11/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Ignore;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter11/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter12/Makefile b/chapter12/Makefile
index c7724bc7..ed0a03c9 100644
--- a/chapter12/Makefile
+++ b/chapter12/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
@echo "testing " $(test_cp)
- @$(JVM) org.junit.runner.JUnitCore $(test_cp)
+ @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
fuzzer: $(default_targets)
@echo "fuzzing " $(test_cp)
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 699711fc..52c2a014 100644
--- a/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter12/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Ignore;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter12/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter13/Makefile b/chapter13/Makefile
index c7724bc7..ed0a03c9 100644
--- a/chapter13/Makefile
+++ b/chapter13/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
@echo "testing " $(test_cp)
- @$(JVM) org.junit.runner.JUnitCore $(test_cp)
+ @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
fuzzer: $(default_targets)
@echo "fuzzing " $(test_cp)
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 699711fc..52c2a014 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Ignore;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter14/Makefile b/chapter14/Makefile
index c7724bc7..ed0a03c9 100644
--- a/chapter14/Makefile
+++ b/chapter14/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
@echo "testing " $(test_cp)
- @$(JVM) org.junit.runner.JUnitCore $(test_cp)
+ @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
fuzzer: $(default_targets)
@echo "fuzzing " $(test_cp)
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 699711fc..52c2a014 100644
--- a/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter14/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Ignore;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter14/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter15/Makefile b/chapter15/Makefile
index c7724bc7..ed0a03c9 100644
--- a/chapter15/Makefile
+++ b/chapter15/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -67,7 +68,7 @@ JVM=nice java -ea -cp "build/classes/main${SEP}${jars}${SEP}$(CLZDIR)/test"
tests: $(default_targets)
@echo "testing " $(test_cp)
- @$(JVM) org.junit.runner.JUnitCore $(test_cp)
+ @$(JVM) org.junit.runner.JUnitCore $(test_cp) com.seaofnodes.simple.FuzzerWrap
fuzzer: $(default_targets)
@echo "fuzzing " $(test_cp)
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 699711fc..52c2a014 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -8,15 +8,30 @@ import org.junit.Ignore;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter16/Makefile b/chapter16/Makefile
index c14ffa27..ed0a03c9 100644
--- a/chapter16/Makefile
+++ b/chapter16/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index eaeed8e2..2c98f405 100644
--- a/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter16/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter16/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter17/Makefile b/chapter17/Makefile
index c14ffa27..ed0a03c9 100644
--- a/chapter17/Makefile
+++ b/chapter17/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
# for printing variable values
@@ -96,3 +97,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index eaeed8e2..2c98f405 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 51e39cd0..a82a7517 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -182,4 +182,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter18/Makefile b/chapter18/Makefile
index f42659de..c2e1b060 100644
--- a/chapter18/Makefile
+++ b/chapter18/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
@@ -103,3 +104,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index d67b2065..1427c99d 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,32 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run only this chapter's fixed regression seeds. Open failures
+ * and exploratory fuzzing are opt-in: run their methods directly in the IDE,
+ * or temporarily remove the relevant Ignore annotation when investigating.
+ * Seeds belong to this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ 973358943756616234L, // Parser failure on nullable field access after dead code
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +40,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index f57a0031..9f2ffa4d 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -172,4 +172,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter19/Makefile b/chapter19/Makefile
index 4c1e1f49..3a6faaff 100644
--- a/chapter19/Makefile
+++ b/chapter19/Makefile
@@ -1,4 +1,5 @@
SHELL := /bin/bash
+# Shared packaging target; included after chapter source variables below.
.DELETE_ON_ERROR:
@@ -103,3 +104,5 @@ lib/hamcrest-core-1.3.jar:
tags: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
+
+include ../build-support/chapter-release.mk
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index d67b2065..b022bec9 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index f7e0c4ad..612dec65 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -171,4 +171,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index d67b2065..b022bec9 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 30a548ad..40d69a62 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -171,4 +171,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter21/Makefile b/chapter21/Makefile
index 0d187b7c..055c632a 100644
--- a/chapter21/Makefile
+++ b/chapter21/Makefile
@@ -111,4 +111,4 @@ lib/hamcrest-core-1.3.jar:
# Build emacs tags (part of a tasty emacs ide experience)
tags: $(main_javas) $(test_javas)
@rm -f TAGS
- @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST)
+ @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index d67b2065..b022bec9 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 30a548ad..40d69a62 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -171,4 +171,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter22/Makefile b/chapter22/Makefile
index 6547a73a..deca5c76 100644
--- a/chapter22/Makefile
+++ b/chapter22/Makefile
@@ -89,7 +89,7 @@ $(CLZDIR)/main/.mtag: $(main_classes) $(main_javas)
@# This crap is really just "javac", but with a beautiful error message.
@# The very very long list of java files is suppressed and counted.
@# The required output "Note: blah blah deprecated blah" is suppressed.
- $(file > .argsM.txt , $(OODM))
+ $(file >.argsM.txt,$(OODM))
@if [ ! -z "$(OODM)" ] ; then \
echo "compiling main because " $< " and " `wc -w < .argsM.txt` " more files" ; \
if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(OODM) >& .out.txt ; then \
@@ -111,7 +111,7 @@ $(main_classes): $(CLZDIR)/main/%class: $(SRC)/%java
OODT :=
$(CLZDIR)/test/.ttag: $(test_classes) $(test_javas) $(CLZDIR)/main/.mtag
@[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test
- $(file > .argsT.txt $(OODT))
+ $(file >.argsT.txt,$(OODT))
@if [ ! -z "$(OODT)" ] ; then \
echo "compiling test because " $< " and " `wc -w < .argsT.txt` " more files" ; \
if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(OODT) >& .out.txt ; then \
@@ -177,4 +177,4 @@ lib/hamcrest-core-1.3.jar:
# Build emacs tags (part of a tasty emacs ide experience)
tags: $(main_javas) $(test_javas)
@rm -f TAGS
- @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST)
+ @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index d67b2065..b022bec9 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 30a548ad..40d69a62 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -171,4 +171,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter23/Makefile b/chapter23/Makefile
index 6547a73a..deca5c76 100644
--- a/chapter23/Makefile
+++ b/chapter23/Makefile
@@ -89,7 +89,7 @@ $(CLZDIR)/main/.mtag: $(main_classes) $(main_javas)
@# This crap is really just "javac", but with a beautiful error message.
@# The very very long list of java files is suppressed and counted.
@# The required output "Note: blah blah deprecated blah" is suppressed.
- $(file > .argsM.txt , $(OODM))
+ $(file >.argsM.txt,$(OODM))
@if [ ! -z "$(OODM)" ] ; then \
echo "compiling main because " $< " and " `wc -w < .argsM.txt` " more files" ; \
if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(OODM) >& .out.txt ; then \
@@ -111,7 +111,7 @@ $(main_classes): $(CLZDIR)/main/%class: $(SRC)/%java
OODT :=
$(CLZDIR)/test/.ttag: $(test_classes) $(test_javas) $(CLZDIR)/main/.mtag
@[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test
- $(file > .argsT.txt $(OODT))
+ $(file >.argsT.txt,$(OODT))
@if [ ! -z "$(OODT)" ] ; then \
echo "compiling test because " $< " and " `wc -w < .argsT.txt` " more files" ; \
if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(OODT) >& .out.txt ; then \
@@ -177,4 +177,4 @@ lib/hamcrest-core-1.3.jar:
# Build emacs tags (part of a tasty emacs ide experience)
tags: $(main_javas) $(test_javas)
@rm -f TAGS
- @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST)
+ @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index d67b2065..b022bec9 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 77e61b42..962202a6 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -171,4 +171,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter24/Makefile b/chapter24/Makefile
index 6547a73a..deca5c76 100644
--- a/chapter24/Makefile
+++ b/chapter24/Makefile
@@ -89,7 +89,7 @@ $(CLZDIR)/main/.mtag: $(main_classes) $(main_javas)
@# This crap is really just "javac", but with a beautiful error message.
@# The very very long list of java files is suppressed and counted.
@# The required output "Note: blah blah deprecated blah" is suppressed.
- $(file > .argsM.txt , $(OODM))
+ $(file >.argsM.txt,$(OODM))
@if [ ! -z "$(OODM)" ] ; then \
echo "compiling main because " $< " and " `wc -w < .argsM.txt` " more files" ; \
if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(SRC) -d $(CLZDIR)/main $(OODM) >& .out.txt ; then \
@@ -111,7 +111,7 @@ $(main_classes): $(CLZDIR)/main/%class: $(SRC)/%java
OODT :=
$(CLZDIR)/test/.ttag: $(test_classes) $(test_javas) $(CLZDIR)/main/.mtag
@[ -d $(CLZDIR)/test ] || mkdir -p $(CLZDIR)/test
- $(file > .argsT.txt $(OODT))
+ $(file >.argsT.txt,$(OODT))
@if [ ! -z "$(OODT)" ] ; then \
echo "compiling test because " $< " and " `wc -w < .argsT.txt` " more files" ; \
if ! javac $(JAVAC_ARGS) -cp "$(CLZDIR)/test$(SEP)$(CLZDIR)/main$(SEP)$(jars)" -sourcepath $(TST) -d $(CLZDIR)/test $(OODT) >& .out.txt ; then \
@@ -177,4 +177,4 @@ lib/hamcrest-core-1.3.jar:
# Build emacs tags (part of a tasty emacs ide experience)
tags: $(main_javas) $(test_javas)
@rm -f TAGS
- @$(CTAGS) -e --recurse=yes --extras=+q --fields=+fksaiS $(SRC) $(TST)
+ @$(CTAGS) -e --recurse=yes --extra=+q --fields=+fksaiS $(SRC) $(TST)
diff --git a/chapter24/README.md b/chapter24/README.md
index 22fda399..3774c80a 100644
--- a/chapter24/README.md
+++ b/chapter24/README.md
@@ -3,8 +3,12 @@
In this Chapter we allow *chaining* conditionals, e.g. `(60 <= score < 90)`
where `score` in the middle does not have to be repeated.
-We also present an Interprocedural Sparse Conditional Constant Propagation
-algorithm.
+The main compiler change is Interprocedural Sparse Conditional Constant
+Propagation (SCCP): an optimistic type solve which also discovers call-graph
+edges. This snapshot still uses the earlier parser/type and memory machinery;
+Chapter 25 currently contains the larger incomplete-types SSA redesign and
+separate compilation. Independent correctness fixes will move backward first;
+see the [backport review queue](../docs/chapter-backports.md).
You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter24) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter23...linear-chapter24) it to the previous chapter.
@@ -37,7 +41,7 @@ if (60 <= score < 90) {
This checks if score is between 60 (inclusive) and 90 (exclusive), without repeating `score`.
Expressions that mix opposite directions of comparison, like using both
-`<=` and `=>`, are not allowed, because they create ambiguous logic.
+`<=` and `>=`, are not allowed by Simple's direction rule.
```java
if (a <= b >= c)
@@ -52,7 +56,8 @@ Stacked comparisons are *only* valid if all the comparison operators
- `>` with `>`
- `>` with `>=`
-All operators above can be combined with `==` and `!=`.
+Equality operators (`==` and `!=`) have their normal, lower precedence; they
+are not links in a relational chain.
It is not allowed to mix directions in a single chain:
```java
@@ -87,7 +92,8 @@ a < b && b == c
```
Comparisons can be chained together in any length, and different comparison operators may be
mixed freely as long as they point in the same direction.
-Equality(`==`) and inequality(`!=`) operators may be combined with any other comparisons at any point in the chain.
+Each intermediate operand is evaluated once. Later operands are evaluated only
+if earlier comparisons succeed, so calls and updates in a chain short-circuit.
E.g:
```
@@ -167,7 +173,7 @@ Without SCCP, this small program cannot remove the computation of `x` and hence
the loop either. A glance at the program tells us `x` must always be a `1` but
the bottom-up (pessimistic) approach decides that since x1 is BOT, x2 must be
BOT so x1 must be BOT. We need the optimistic approach to break the
-statemate, and just *assume* x1 is `1`, and then we can discover than x2 is
+stalemate, and just *assume* x1 is `1`, and then we can discover that x2 is
also a `1`.
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index d67b2065..b022bec9 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -7,15 +7,30 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue;
/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
+ * Normal tests run chapter-local regression seeds. Open failures and exploratory
+ * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore).
+ * Seeds depend on this chapter's generator; do not copy them between chapters.
*/
public class FuzzerWrap {
+ private static final long[] REGRESSION_SEEDS = {
+ };
+
+ private static final long[] OPEN_FAILING_SEEDS = {
+ };
+
+ @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); }
+ @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
+
+ private static void fuzzPeepsSeeds(long... seeds) {
+ var fuzzer = new Fuzzer();
+ for (long seed : seeds)
+ fuzzer.fuzzPeepsRegression(seed);
+ }
+
@Ignore
@Test
- public void fuzzPeeps() {
+ public void fuzzPeepsLarge() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
fuzzer.fuzzPeeps(i);
@@ -23,8 +38,8 @@ public class FuzzerWrap {
}
- @Test
- public void fuzzPeepsSmall() {
+ @Test @Ignore
+ public void fuzzPeepsRandom() {
Random R = new Random(System.currentTimeMillis());
var fuzzer = new Fuzzer();
for (int i=0; i<100; i++)
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 77e61b42..962202a6 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -171,4 +171,18 @@ public class Fuzzer {
public boolean noExceptions() {
return exceptions.isEmpty();
}
+
+ /** Run a fixed chapter-local seed without reducing or swallowing failures. */
+ public void fuzzPeepsRegression(long seed) {
+ var rand = new Random(seed);
+ var sb = new StringBuilder();
+ var valid = new ScriptGenerator(rand, sb, true).genProgram();
+ try {
+ runCheck(sb.toString(), valid);
+ } catch (Throwable e) {
+ AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed);
+ ae.initCause(e);
+ throw ae;
+ }
+ }
}
diff --git a/chapter25/Makefile b/chapter25/Makefile
index f07f5d4b..9cacde76 100644
--- a/chapter25/Makefile
+++ b/chapter25/Makefile
@@ -236,6 +236,9 @@ lib/hamcrest-core-1.3.jar:
@(cd lib; wget https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar)
# Build emacs tags (part of a tasty emacs ide experience)
+.PHONY: tags
+tags: TAGS
+
TAGS: $(main_javas) $(test_javas)
@rm -f TAGS
@$(CTAGS) -e --recurse=yes --fields=+fksaiS $(SRC) $(TST)
diff --git a/chapter25/README.md b/chapter25/README.md
index 10cfe7e1..971c4746 100644
--- a/chapter25/README.md
+++ b/chapter25/README.md
@@ -1,86 +1,124 @@
-# Chapter 25: Modules
-
-
-You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter22...linear-chapter24) it to the previous chapter.
-
-
-## Modules
-
-Goal: A Module plan (not yet a "system")
-Goal: No syntax for the starter 1-file-1-line helloWorld.smp "module"
-Goal: External symbols are in ".o" files, like Java ".class" - not an source file #include like C/C++
-
-Needs a search strategy for ".o" files, so they mirror the source code directory structure.
-Means there is a "build" root for .o files.
-Means the CLI has a --flag for the build root, or some sensible default ($cwd/build)
-
-Projects with subdirectories pick up all symbols qualified names from the path-to-project-root
-
-Goal: under no circumstances can you load from an uninitialized variable (although you can see default inits for fields that have defaults).
-Goal: nested tree-structured name spaces, to control complexity.
-Goal: field decl order within a file is fairly flexible.
-
-
-## more
-
-Has a defined project root (default: CWD).
-
-Has a path to root: %PATH, e.g. $ROOT/a/b/c/
-
-Has a file name: $ROOT/ a/b/c/ foo.smp
-
-Everything in a dir is implicitly in name-space above it.
-
-Suppose `foo.smp` contains `PI=3.14;`
-
-Then the fully qualified name is: a.b.c.foo.PI
-
-Code is implicitly in the `struct a.b.c.foo {}` namespace.
-
-Final constant values can be accessed by direct names.
-
-Non-finals or non-constant need a constructed object.
-
-
-
-Top-level code in `foo.smp` is implicitly in the foo class init,
-which runs the first time foo is touched,
-which requires a "has been init" touch on every possible-first-access,
-which is every non-local ("sideways") reference.
-
-Always the init for parents are done before children,
-so `a` completes before `a.b`, and so on.
-
-Means `a` init can NOT reference e.g. `b,c,foo`, although
-you can define top-level fcns that refer to `b`.
-
-Legal in `a.smp`: `val make_b = { -> new b{} };` // fcn is final constant
-
-Legal in `a.smp`: `val b = new b{} ;` // not constant, so requires instance & <clinit> is done
-
-Means making an `a` instance in a's <clinit>, will also run a's <init>
-which can refer to instance fcns, which can refer to not-init finals?
-Limit: all fcns can only refer to instance vars *before* them.
-
-
-
-Would like to make golden instances, static singletons:
-val special = new a{/*anything already in-scope and <clinit>ed*/};
-
-
-
-The default 1-file main then:
-$ROOT==$CWD
-Code is implicitly in the *empty* name space, is in the <clinit> for the empty
-name space and gets run on project start.
-
-------
-
-Implications for e.g. sys:
-
- .../simple/sys.smp - top level, has sys.parseAryI64
- ../simple/sys/sys.io.smp - has `struct io { ... }`
-
-
-For now just gather the exported TYPE symbols, and a count of them, and export/input ELF.
-NO: for now split into `sys` class from instance, \ No newline at end of file
+# Chapter 25: Modules, Separate Compilation, and SSA Construction
+
+This chapter compiles Simple source files into reusable object files, loads
+their types and ideal IR into later compilations, and links native programs
+against a separately built system library. It also contains a substantial
+parser, type, and memory-SSA redesign to support incomplete information.
+
+These are the contents of the current snapshot. Independent correctness fixes
+will move to earlier chapters first; moving the larger SSA redesign backward,
+or splitting this chapter, is deferred. See the [backport queue](../docs/chapter-backports.md).
+
+You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25)
+in the [linear history](https://github.com/SeaOfNodes/Simple/tree/linear) and
+[compare it with Chapter 24](https://github.com/SeaOfNodes/Simple/compare/linear-chapter24...linear-chapter25).
+
+## Compilation units and names
+
+`CompUnit` represents a source or object file. `ParseAll` discovers dependencies,
+parses needed sources, and loads previously compiled units. Source and build
+trees use corresponding relative paths; class names use dotted paths. For
+example, `a/b/foo.smp` supplies the namespace `a.b.foo`.
+
+Each unit has Start/Stop nodes. A function's ownership by a compilation unit is
+distinct from an edge representing unknown external callers: retaining code
+for emission must not imply that arbitrary callers can reach it.
+
+The driver accepts `--root` for the project root, `-o` for output, and `-L` for
+external Simple object search paths, including an object file or a directory.
+The compiler also resolves external C symbols for native linking. Names beginning
+with `_` control privacy; public reachability depends on enclosing names too.
+
+## Object files contain ideal IR
+
+The ELF writer stores native code and a `.simple` section containing canonical
+types, ideal graph nodes, and dependencies. The reader reconstructs that graph
+so imported functions can participate in optimization, including inlining.
+Calls that remain out of line are resolved by native linking.
+
+`GlobalBits` maps file identities and per-file indices to local dense indices
+for aliases, function targets, and return points. Equal local numbers from
+independent compilations must not be mistaken for equal identities. Types are
+closed and upgraded after loading, including cross-unit cyclic references.
+
+Serialization preserves declaration types separately from sharpened flow types.
+Every semantic node/type field needs matching read/write support. The source's
+expensive serialization bijection check is currently disabled, and the larger
+incremental-rebuild test `testModule0` is ignored. Neither is an active
+validation guarantee.
+
+## Constructing SSA before types are complete
+
+Simple still parses directly to SSA without an AST. Forward references mean an
+expression's eventual type may be unavailable while parsing it. Syntax and
+resolved lexical binding therefore choose topology; types subsequently refine,
+optimize, and validate the graph.
+
+- Arithmetic nodes defer integer versus floating-point mode selection, including
+ resolution through recursive graph components after SCCP.
+- Calls preserve their syntactic receiver slot before the callee kind is settled.
+ Symbolic field information defers layout-dependent decisions.
+- Loads and ordinary Phis no longer carry parser-supplied lower-bound types.
+- `TypeScalar` represents integer, float, memory-pointer, and function-pointer
+ values separately from global control/memory bottom.
+- `TypeStruct._open` describes an incomplete field set; `_fref` records the
+ absence of an authoritative definition. Discovering fields does not define a type.
+- Guards carry branch-proven zero/nonzero facts before the input family is known.
+ `FunPtrNode` retains an explicit edge to its function's return graph.
+
+The driver runs parsing, pessimistic iteration, SCCP, and final type checking
+before serialization and machine selection. Nontrivial inlining is coordinated
+with iteration instead of always occurring as an immediate peephole.
+
+## Memory and construction
+
+The parser carries ordinary bulk memory. The optimizer recovers precise aliases
+using `MemMerge`, `MemPhi`, and `BulkMemPhi`. A bulk Phi covers all aliases
+except its exclusions, represented by parallel precise Phis. Every slice must
+be covered exactly once.
+
+User constructors are declared and called as follows:
+
+```java
+struct Box {
+ i64[] values;
+ new Box = { i64[] initial -> values = initial; };
+};
+return new Box(new i64[1]);
+```
+
+An allocation helper is generated at the declaration. It allocates, calls the
+hidden `<init>` and then the user constructor, and publishes initialized private
+memory through `EscapeNode`s. Parser `Var` metadata detects early field reads
+and missing initialization on constructor exits. Store widths come from target
+declarations, not from the values being stored.
+
+File-level code is represented by a class initializer (`<clinit>`), distinct
+from instance initialization. The older [module design notes](module.md) and
+[roadmap](ROADMAP.md) contain proposals and alternatives, especially concerning
+initialization order; they are not a specification of all implemented rules.
+
+## System library and examples
+
+The former single `sys.smp` library is organized into units for libc bindings,
+I/O, characters, scanners, arrays, growable buffers, and bitsets. The native
+driver supports real program arguments. Examples in `docs/examples` include
+Bubble Sort, Capitalize, Dijkstra, and FileIO.
+
+`Chapter25Test` exercises library loading and native linking, including a Hello
+World call deliberately kept out of line. That test checks the client's external
+symbol before linking against `sys.o`. Other tests cover forward constructors,
+field updates through calls, literal escapes, and reduced compiler failures.
+Independent regressions are candidates for earlier chapters.
+
+## Building and checking
+
+From this directory, `make lib` supplies Java test dependencies and `make tests`
+runs the raw chapter suites, standalone suites, system tests, and fuzzer
+regressions. `make release` builds the compiler jar and native library artifacts;
+`make tags` builds editor tags. Native tools and the selected CPU/ABI must match
+the environment; the Makefile currently defaults to x86-64/win64.
+
+Compiler changes can invalidate both serialized IR and native code in `sys.o`.
+Rebuild it before interpreting linked-program test results. A source-only subset
+is not equivalent to this chapter's full `make tests`.
diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md
new file mode 100644
index 00000000..4e7e2a69
--- /dev/null
+++ b/docs/chapter-backports.md
@@ -0,0 +1,176 @@
+# Chapter backport review
+
+This is a work queue, not a record of completed compiler backports. Independent
+corrections should start in the earliest affected chapter and propagate through
+every later affected snapshot. A regression in Chapter 25's `Chapter21Test`
+does not test the compiler in the `chapter21` directory.
+
+For now, keep building SSA with incomplete types in Chapter 25. Moving that
+architecture earlier, or splitting Chapter 25, is deferred while small changes
+establish the review workflow. No renumbering is committed.
+
+## First two review candidates
+
+| ID | Small change | Destination | Evidence and regression |
+|---|---|---|---|
+| B01 | Encode a 64-bit memory comparison with a 32-bit immediate | 21, then 22-24; 25 already fixed | `CmpMemX86.encoding`: `case 3: enc.add8(_imm)` becomes `add4`. Keep each chapter's type accessor. Check instruction length/bytes and a comparison followed by another instruction. |
+| B02 | Correct immediate-multiply REX register order | 22, then 23-24; audit 21 separately | `MulIX86.encoding`: `rex(src,dst,0)` becomes `rex(dst,src,0)`. Exercise source and destination on opposite sides of register 8. Chapter 21 uses `ImmX86`, not this one-line implementation. |
+
+The initial proposal was B01 followed by B02. The Chapter 18 investigation below
+now supplies another small first-review candidate, B11. Review one correction's
+reduced failure, smallest patch, and test results before starting another.
+No compiler fixes have been applied with this build and documentation change.
+
+## Subsequent small changes
+
+An **audit** item needs an old-chapter reproducer before it is scheduled. Copy
+the correction into the chapter's representation, not the entire modern file.
+
+| ID | Change | Proposed destination | Scope and acceptance evidence |
+|---|---|---|---|
+| B03 | Correct x86 logical-not encoding | 21 through 24 | `NotX86`: preserve a shared source/destination until after testing; correct SETcc operand fields and zero-extend its byte result. Check zero/nonzero, overlapping registers, and byte registers requiring REX. Exclude Not/Phi optimizations. |
+| B04 | Preserve comparison results while releasing parser temporaries | 24 | `parseComparison` lifetime fix in `414931c8`. Reduce seed `-8212834489697770130`; exclude unrelated `<clinit>` cleanup in that commit. |
+| B05 | Preserve RHS control, memory, and local updates in logical OR | 23 through 24; **audit** earliest implementation | Chapter 25 `parseIf` retains `_scope` for the false-side `||` expression. Test scalar and field updates and short-circuit behavior. Avoid importing GuardNode. |
+| B06 | Decode string escapes; reject unknown/truncated escapes | 22 through 24 | Literal scanner only. Check newline, tab, carriage return, backslash, quote, NUL, and malformed input. |
+| B07 | Decode character escapes; reject truncated literals | 22 through 24 | Separate patch sharing B06's helper. Check values and errors; `parseChar` first appears in 22. |
+| B08 | Complete `&=` and `|=` syntax sugar | 17 onward; **audit** availability of bitwise operators | A small language addition, not a correctness-only patch. Move lexer and assignment expansion together. Check locals, fields, arrays, and exactly-once address evaluation. |
+| B09 | Make diagnostic function lookup read-only | 18 onward; **audit** first mutating printer lookup | Separate `lookupFun` from cleanup-performing `link`. Verify printing preserves graph/linker state. Do not import FunPtrNode or compilation units for this fix. |
+| B10 | Preserve widening state in integer nonzero refinement | **Audit** first chapter with both widening and `nonZero`; no later than 24 | Chapter 25 `TypeInteger.nonZero` preserves `_widen`. Check lattice laws and loop refinement. Exclude TypeScalar, storage-type, and serialization changes. |
+| B11 | Diagnose return types using the optimized return expression | 18; 19 already has the correction | `ReturnNode.err()` uses `expr()._type` instead of the parse-time `mt` aggregate. Chapter 18 rejects `struct S { u8 x; }; return new S; return 0;` with a mixed integer/reference error; Chapter 19 accepts it. Also test genuinely reachable incompatible returns. This is a candidate, not yet an applied or isolated-patch-verified fix. |
+
+After the first two reviews, batch only corrections with established independence
+and regressions. Keep one logical correction per commit across affected chapters.
+
+## Larger or entangled changes: defer
+
+| Group | Eventual home | Why not in the small queue yet |
+|---|---|---|
+| Dominator caches, scheduling, global constant cloning | 11 / 18 / 19 as applicable | Current changes mix inlining, scheduling, and compilation-unit ownership. Need a reduced old-IR failure. |
+| Register allocation and spilling | 20 onward | Need constrained-register/spill regressions; changed golden spill counts are insufficient evidence. |
+| Conditional Store and array Load control | Memory/arrays chapters | Reproduce under the earlier alias model before extracting fixes from the new memory implementation. |
+| SCCP dependencies, function revival, reachability | 24; some foundations may fit 18 | Separate old-IR corrections from new Guard/Escape/BulkMemPhi and external-caller machinery. |
+| Lattice fixes in `7f3b8856` | Owning type introduction | The commit mixes TypeFunPtr, TypeMem, and new XInt/escape-summary behavior. Not a generic earlier-lattice patch. |
+| TypeScalar, numeric modes, guards, symbolic fields, open/forward types | Revisit earlier homes later | A connected incomplete-types architecture, including phase ordering and errors. |
+| BulkMemPhi/MemPhi, private constructor memory, allocation helpers | Revisit memory / constructors / methods | Move invariants and regressions together. |
+| Serialization, global identity remapping, module escape summaries | Separate compilation | Remain with modules. |
+
+## Review and test protocol
+
+1. Run the destination's unmodified `make tests`. Record baseline failures and
+ resolve them before accepting a compiler backport into that chapter.
+2. Add a deterministic regression proving the old failure. Prefer tiny source
+ programs; use encoding checks when register placement is the property at issue.
+3. Apply the smallest correction; run the focused regression and full chapter
+ `make tests`. Stop for Cliff's review before the next trial fix.
+4. Propagate the accepted fix and regression through every affected later
+ snapshot. Run each snapshot's full `make tests`, not just 25's inherited tests.
+5. Run TypeTest for lattice changes and rebuild system objects when compiler
+ changes invalidate their IR/native code. Force a full Java rebuild when shared
+ APIs or constants change: older Makefiles do not track all Java dependencies.
+ Do not weaken checks or drop tests.
+6. Record chapters, commands, and results here. Keep unrelated dirty changes;
+ do not push without explicit approval.
+
+The top-level runner supports all 25 chapters:
+
+```sh
+make lib # dependencies, before tests on a fresh checkout
+make tests
+make -k tests # visit all chapters; failures still fail the command
+make tests CHAPTERS="chapter21 chapter22"
+make tags release CHAPTERS="chapter01 chapter24 chapter25"
+```
+
+Runs are sequential, and child failures propagate. `make tag` aliases `tags`.
+Java, GNU make, bash, ctags (for tags), and the existing native test tools must
+be on PATH. Early release targets package classes into jars; they do not imply
+standalone executable compiler drivers. Older Makefiles discover jars when make
+starts, so run `make lib` separately before `make tests`.
+
+## Validation record
+
+All compiler backports B01-B11 are pending.
+
+On 2026-09-17, using Windows/Cygwin and OpenJDK 22.0.2:
+
+| Check | Result |
+|---|---|
+| All 25 chapter `tags` targets | Passed via `make -k tags`. |
+| All 25 chapter `release` targets | Passed, in two batches (1-22 and 23-25). |
+| `make -k tests`: Chapters 1-17, 19-21, 23-25 | Passed each complete chapter target. |
+| Chapter 18 | 300 ordinary tests passed; `fuzzPeepsSmall` failed on seed `973358943756616234`. This test chooses seeds from wall-clock time. Preserve the failure rather than rerunning until green. |
+| Chapter 22, initial run | `testLatticeTheory` failed with a TypeConAry/TypeRPC cast error. |
+| `make -B tests CHAPTERS=chapter22` | Passed after full rebuild: 369 ordinary tests and the fuzzer test. This is consistent with stale classes; the original failure remains recorded. |
+
+That initial test baseline was **not green** because of Chapter 18. The follow-up
+below records the explicitly requested change to default fuzzing coverage; the
+underlying compiler failure remains open.
+
+Chapter 25 initially could not run its system suite because the Hello World
+example was deleted in the working tree. Cliff restored it; the final full run
+passed (362 raw0, 32 raw1, 7 standalone, 16 system, and 1 fuzzer test).
+
+Build-only fixes made during setup: supply missing early Makefiles and release
+targets, alias Chapter 25's `TAGS` as `tags`, use the existing older-chapter ctags
+option spelling in 21-24, and correct malformed `$(file ...)` argument-list
+writes in 22-24. These changes do not move compiler behavior between chapters.
+
+Local logs (ignored build artifacts): `build/review/tests-verified.log`,
+`build/review/chapter22-rebuilt-tests.log`, `build/review/tags-verified.log`,
+`build/review/release-01-22.log`, and `build/review/release-23-25.log`.
+
+The Chapter 18 fuzzer printed this reduced input:
+
+```java
+struct s0 {
+ u8 v1;
+};
+s0? !UmPOLQK=null;
+if(0)
+ while(0&UmPOLQK.v1) {}
+if(UmPOLQK.v1) {}
+return new s0;
+while(0) {}
+```
+
+### Deterministic fuzzer follow-up
+
+Chapters 8-24 now follow Chapter 25's seed-list structure. Each wrapper has its
+own `REGRESSION_SEEDS` and ignored `OPEN_FAILING_SEEDS`. Exploratory fuzzing is
+opt-in, including the previously automatic random runs. Chapters 8-15 now invoke
+their wrappers from `make tests` as well. Fixed-seed failures throw immediately
+with the seed and original cause, without entering the exploratory reducer.
+Each chapter retains its generator and checking strategy; seeds are not copied
+between chapters.
+
+The regression lists start empty. Chapter 18's open list contains only
+`973358943756616234`; explicitly invoking that list still reproduces the failure.
+Passing default tests therefore does not mean that this compiler bug is fixed
+or that new fuzz regression coverage has been established. After a correction,
+move its seed to that chapter's regression list once the full seed passes.
+
+Validation: `make -k tests` passed across all 25 chapters after the harness
+changes. After adding wrapper invocation to Chapters 8-15, their complete
+`make tests` targets passed again. Logs: `build/review/seed-lists-all-tests.log`,
+`build/review/seed-lists-08-15-tests.log`, and
+`build/review/chapter18-open-seed.log` (intentional failure).
+
+The reduced Chapter 18 source exposes two separate concerns:
+
+- With peepholes disabled, nullable field lookup can fail during parsing;
+ with peepholes enabled, the reduced original reaches a mixed-return error.
+ Chapter 19 changes the fuzzer comparison to two optimizer worklist seeds,
+ rather than peepholes disabled/enabled. That harness change deserves separate
+ review; it is not proof that the compiler discrepancy was corrected.
+- The mixed-return diagnostic reduces further to
+ `struct S { u8 x; }; return new S; return 0;`. Chapter 18 rejects it with
+ peepholes both disabled and enabled. Chapter 19 accepts it under both fuzzer
+ worklist orders. A plain `return new S;` is accepted in both chapters. The
+ corresponding Chapter 19 `ReturnNode.err()` change is B11 above. Dead
+ `if(0) return 0;` and a trailing `while(0) {}` also trigger the Chapter 18
+ error. No compiler code has been changed yet.
+
+Additional source probes across Chapters 19-25 show differing treatment of
+unused nullable loads. A live `return p.x;` with null `p` was rejected in every
+tested snapshot. Keep the original seed open: B11 alone has not been shown to
+resolve its separate parser/checking discrepancy.