diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-25 09:51:44 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-25 09:51:44 -0700 |
| commit | 4051ce7cd26314f01dc8c0237899a742e0ab2084 (patch) | |
| tree | 29cb9b47e0461c34be1797ac36dd31e8f0b10c09 | |
| parent | 5cfb046c8306a81ec40be82a59015f16f654fa93 (diff) | |
| download | Simple-4051ce7cd26314f01dc8c0237899a742e0ab2084.tar.gz Simple-4051ce7cd26314f01dc8c0237899a742e0ab2084.zip | |
Fix ++x in methods
Fix ideaj setups
93 files changed, 2258 insertions, 242 deletions
@@ -11,7 +11,9 @@ build/ *.log # Idea -.idea/ +.idea/* +!.idea/modules.xml +!.idea/compiler.xml target/ *.iml .vscode/ @@ -25,17 +27,10 @@ TAGS # Libs */lib/*jar -# Portable IDEA descriptors for the memory, scheduling and numeric chapters -!chapter10/chapter10.iml -!chapter11/chapter11.iml -!chapter12/chapter12.iml -!chapter13/chapter13.iml -!chapter14/chapter14.iml +# Portable IDEA descriptors; personal IDE settings stay ignored. +!chapter*/*.iml !graph/graph.iml !print/print.iml !isa/isa.iml !isa/src/test-support/isa-eval.iml - -# Portable descriptors for the mutability / syntax-sugar split -!chapter17a/chapter17a.iml -!chapter17b/chapter17b.iml +!transpile-tests/transpile-tests.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..c85eefbb --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <bytecodeTargetLevel target="21" /> + </component> +</project> diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..43c22d84 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/chapter01/chapter01.iml" filepath="$PROJECT_DIR$/chapter01/chapter01.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter02/chapter02.iml" filepath="$PROJECT_DIR$/chapter02/chapter02.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter03/chapter03.iml" filepath="$PROJECT_DIR$/chapter03/chapter03.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter04/chapter04.iml" filepath="$PROJECT_DIR$/chapter04/chapter04.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter05/chapter05.iml" filepath="$PROJECT_DIR$/chapter05/chapter05.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter06/chapter06.iml" filepath="$PROJECT_DIR$/chapter06/chapter06.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter07/chapter07.iml" filepath="$PROJECT_DIR$/chapter07/chapter07.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter08/chapter08.iml" filepath="$PROJECT_DIR$/chapter08/chapter08.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter09/chapter09.iml" filepath="$PROJECT_DIR$/chapter09/chapter09.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter10/chapter10.iml" filepath="$PROJECT_DIR$/chapter10/chapter10.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter11/chapter11.iml" filepath="$PROJECT_DIR$/chapter11/chapter11.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter12/chapter12.iml" filepath="$PROJECT_DIR$/chapter12/chapter12.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter13/chapter13.iml" filepath="$PROJECT_DIR$/chapter13/chapter13.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter14/chapter14.iml" filepath="$PROJECT_DIR$/chapter14/chapter14.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter15/chapter15.iml" filepath="$PROJECT_DIR$/chapter15/chapter15.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter16/chapter16.iml" filepath="$PROJECT_DIR$/chapter16/chapter16.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter17a/chapter17a.iml" filepath="$PROJECT_DIR$/chapter17a/chapter17a.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter17b/chapter17b.iml" filepath="$PROJECT_DIR$/chapter17b/chapter17b.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter18/chapter18.iml" filepath="$PROJECT_DIR$/chapter18/chapter18.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter19/chapter19.iml" filepath="$PROJECT_DIR$/chapter19/chapter19.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter20/chapter20.iml" filepath="$PROJECT_DIR$/chapter20/chapter20.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter21/chapter21.iml" filepath="$PROJECT_DIR$/chapter21/chapter21.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter22/chapter22.iml" filepath="$PROJECT_DIR$/chapter22/chapter22.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter23/chapter23.iml" filepath="$PROJECT_DIR$/chapter23/chapter23.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter24a/chapter24a.iml" filepath="$PROJECT_DIR$/chapter24a/chapter24a.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter24b/chapter24b.iml" filepath="$PROJECT_DIR$/chapter24b/chapter24b.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter25/chapter25.iml" filepath="$PROJECT_DIR$/chapter25/chapter25.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter26/chapter26.iml" filepath="$PROJECT_DIR$/chapter26/chapter26.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter27/chapter27.iml" filepath="$PROJECT_DIR$/chapter27/chapter27.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter28a/chapter28a.iml" filepath="$PROJECT_DIR$/chapter28a/chapter28a.iml" /> + <module fileurl="file://$PROJECT_DIR$/chapter28b/chapter28b.iml" filepath="$PROJECT_DIR$/chapter28b/chapter28b.iml" /> + <module fileurl="file://$PROJECT_DIR$/print/print.iml" filepath="$PROJECT_DIR$/print/print.iml" /> + <module fileurl="file://$PROJECT_DIR$/graph/graph.iml" filepath="$PROJECT_DIR$/graph/graph.iml" /> + <module fileurl="file://$PROJECT_DIR$/isa/isa.iml" filepath="$PROJECT_DIR$/isa/isa.iml" /> + <module fileurl="file://$PROJECT_DIR$/isa/src/test-support/isa-eval.iml" filepath="$PROJECT_DIR$/isa/src/test-support/isa-eval.iml" /> + <module fileurl="file://$PROJECT_DIR$/transpile-tests/transpile-tests.iml" filepath="$PROJECT_DIR$/transpile-tests/transpile-tests.iml" /> + </modules> + </component> +</project> diff --git a/.run/chapter01 tests.run.xml b/.run/chapter01 tests.run.xml new file mode 100644 index 00000000..b0cbc021 --- /dev/null +++ b/.run/chapter01 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter01 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter01" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter01" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter02 tests.run.xml b/.run/chapter02 tests.run.xml new file mode 100644 index 00000000..e7a863cf --- /dev/null +++ b/.run/chapter02 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter02 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter02" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter02" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter03 tests.run.xml b/.run/chapter03 tests.run.xml new file mode 100644 index 00000000..64aa3780 --- /dev/null +++ b/.run/chapter03 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter03 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter03" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter03" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter04 tests.run.xml b/.run/chapter04 tests.run.xml new file mode 100644 index 00000000..5412ec33 --- /dev/null +++ b/.run/chapter04 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter04 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter04" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter04" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter05 tests.run.xml b/.run/chapter05 tests.run.xml new file mode 100644 index 00000000..2a3314f9 --- /dev/null +++ b/.run/chapter05 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter05 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter05" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter05" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter06 tests.run.xml b/.run/chapter06 tests.run.xml new file mode 100644 index 00000000..26abc0ff --- /dev/null +++ b/.run/chapter06 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter06 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter06" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter06" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter07 tests.run.xml b/.run/chapter07 tests.run.xml new file mode 100644 index 00000000..b94ddfb0 --- /dev/null +++ b/.run/chapter07 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter07 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter07" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter07" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter08 tests.run.xml b/.run/chapter08 tests.run.xml new file mode 100644 index 00000000..6ad6fd95 --- /dev/null +++ b/.run/chapter08 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter08 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter08" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter08" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter09 tests.run.xml b/.run/chapter09 tests.run.xml new file mode 100644 index 00000000..027409b1 --- /dev/null +++ b/.run/chapter09 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter09 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter09" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter09" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter10 tests.run.xml b/.run/chapter10 tests.run.xml new file mode 100644 index 00000000..af2016fe --- /dev/null +++ b/.run/chapter10 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter10 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter10" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter10" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter11 tests.run.xml b/.run/chapter11 tests.run.xml new file mode 100644 index 00000000..356dd845 --- /dev/null +++ b/.run/chapter11 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter11 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter11" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter11" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter12 tests.run.xml b/.run/chapter12 tests.run.xml new file mode 100644 index 00000000..c74c497e --- /dev/null +++ b/.run/chapter12 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter12 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter12" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter12" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter13 tests.run.xml b/.run/chapter13 tests.run.xml new file mode 100644 index 00000000..accaa816 --- /dev/null +++ b/.run/chapter13 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter13 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter13" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter13" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter14 tests.run.xml b/.run/chapter14 tests.run.xml new file mode 100644 index 00000000..3f36798d --- /dev/null +++ b/.run/chapter14 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter14 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter14" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter14" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter15 tests.run.xml b/.run/chapter15 tests.run.xml new file mode 100644 index 00000000..e5dd75b2 --- /dev/null +++ b/.run/chapter15 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter15 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter15" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter15" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter16 tests.run.xml b/.run/chapter16 tests.run.xml new file mode 100644 index 00000000..96cfb235 --- /dev/null +++ b/.run/chapter16 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter16 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter16" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter16" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter17a tests.run.xml b/.run/chapter17a tests.run.xml new file mode 100644 index 00000000..7e8270f3 --- /dev/null +++ b/.run/chapter17a tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter17a tests" type="JUnit" factoryName="JUnit"> + <module name="chapter17a" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter17a" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter17b tests.run.xml b/.run/chapter17b tests.run.xml new file mode 100644 index 00000000..80ed97e7 --- /dev/null +++ b/.run/chapter17b tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter17b tests" type="JUnit" factoryName="JUnit"> + <module name="chapter17b" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter17b" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter18 tests.run.xml b/.run/chapter18 tests.run.xml new file mode 100644 index 00000000..8377fe06 --- /dev/null +++ b/.run/chapter18 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter18 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter18" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter18" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter19 tests.run.xml b/.run/chapter19 tests.run.xml new file mode 100644 index 00000000..815edb14 --- /dev/null +++ b/.run/chapter19 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter19 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter19" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter19" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter20 tests.run.xml b/.run/chapter20 tests.run.xml new file mode 100644 index 00000000..5784f410 --- /dev/null +++ b/.run/chapter20 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter20 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter20" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter20" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter21 tests.run.xml b/.run/chapter21 tests.run.xml new file mode 100644 index 00000000..ee50ade2 --- /dev/null +++ b/.run/chapter21 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter21 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter21" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter21" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter22 tests.run.xml b/.run/chapter22 tests.run.xml new file mode 100644 index 00000000..5a0cb467 --- /dev/null +++ b/.run/chapter22 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter22 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter22" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter22" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter23 field write.run.xml b/.run/chapter23 field write.run.xml new file mode 100644 index 00000000..18f12fcf --- /dev/null +++ b/.run/chapter23 field write.run.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter23 field write" type="JUnit" factoryName="JUnit"> + <module name="chapter23" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="method" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter23" /> + <option name="MAIN_CLASS_NAME" value="com.seaofnodes.simple.PrefixUpdateTest" /> + <option name="METHOD_NAME" value="testMethodFieldWrites" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter23 tests.run.xml b/.run/chapter23 tests.run.xml new file mode 100644 index 00000000..ed475036 --- /dev/null +++ b/.run/chapter23 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter23 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter23" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter23" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter24a tests.run.xml b/.run/chapter24a tests.run.xml new file mode 100644 index 00000000..8829b4d1 --- /dev/null +++ b/.run/chapter24a tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter24a tests" type="JUnit" factoryName="JUnit"> + <module name="chapter24a" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter24a" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter24b tests.run.xml b/.run/chapter24b tests.run.xml new file mode 100644 index 00000000..605be42a --- /dev/null +++ b/.run/chapter24b tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter24b tests" type="JUnit" factoryName="JUnit"> + <module name="chapter24b" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter24b" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter25 tests.run.xml b/.run/chapter25 tests.run.xml new file mode 100644 index 00000000..dfd06ac5 --- /dev/null +++ b/.run/chapter25 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter25 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter25" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter25" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter26 tests.run.xml b/.run/chapter26 tests.run.xml new file mode 100644 index 00000000..913c9434 --- /dev/null +++ b/.run/chapter26 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter26 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter26" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter26" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter27 tests.run.xml b/.run/chapter27 tests.run.xml new file mode 100644 index 00000000..e6249dda --- /dev/null +++ b/.run/chapter27 tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter27 tests" type="JUnit" factoryName="JUnit"> + <module name="chapter27" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter27" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter28a tests.run.xml b/.run/chapter28a tests.run.xml new file mode 100644 index 00000000..a98470bb --- /dev/null +++ b/.run/chapter28a tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter28a tests" type="JUnit" factoryName="JUnit"> + <module name="chapter28a" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter28a" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> diff --git a/.run/chapter28b tests.run.xml b/.run/chapter28b tests.run.xml new file mode 100644 index 00000000..d290923d --- /dev/null +++ b/.run/chapter28b tests.run.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="chapter28b tests" type="JUnit" factoryName="JUnit"> + <module name="chapter28b" /> + <option name="PACKAGE_NAME" value="com.seaofnodes.simple" /> + <option name="TEST_OBJECT" value="package" /> + <option name="VM_PARAMETERS" value="-ea" /> + <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/chapter28b" /> + <TEST_SEARCH_SCOPE> + <value defaultName="singleModule" /> + </TEST_SEARCH_SCOPE> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> +</component> @@ -117,6 +117,7 @@ To build and test just the memory chapters: make lib tests release CHAPTERS="chapter10 chapter11 chapter12" ``` -The root Maven reactor includes every chapter. Chapters 10-14 have portable -IDEA module descriptors depending on the shared `graph` and `print` modules. +Every chapter has an IntelliJ module with its own compiler and test classpath. +See [the IntelliJ setup](docs/intellij.md) to open and debug any chapter's tests. +The root Maven reactor also includes every chapter. Make and the linear-history workflow discover the numbered chapter directories. diff --git a/build-support/idea.py b/build-support/idea.py new file mode 100644 index 00000000..84f75c22 --- /dev/null +++ b/build-support/idea.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Refresh the native IntelliJ project after adding or moving chapters. + +Close the project first: IDEA can otherwise save its old module list over ours. +No Maven import is needed. Personal settings stay in the ignored workspace.xml. +""" + +from pathlib import Path +import re +import shutil +import xml.etree.ElementTree as ET + +ROOT = Path(__file__).resolve().parent.parent + + +def save(path, text): + path = ROOT / path + data = text.encode("utf-8") + if path.exists() and path.read_bytes() == data: + return + if path.exists(): + backup = ROOT / "build/idea-backup" / path.relative_to(ROOT) + backup.parent.mkdir(parents=True, exist_ok=True) + # Keep the original settings across repeated refreshes. + if not backup.exists(): + shutil.copyfile(path, backup) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(data) + + +def xml(path, root): + ET.indent(root, space=" ") + save(path, '<?xml version="1.0" encoding="UTF-8"?>\n' + + ET.tostring(root, encoding="unicode") + "\n") + + +def module(name, directory, dependencies=(), junit=None, main="src/main/java"): + root = ET.Element("module", type="JAVA_MODULE", version="4") + manager = ET.SubElement(root, "component", name="NewModuleRootManager", + **{"inherit-compiler-output": "false"}) + relative = "../" * len(Path(directory).parts) + output = "file://$MODULE_DIR$/" + relative + "build/idea/" + ET.SubElement(manager, "output", url=output + "production/" + name) + ET.SubElement(manager, "output-test", url=output + "test/" + name) + ET.SubElement(manager, "exclude-output") + content = ET.SubElement(manager, "content", url="file://$MODULE_DIR$") + for source, test in [(main, "false"), ("src/test/java", "true")]: + if (ROOT / directory / source).is_dir(): + ET.SubElement(content, "sourceFolder", url="file://$MODULE_DIR$/" + source, + isTestSource=test) + for excluded in ["build", "target", "lib"] + (["src/test-support"] if name == "isa" else []): + ET.SubElement(content, "excludeFolder", url="file://$MODULE_DIR$/" + excluded) + ET.SubElement(manager, "orderEntry", type="inheritedJdk") + ET.SubElement(manager, "orderEntry", type="sourceFolder", forTests="false") + for dependency, scope in dependencies: + attributes = {"type": "module", "module-name": dependency} + if scope: + attributes["scope"] = scope + ET.SubElement(manager, "orderEntry", **attributes) + if junit: + entry = ET.SubElement(manager, "orderEntry", type="module-library", scope="TEST") + library = ET.SubElement(entry, "library", name="JUnit4") + classes = ET.SubElement(library, "CLASSES") + for jar in ["junit-4.12.jar", "hamcrest-core-1.3.jar"]: + ET.SubElement(classes, "root", url="jar://$MODULE_DIR$/" + junit + "/" + jar + "!/") + ET.SubElement(library, "JAVADOC") + ET.SubElement(library, "SOURCES") + path = directory + "/" + name + ".iml" + xml(path, root) + return path + + +def run(name, chapter, test_class=None, method=None): + root = ET.Element("component", name="ProjectRunConfigurationManager") + config = ET.SubElement(root, "configuration", default="false", name=name, + type="JUnit", factoryName="JUnit") + ET.SubElement(config, "module", name=chapter) + options = {"PACKAGE_NAME": "com.seaofnodes.simple", + "TEST_OBJECT": "method" if method else "package", + "VM_PARAMETERS": "-ea", "WORKING_DIRECTORY": "$PROJECT_DIR$/" + chapter} + if test_class: + options["MAIN_CLASS_NAME"] = "com.seaofnodes.simple." + test_class + if method: + options["METHOD_NAME"] = method + for key, value in options.items(): + ET.SubElement(config, "option", name=key, value=value) + scope = ET.SubElement(config, "TEST_SEARCH_SCOPE") + ET.SubElement(scope, "value", defaultName="singleModule") + before = ET.SubElement(config, "method", v="2") + ET.SubElement(before, "option", name="Make", enabled="true") + xml(".run/" + name + ".run.xml", root) + + +def main(): + chapters = sorted(path.name for path in ROOT.iterdir() + if re.fullmatch(r"chapter\d\d[a-z]?", path.name) + and (path / "pom.xml").is_file()) + paths = [] + for chapter in chapters: + dependencies = [("graph", None), ("print", None)] + if "isa/isa.mk" in (ROOT / chapter / "Makefile").read_text(encoding="utf-8"): + dependencies += [("isa", None), ("isa-eval", "TEST")] + paths.append(module(chapter, chapter, dependencies, "lib")) + run(chapter + " tests", chapter) + paths.append(module("print", "print")) + paths.append(module("graph", "graph", [("print", None)])) + paths.append(module("isa", "isa", [("isa-eval", "TEST")])) + paths.append(module("isa-eval", "isa/src/test-support", [("isa", None)], main="java")) + paths.append(module("transpile-tests", "transpile-tests", junit="../chapter01/lib")) + project = ET.Element("project", version="4") + manager = ET.SubElement(project, "component", name="ProjectModuleManager") + modules = ET.SubElement(manager, "modules") + for path in paths: + ET.SubElement(modules, "module", fileurl="file://$PROJECT_DIR$/" + path, + filepath="$PROJECT_DIR$/" + path) + xml(".idea/modules.xml", project) + run("chapter23 field write", "chapter23", "PrefixUpdateTest", "testMethodFieldWrites") + + # SDK names are local to the IDE installation. Preserve an existing choice; + # a new checkout asks the user to select an installed JDK 21 in Project Structure. + if not (ROOT / ".idea/misc.xml").exists(): + save(".idea/misc.xml", '<project version="4">\n' + ' <component name="ProjectRootManager" version="2" languageLevel="JDK_21">\n' + ' <output url="file://$PROJECT_DIR$/build/idea" />\n' + ' </component>\n</project>\n') + + # New gutter runs must use the selected module's directory, not the repo root. + # Do not rewrite existing personal configurations, breakpoints or window state. + workspace = ROOT / ".idea/workspace.xml" + project = ET.parse(workspace).getroot() if workspace.exists() else ET.Element("project", version="4") + manager = project.find("component[@name='RunManager']") + if manager is None: + manager = ET.SubElement(project, "component", name="RunManager") + template = manager.find("configuration[@default='true'][@type='JUnit']") + if template is None: + template = ET.SubElement(manager, "configuration", default="true", type="JUnit", factoryName="JUnit") + option = template.find("option[@name='WORKING_DIRECTORY']") + if option is None: + option = ET.SubElement(template, "option", name="WORKING_DIRECTORY") + option.set("value", "$MODULE_WORKING_DIR$") + if template.find("option[@name='VM_PARAMETERS']") is None: + ET.SubElement(template, "option", name="VM_PARAMETERS", value="-ea") + if template.find("TEST_SEARCH_SCOPE") is None: + scope = ET.SubElement(template, "TEST_SEARCH_SCOPE") + ET.SubElement(scope, "value", defaultName="singleModule") + if template.find("method") is None: + before = ET.SubElement(template, "method", v="2") + ET.SubElement(before, "option", name="Make", enabled="true") + xml(".idea/workspace.xml", project) + print(f"Configured {len(chapters)} chapters and 5 shared/tool modules. Select JDK 21 and reopen the project.") + + +if __name__ == "__main__": + main() diff --git a/chapter01/chapter01.iml b/chapter01/chapter01.iml new file mode 100644 index 00000000..c5ec48d6 --- /dev/null +++ b/chapter01/chapter01.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter01" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter01" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter02/chapter02.iml b/chapter02/chapter02.iml new file mode 100644 index 00000000..3744c53c --- /dev/null +++ b/chapter02/chapter02.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter02" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter02" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter03/chapter03.iml b/chapter03/chapter03.iml new file mode 100644 index 00000000..afb124e2 --- /dev/null +++ b/chapter03/chapter03.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter03" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter03" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter04/chapter04.iml b/chapter04/chapter04.iml new file mode 100644 index 00000000..deca3652 --- /dev/null +++ b/chapter04/chapter04.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter04" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter04" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter05/chapter05.iml b/chapter05/chapter05.iml new file mode 100644 index 00000000..de62bba5 --- /dev/null +++ b/chapter05/chapter05.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter05" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter05" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter06/chapter06.iml b/chapter06/chapter06.iml new file mode 100644 index 00000000..380991da --- /dev/null +++ b/chapter06/chapter06.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter06" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter06" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter07/chapter07.iml b/chapter07/chapter07.iml new file mode 100644 index 00000000..0e472f64 --- /dev/null +++ b/chapter07/chapter07.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter07" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter07" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter08/chapter08.iml b/chapter08/chapter08.iml new file mode 100644 index 00000000..9219215c --- /dev/null +++ b/chapter08/chapter08.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter08" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter08" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter09/chapter09.iml b/chapter09/chapter09.iml new file mode 100644 index 00000000..2c8a9807 --- /dev/null +++ b/chapter09/chapter09.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter09" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter09" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter10/chapter10.iml b/chapter10/chapter10.iml index a352db38..4b588b86 100644 --- a/chapter10/chapter10.iml +++ b/chapter10/chapter10.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/chapter11/chapter11.iml b/chapter11/chapter11.iml index 92b9e5ca..ba1d65d7 100644 --- a/chapter11/chapter11.iml +++ b/chapter11/chapter11.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/chapter12/chapter12.iml b/chapter12/chapter12.iml index 28f6886a..aa6140e7 100644 --- a/chapter12/chapter12.iml +++ b/chapter12/chapter12.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/chapter13/chapter13.iml b/chapter13/chapter13.iml index 6fa60f3f..1059b490 100644 --- a/chapter13/chapter13.iml +++ b/chapter13/chapter13.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/chapter14/chapter14.iml b/chapter14/chapter14.iml index 4c719b6d..9b3ebcb6 100644 --- a/chapter14/chapter14.iml +++ b/chapter14/chapter14.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/chapter15/chapter15.iml b/chapter15/chapter15.iml new file mode 100644 index 00000000..c32e7935 --- /dev/null +++ b/chapter15/chapter15.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter15" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter15" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter16/chapter16.iml b/chapter16/chapter16.iml new file mode 100644 index 00000000..6900ca28 --- /dev/null +++ b/chapter16/chapter16.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter16" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter16" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter17a/chapter17a.iml b/chapter17a/chapter17a.iml index 5de7746c..cb7bf492 100644 --- a/chapter17a/chapter17a.iml +++ b/chapter17a/chapter17a.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/chapter17b/chapter17b.iml b/chapter17b/chapter17b.iml index 3075ff71..9c6d0fd0 100644 --- a/chapter17b/chapter17b.iml +++ b/chapter17b/chapter17b.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/chapter18/chapter18.iml b/chapter18/chapter18.iml new file mode 100644 index 00000000..4fb35bc8 --- /dev/null +++ b/chapter18/chapter18.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter18" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter18" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter19/chapter19.iml b/chapter19/chapter19.iml new file mode 100644 index 00000000..463c54e4 --- /dev/null +++ b/chapter19/chapter19.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter19" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter19" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter20/chapter20.iml b/chapter20/chapter20.iml new file mode 100644 index 00000000..0b57cffa --- /dev/null +++ b/chapter20/chapter20.iml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter20" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter20" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter21/chapter21.iml b/chapter21/chapter21.iml new file mode 100644 index 00000000..99a3e35d --- /dev/null +++ b/chapter21/chapter21.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter21" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter21" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter22/chapter22.iml b/chapter22/chapter22.iml new file mode 100644 index 00000000..c1a9cdff --- /dev/null +++ b/chapter22/chapter22.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter22" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter22" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter23/chapter23.iml b/chapter23/chapter23.iml new file mode 100644 index 00000000..51bb984f --- /dev/null +++ b/chapter23/chapter23.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter23" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter23" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java index 1bd1a74d..24d9dc6f 100644 --- a/chapter23/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter23/src/main/java/com/seaofnodes/simple/Parser.java @@ -1233,19 +1233,17 @@ public class Parser { // Pre-dec/pre-inc int old = pos(); if( match("--") || match("++") ) { - int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._final ) - throw error("Cannot reassign final '"+n._name+"'"); - Node expr = n.type() instanceof TypeFloat - ? peep(new AddFNode(_scope.in(n),con(TypeFloat.constant(delta)))) - : zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._final ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -1266,7 +1264,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return newString(parseString()); if( matchx("true" ) ) return con(1); @@ -1313,7 +1314,7 @@ public class Parser { // Else safety check for accessing fields out of function scope. if( instance(var) ) { Node self = _scope.in(_scope.lookup("self")); // Insert self reference - return parsePostfixName(self,id); + return parsePostfixName(self,id,pre); } // Load local value @@ -1324,7 +1325,7 @@ public class Parser { else throw error("Cannot read uninitialized field '"+id+"'"); // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) // Normal primary, check for postfix updates return parsePostfix(rvalue); // Assign-update direct into Scope @@ -1332,7 +1333,8 @@ public class Parser { throw error("Cannot reassign final '"+var._name+"'"); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // true if instance variable lookup (var is in any number of nested blocks, @@ -1561,7 +1563,9 @@ public class Parser { * expr ('[' expr ']')* [ [op]= expr ] // Array reference * </pre> */ - private Node parsePostfixName(Node expr, String name) { + private Node parsePostfixName(Node expr, String name) { return parsePostfixName(expr,name,0); } + + private Node parsePostfixName(Node expr, String name, int pre) { if( expr._type==Type.NIL ) throw error("Accessing unknown field '" + name + "' from 'null'"); @@ -1594,7 +1598,7 @@ public class Parser { : off(base,name)).keep(); // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node val = parseAsgn().keep(); Node lift = liftExpr( val, tf, false ); @@ -1613,14 +1617,16 @@ public class Parser { load = peep(load); // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,tf); + // A narrow Store may drop its conversion; prefix still returns it. + if( pre!=0 ) op.keep(); store(name,f._alias,tf,expr.unkeep(),off.unkeep(),op,false,base.isAry() ? ctrl() : null); - load = postfix(ch) ? load.unkeep() : op; - // And use the original loaded value as the result - return load; - //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN + if( pre!=0 ) op.unkeep(); + if( postfix(ch) ) load.unkeep(); + // Prefix returns the new value; postfix returns the old load. + return pre==0 && postfix(ch) ? load : op; } else { off.unkill(); // Method call: Loaded a function from a named field, AND the base diff --git a/chapter23/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter23/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..56d553b3 --- /dev/null +++ b/chapter23/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,TestC.CALL_CONVENTION,null,abi,driver.toString(),"build/objs/prefixUpdate","S","",-1); + } +} diff --git a/chapter24a/chapter24a.iml b/chapter24a/chapter24a.iml new file mode 100644 index 00000000..5512b871 --- /dev/null +++ b/chapter24a/chapter24a.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter24a" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter24a" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter24a/src/main/java/com/seaofnodes/simple/Parser.java b/chapter24a/src/main/java/com/seaofnodes/simple/Parser.java index 81c7be32..7a67c096 100644 --- a/chapter24a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter24a/src/main/java/com/seaofnodes/simple/Parser.java @@ -1246,19 +1246,17 @@ public class Parser { // Pre-dec/pre-inc int old = pos(); if( match("--") || match("++") ) { - int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._final ) - throw error("Cannot reassign final '"+n._name+"'"); - Node expr = n.type() instanceof TypeFloat - ? peep(new AddFNode(_scope.in(n),con(TypeFloat.constant(delta)))) - : zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._final ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -1279,7 +1277,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return newString(parseString()); if( matchx("true" ) ) return con(1); @@ -1326,7 +1327,7 @@ public class Parser { // Else safety check for accessing fields out of function scope. if( instance(var) ) { Node self = _scope.in(_scope.lookup("self")); // Insert self reference - return parsePostfixName(self,id); + return parsePostfixName(self,id,pre); } // Load local value @@ -1337,7 +1338,7 @@ public class Parser { else throw error("Cannot read uninitialized field '"+id+"'"); // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) // Normal primary, check for postfix updates return parsePostfix(rvalue); // Assign-update direct into Scope @@ -1345,7 +1346,8 @@ public class Parser { throw error("Cannot reassign final '"+var._name+"'"); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // true if instance variable lookup (var is in any number of nested blocks, @@ -1574,7 +1576,9 @@ public class Parser { * expr ('[' expr ']')* [ [op]= expr ] // Array reference * </pre> */ - private Node parsePostfixName(Node expr, String name) { + private Node parsePostfixName(Node expr, String name) { return parsePostfixName(expr,name,0); } + + private Node parsePostfixName(Node expr, String name, int pre) { if( expr._type==Type.NIL ) throw error("Accessing unknown field '" + name + "' from 'null'"); @@ -1609,7 +1613,7 @@ public class Parser { : off(base,name)).keep(); // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node val = parseAsgn().keep(); Node lift = liftExpr( val, tf, false ); @@ -1628,14 +1632,16 @@ public class Parser { load = peep(load); // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,tf); + // A narrow Store may drop its conversion; prefix still returns it. + if( pre!=0 ) op.keep(); store(name,f._alias,tf,expr.unkeep(),off.unkeep(),op,false,base.isAry() ? ctrl() : null); - load = postfix(ch) ? load.unkeep() : op; - // And use the original loaded value as the result - return load; - //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN + if( pre!=0 ) op.unkeep(); + if( postfix(ch) ) load.unkeep(); + // Prefix returns the new value; postfix returns the old load. + return pre==0 && postfix(ch) ? load : op; } else { off.unkill(); // Method call: Loaded a function from a named field, AND the base diff --git a/chapter24a/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter24a/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..56d553b3 --- /dev/null +++ b/chapter24a/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,TestC.CALL_CONVENTION,null,abi,driver.toString(),"build/objs/prefixUpdate","S","",-1); + } +} diff --git a/chapter24b/chapter24b.iml b/chapter24b/chapter24b.iml new file mode 100644 index 00000000..c385fd00 --- /dev/null +++ b/chapter24b/chapter24b.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter24b" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter24b" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter24b/src/main/java/com/seaofnodes/simple/Parser.java b/chapter24b/src/main/java/com/seaofnodes/simple/Parser.java index 81c7be32..7a67c096 100644 --- a/chapter24b/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter24b/src/main/java/com/seaofnodes/simple/Parser.java @@ -1246,19 +1246,17 @@ public class Parser { // Pre-dec/pre-inc int old = pos(); if( match("--") || match("++") ) { - int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._final ) - throw error("Cannot reassign final '"+n._name+"'"); - Node expr = n.type() instanceof TypeFloat - ? peep(new AddFNode(_scope.in(n),con(TypeFloat.constant(delta)))) - : zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._final ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -1279,7 +1277,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return newString(parseString()); if( matchx("true" ) ) return con(1); @@ -1326,7 +1327,7 @@ public class Parser { // Else safety check for accessing fields out of function scope. if( instance(var) ) { Node self = _scope.in(_scope.lookup("self")); // Insert self reference - return parsePostfixName(self,id); + return parsePostfixName(self,id,pre); } // Load local value @@ -1337,7 +1338,7 @@ public class Parser { else throw error("Cannot read uninitialized field '"+id+"'"); // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) // Normal primary, check for postfix updates return parsePostfix(rvalue); // Assign-update direct into Scope @@ -1345,7 +1346,8 @@ public class Parser { throw error("Cannot reassign final '"+var._name+"'"); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // true if instance variable lookup (var is in any number of nested blocks, @@ -1574,7 +1576,9 @@ public class Parser { * expr ('[' expr ']')* [ [op]= expr ] // Array reference * </pre> */ - private Node parsePostfixName(Node expr, String name) { + private Node parsePostfixName(Node expr, String name) { return parsePostfixName(expr,name,0); } + + private Node parsePostfixName(Node expr, String name, int pre) { if( expr._type==Type.NIL ) throw error("Accessing unknown field '" + name + "' from 'null'"); @@ -1609,7 +1613,7 @@ public class Parser { : off(base,name)).keep(); // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node val = parseAsgn().keep(); Node lift = liftExpr( val, tf, false ); @@ -1628,14 +1632,16 @@ public class Parser { load = peep(load); // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,tf); + // A narrow Store may drop its conversion; prefix still returns it. + if( pre!=0 ) op.keep(); store(name,f._alias,tf,expr.unkeep(),off.unkeep(),op,false,base.isAry() ? ctrl() : null); - load = postfix(ch) ? load.unkeep() : op; - // And use the original loaded value as the result - return load; - //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN + if( pre!=0 ) op.unkeep(); + if( postfix(ch) ) load.unkeep(); + // Prefix returns the new value; postfix returns the old load. + return pre==0 && postfix(ch) ? load : op; } else { off.unkill(); // Method call: Loaded a function from a named field, AND the base diff --git a/chapter24b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter24b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..56d553b3 --- /dev/null +++ b/chapter24b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,TestC.CALL_CONVENTION,null,abi,driver.toString(),"build/objs/prefixUpdate","S","",-1); + } +} diff --git a/chapter25/chapter25.iml b/chapter25/chapter25.iml new file mode 100644 index 00000000..fc9e2e3d --- /dev/null +++ b/chapter25/chapter25.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter25" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter25" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java index 56233250..727b1c61 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java @@ -1228,17 +1228,17 @@ public class Parser { // Pre-dec/pre-inc int old = pos(); if( match("--") || match("++") ) { - int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._final ) - throw error("Cannot reassign final '"+n._name+"'"); - Node expr = liftExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type(),true); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._final ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -1259,7 +1259,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return newString(parseString()); if( matchx("true" ) ) return con(1); @@ -1307,7 +1310,7 @@ public class Parser { if( instance(var) ) { Var selfVar = _scope.lookup("self"); Node self = liftExpr(_scope.in(selfVar),selfVar.type(),true); - return parsePostfixName(self,id); + return parsePostfixName(self,id,pre); } // Load local value @@ -1322,7 +1325,7 @@ public class Parser { else throw error("Cannot read uninitialized field '"+id+"'"); // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) // Normal primary, check for postfix updates return parsePostfix(rvalue); // Assign-update direct into Scope @@ -1330,7 +1333,8 @@ public class Parser { throw error("Cannot reassign final '"+var._name+"'"); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // true if instance variable lookup (var is in any number of nested blocks, @@ -1573,14 +1577,16 @@ public class Parser { * expr ('[' expr ']')* [ [op]= expr ] // Array reference * </pre> */ - private Node parsePostfixName(Node expr, String name) { + private Node parsePostfixName(Node expr, String name) { return parsePostfixName(expr,name,0); } + + private Node parsePostfixName(Node expr, String name, int pre) { if( expr._type==Type.NIL ) throw error("Accessing unknown field '" + name + "' from 'null'"); // Sanity check expr for being a reference if( !(expr._type instanceof TypeMemPtr ptr) ) - return parseUnknownField(expr,name); + return parseUnknownField(expr,name,pre); // Find the field from the Type. Lookup in the base object field names. TypeMemPtr ptr2 = (TypeMemPtr)TYPES.get(ptr._obj._name); @@ -1588,7 +1594,7 @@ public class Parser { ptr = ptr.makeFrom(ptr2._obj); // The declaration fixes the field shape; retain this view's access. TypeStruct base = ptr._obj; int fidx = base.find(name); - if( fidx==-1 && base._open ) return parseUnknownField(expr,name); + if( fidx==-1 && base._open ) return parseUnknownField(expr,name,pre); if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); @@ -1609,7 +1615,7 @@ public class Parser { : off(base,name)).keep(); // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node val = parseAsgn().keep(); Node lift = liftExpr( val, tf, false ); @@ -1628,14 +1634,16 @@ public class Parser { load = peep(load); // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,tf); + // A narrow Store may drop its conversion; prefix still returns it. + if( pre!=0 ) op.keep(); store(name,f._alias,tf,expr.unkeep(),off.unkeep(),op,false,base.isAry() ? ctrl() : null); - load = postfix(ch) ? load.unkeep() : op; - // And use the original loaded value as the result - return load; - //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN + if( pre!=0 ) op.unkeep(); + if( postfix(ch) ) load.unkeep(); + // Prefix returns the new value; postfix returns the old load. + return pre==0 && postfix(ch) ? load : op; } else { off.unkill(); // Method call: Loaded a function from a named field, AND the base @@ -1660,7 +1668,7 @@ public class Parser { // Syntax fixes the access even when a forward definition or a loop // leaves the receiver untyped. Resolve its alias and layout in the graph. - private Node parseUnknownField(Node expr, String name) { + private Node parseUnknownField(Node expr, String name, int pre) { expr.keep(); Node off = new FldOffNode(expr,name).peephole(); if( name.equals("[]") ) { @@ -1669,7 +1677,7 @@ public class Parser { off = peep(new AddNode(off,peep(new ShlNode(loc(),index,scale)))); } off.keep(); - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node value = parseAsgn().keep(); Node st = new StoreNode(loc(),name,1,Type.BOTTOM,mem(),expr,off,value,false); st.setDef(0,ctrl()); @@ -1681,7 +1689,7 @@ public class Parser { Node load = new LoadNode(loc(),name,1,mem(),expr,off); load.setDef(0,ctrl()); load = peep(load); - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,Type.BOTTOM).keep(); Node st = new StoreNode(loc(),name,1,Type.BOTTOM,mem(),expr,off,op,false); @@ -1691,7 +1699,7 @@ public class Parser { // the stored value after conversion; postfix returns the old load. // Reading our own Store folds once its alias and width are known. if( postfix(ch) ) load.unkeep(); - else { + if( pre!=0 || !postfix(ch) ) { load = new LoadNode(loc(),name,1,mem(),expr,off); load.setDef(0,ctrl()); load = peep(load); @@ -1701,7 +1709,7 @@ public class Parser { } else load.keep(); expr.unkill(); off.unkill(); - return parsePostfix(load.unkeep()); + return pre==0 ? parsePostfix(load.unkeep()) : load.unkeep(); } // zero/sign extend. "i" is limited to either classic unsigned (min==0) or diff --git a/chapter25/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter25/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..56d553b3 --- /dev/null +++ b/chapter25/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,TestC.CALL_CONVENTION,null,abi,driver.toString(),"build/objs/prefixUpdate","S","",-1); + } +} diff --git a/chapter26/chapter26.iml b/chapter26/chapter26.iml new file mode 100644 index 00000000..6d87ad07 --- /dev/null +++ b/chapter26/chapter26.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter26" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter26" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter26/src/main/java/com/seaofnodes/simple/Parser.java b/chapter26/src/main/java/com/seaofnodes/simple/Parser.java index 45c429d8..520d7e3a 100644 --- a/chapter26/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter26/src/main/java/com/seaofnodes/simple/Parser.java @@ -1475,18 +1475,17 @@ public class Parser { // Pre-dec/pre-inc int old = pos(); if( match("--") || match("++") ) { - int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._final && !_scope.canInit(n) ) - throw error("Cannot reassign final '"+n._name+"'"); - checkSharedField(n); - Node expr = liftExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type(),true); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._final && !_scope.canInit(var) ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -1507,7 +1506,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return newString(parseString()); if( matchx("true" ) ) return con(1); @@ -1555,7 +1557,7 @@ public class Parser { if( instance(var) ) { Var selfVar = _scope.lookup("self"); Node self = liftExpr(_scope.in(selfVar),selfVar.type(),true); - return parsePostfixName(self,id); + return parsePostfixName(self,id,pre); } // Load local value @@ -1570,7 +1572,7 @@ public class Parser { else throw error("Cannot read uninitialized field '"+id+"'"); // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) // Normal primary, check for postfix updates return parsePostfix(rvalue); // Assign-update direct into Scope @@ -1580,7 +1582,8 @@ public class Parser { checkCaptureWrite(var); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // true if instance variable lookup (var is in any number of nested blocks, @@ -1877,8 +1880,10 @@ public class Parser { * expr ('[' expr ']')* [ [op]= expr ] // Array reference * </pre> */ - private Node parsePostfixName(Node expr, String name) { - if( privateSelf(expr) ) return parsePrivateField(name); + private Node parsePostfixName(Node expr, String name) { return parsePostfixName(expr,name,0); } + + private Node parsePostfixName(Node expr, String name, int pre) { + if( privateSelf(expr) ) return parsePrivateField(name,pre); if( !privateSelf(expr) && mayBeSelf(expr,new java.util.BitSet()) ) throw error("Cannot access self through a merged alias during construction"); @@ -1887,7 +1892,7 @@ public class Parser { // Sanity check expr for being a reference if( !(expr._type instanceof TypeMemPtr ptr) ) - return parseUnknownField(expr,name); + return parseUnknownField(expr,name,pre); // Find the field from the Type. Lookup in the base object field names. TypeMemPtr ptr2 = (TypeMemPtr)TYPES.get(ptr._obj._name); @@ -1895,7 +1900,7 @@ public class Parser { ptr = ptr.makeFrom(ptr2._obj); // The declaration fixes the field shape; retain this view's access. TypeStruct base = ptr._obj; int fidx = base.find(name); - if( fidx==-1 && base._open ) return parseUnknownField(expr,name); + if( fidx==-1 && base._open ) return parseUnknownField(expr,name,pre); if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); @@ -1918,7 +1923,7 @@ public class Parser { : off(base,name)).keep(); // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node val = parseAsgn().keep(); Node lift = liftExpr( val, tf, false ); @@ -1937,14 +1942,16 @@ public class Parser { load = peep(load); // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,tf); + // A narrow Store may drop its conversion; prefix still returns it. + if( pre!=0 ) op.keep(); store(name,f._alias,tf,expr.unkeep(),off.unkeep(),op,false,base.isAry() ? ctrl() : null); - load = postfix(ch) ? load.unkeep() : op; - // And use the original loaded value as the result - return load; - //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN + if( pre!=0 ) op.unkeep(); + if( postfix(ch) ) load.unkeep(); + // Prefix returns the new value; postfix returns the old load. + return pre==0 && postfix(ch) ? load : op; } else { off.unkill(); // Method call: Loaded a function from a named field, AND the base @@ -1969,7 +1976,7 @@ public class Parser { // Syntax fixes the access even when a forward definition or a loop // leaves the receiver untyped. Resolve its alias and layout in the graph. - private Node parseUnknownField(Node expr, String name) { + private Node parseUnknownField(Node expr, String name, int pre) { expr.keep(); Node off = new FldOffNode(expr,name).peephole(); if( name.equals("[]") ) { @@ -1978,7 +1985,7 @@ public class Parser { off = peep(new AddNode(off,peep(new ShlNode(loc(),index,scale)))); } off.keep(); - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node value = parseAsgn().keep(); unknownStore(name,expr,off,value); expr.unkill(); @@ -1988,7 +1995,7 @@ public class Parser { Node load = new LoadNode(loc(),name,1,mem(),expr,off); load.setDef(0,ctrl()); load = peep(load); - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,Type.BOTTOM).keep(); unknownStore(name,expr,off,op); @@ -1996,7 +2003,7 @@ public class Parser { // the stored value after conversion; postfix returns the old load. // Reading our own Store folds once its alias and width are known. if( postfix(ch) ) load.unkeep(); - else { + if( pre!=0 || !postfix(ch) ) { load = new LoadNode(loc(),name,1,mem(),expr,off); load.setDef(0,ctrl()); load = peep(load); @@ -2010,7 +2017,7 @@ public class Parser { } else load.keep(); expr.unkill(); off.unkill(); - return parsePostfix(load.unkeep()); + return pre==0 ? parsePostfix(load.unkeep()) : load.unkeep(); } private void unknownStore(String name, Node ptr, Node off, Node value) { @@ -2022,12 +2029,12 @@ public class Parser { // Explicit self.x and an unqualified field name use the same local slot. // The map also keeps self.x distinct from a parameter/local named x. - private Node parsePrivateField(String name) { + private Node parsePrivateField(String name, int pre) { Var v=_fieldVars.get(name); if( v==null ) throw error("Cannot resolve field '"+name+"'"); if( v.type() instanceof TypeFunPtr ctor && ctor.ret() instanceof TypeMem ) throw error("A constructor is only called by new"); - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { checkPrivateWrite(name); Node value=parseAsgn().keep(); checkPublication(value); @@ -2037,13 +2044,14 @@ public class Parser { Node value=_scope.in(_scope.update(v,null)); if( value instanceof ConstantNode && (value._type==Type.BOTTOM || value._type==Type.TOP) ) throw error("Cannot read uninitialized field '"+name+"'"); - char ch=_lexer.matchOperAssign(); + char ch=pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { checkPrivateWrite(name); Node op=opAssign(ch,value,v.type()); checkPublication(op); _scope.update(v,op); - return postfix(ch) ? value.unkeep() : op; + if( postfix(ch) ) value.unkeep(); + return pre==0 && postfix(ch) ? value : op; } if( value._type instanceof TypeFunPtr && match("(") ) return parsePostfix(require(functionCall(value,self()),")")); diff --git a/chapter26/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter26/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..56d553b3 --- /dev/null +++ b/chapter26/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,TestC.CALL_CONVENTION,null,abi,driver.toString(),"build/objs/prefixUpdate","S","",-1); + } +} diff --git a/chapter27/chapter27.iml b/chapter27/chapter27.iml new file mode 100644 index 00000000..40cc410d --- /dev/null +++ b/chapter27/chapter27.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter27" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter27" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter27/src/main/java/com/seaofnodes/simple/Parser.java b/chapter27/src/main/java/com/seaofnodes/simple/Parser.java index 145feb15..93ad8f4e 100644 --- a/chapter27/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter27/src/main/java/com/seaofnodes/simple/Parser.java @@ -1475,18 +1475,17 @@ public class Parser { // Pre-dec/pre-inc int old = pos(); if( match("--") || match("++") ) { - int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post + int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._final && !_scope.canInit(n) ) - throw error("Cannot reassign final '"+n._name+"'"); - checkSharedField(n); - Node expr = liftExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type(),true); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._final && !_scope.canInit(var) ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -1507,7 +1506,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return newString(parseString()); if( matchx("true" ) ) return con(1); @@ -1555,7 +1557,7 @@ public class Parser { if( instance(var) ) { Var selfVar = _scope.lookup("self"); Node self = liftExpr(_scope.in(selfVar),selfVar.type(),true); - return parsePostfixName(self,id); + return parsePostfixName(self,id,pre); } // Load local value @@ -1570,7 +1572,7 @@ public class Parser { else throw error("Cannot read uninitialized field '"+id+"'"); // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) // Normal primary, check for postfix updates return parsePostfix(rvalue); // Assign-update direct into Scope @@ -1580,7 +1582,8 @@ public class Parser { checkCaptureWrite(var); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // true if instance variable lookup (var is in any number of nested blocks, @@ -1877,8 +1880,10 @@ public class Parser { * expr ('[' expr ']')* [ [op]= expr ] // Array reference * </pre> */ - private Node parsePostfixName(Node expr, String name) { - if( privateSelf(expr) ) return parsePrivateField(name); + private Node parsePostfixName(Node expr, String name) { return parsePostfixName(expr,name,0); } + + private Node parsePostfixName(Node expr, String name, int pre) { + if( privateSelf(expr) ) return parsePrivateField(name,pre); if( !privateSelf(expr) && mayBeSelf(expr,new java.util.BitSet()) ) throw error("Cannot access self through a merged alias during construction"); @@ -1887,7 +1892,7 @@ public class Parser { // Sanity check expr for being a reference if( !(expr._type instanceof TypeMemPtr ptr) ) - return parseUnknownField(expr,name); + return parseUnknownField(expr,name,pre); // Find the field from the Type. Lookup in the base object field names. TypeMemPtr ptr2 = (TypeMemPtr)TYPES.get(ptr._obj._name); @@ -1895,7 +1900,7 @@ public class Parser { ptr = ptr.makeFrom(ptr2._obj); // The declaration fixes the field shape; retain this view's access. TypeStruct base = ptr._obj; int fidx = base.find(name); - if( fidx==-1 && base._open ) return parseUnknownField(expr,name); + if( fidx==-1 && base._open ) return parseUnknownField(expr,name,pre); if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'"); @@ -1918,7 +1923,7 @@ public class Parser { : off(base,name)).keep(); // Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node val = parseAsgn().keep(); Node lift = liftExpr( val, tf, false ); @@ -1937,14 +1942,16 @@ public class Parser { load = peep(load); // Check for assign-update, "ptr.fld += expr" or "ary[idx]++" - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,tf); + // A narrow Store may drop its conversion; prefix still returns it. + if( pre!=0 ) op.keep(); store(name,f._alias,tf,expr.unkeep(),off.unkeep(),op,false,base.isAry() ? ctrl() : null); - load = postfix(ch) ? load.unkeep() : op; - // And use the original loaded value as the result - return load; - //throw Utils.TODO(); // NO POSTFIX AFTER STORE, JUST RETURN + if( pre!=0 ) op.unkeep(); + if( postfix(ch) ) load.unkeep(); + // Prefix returns the new value; postfix returns the old load. + return pre==0 && postfix(ch) ? load : op; } else { off.unkill(); // Method call: Loaded a function from a named field, AND the base @@ -1969,7 +1976,7 @@ public class Parser { // Syntax fixes the access even when a forward definition or a loop // leaves the receiver untyped. Resolve its alias and layout in the graph. - private Node parseUnknownField(Node expr, String name) { + private Node parseUnknownField(Node expr, String name, int pre) { expr.keep(); Node off = new FldOffNode(expr,name).peephole(); if( name.equals("[]") ) { @@ -1978,7 +1985,7 @@ public class Parser { off = peep(new AddNode(off,peep(new ShlNode(loc(),index,scale)))); } off.keep(); - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { Node value = parseAsgn().keep(); unknownStore(name,expr,off,value); expr.unkill(); @@ -1988,7 +1995,7 @@ public class Parser { Node load = new LoadNode(loc(),name,1,mem(),expr,off); load.setDef(0,ctrl()); load = peep(load); - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { Node op = opAssign(ch,load,Type.BOTTOM).keep(); unknownStore(name,expr,off,op); @@ -1996,7 +2003,7 @@ public class Parser { // the stored value after conversion; postfix returns the old load. // Reading our own Store folds once its alias and width are known. if( postfix(ch) ) load.unkeep(); - else { + if( pre!=0 || !postfix(ch) ) { load = new LoadNode(loc(),name,1,mem(),expr,off); load.setDef(0,ctrl()); load = peep(load); @@ -2010,7 +2017,7 @@ public class Parser { } else load.keep(); expr.unkill(); off.unkill(); - return parsePostfix(load.unkeep()); + return pre==0 ? parsePostfix(load.unkeep()) : load.unkeep(); } private void unknownStore(String name, Node ptr, Node off, Node value) { @@ -2022,12 +2029,12 @@ public class Parser { // Explicit self.x and an unqualified field name use the same local slot. // The map also keeps self.x distinct from a parameter/local named x. - private Node parsePrivateField(String name) { + private Node parsePrivateField(String name, int pre) { Var v=_fieldVars.get(name); if( v==null ) throw error("Cannot resolve field '"+name+"'"); if( v.type() instanceof TypeFunPtr ctor && ctor.ret() instanceof TypeMem ) throw error("A constructor is only called by new"); - if( matchOpx('=','=') ) { + if( pre==0 && matchOpx('=','=') ) { checkPrivateWrite(name); Node value=parseAsgn().keep(); checkPublication(value); @@ -2037,13 +2044,14 @@ public class Parser { Node value=_scope.in(_scope.update(v,null)); if( value instanceof ConstantNode && (value._type==Type.BOTTOM || value._type==Type.TOP) ) throw error("Cannot read uninitialized field '"+name+"'"); - char ch=_lexer.matchOperAssign(); + char ch=pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch!=0 ) { checkPrivateWrite(name); Node op=opAssign(ch,value,v.type()); checkPublication(op); _scope.update(v,op); - return postfix(ch) ? value.unkeep() : op; + if( postfix(ch) ) value.unkeep(); + return pre==0 && postfix(ch) ? value : op; } if( value._type instanceof TypeFunPtr && match("(") ) return parsePostfix(require(functionCall(value,self()),")")); diff --git a/chapter27/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter27/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..56d553b3 --- /dev/null +++ b/chapter27/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,53 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeInteger; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,TestC.CALL_CONVENTION,null,abi,driver.toString(),"build/objs/prefixUpdate","S","",-1); + } +} diff --git a/chapter28a/chapter28a.iml b/chapter28a/chapter28a.iml new file mode 100644 index 00000000..1f5fc984 --- /dev/null +++ b/chapter28a/chapter28a.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter28a" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter28a" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter28a/src/main/java/com/seaofnodes/simple/Parser.java b/chapter28a/src/main/java/com/seaofnodes/simple/Parser.java index c3ad0284..5a9a5ca3 100644 --- a/chapter28a/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter28a/src/main/java/com/seaofnodes/simple/Parser.java @@ -1993,19 +1993,16 @@ public class Parser { int old = pos(); if( match("--") || match("++") ) { int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._extern!=0 ) - return parsePostfixName(_scope.in(n),name,delta); - if( n._final && !_scope.canInit(n) ) - throw error("Cannot reassign final '"+n._name+"'"); - checkSharedFunction(n); - Node expr = convertExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type()); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._extern==0 && var._final && !_scope.canInit(var) ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -2026,7 +2023,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return parsePostfix(newString(parseString())); if( matchx("true" ) ) return con(1); @@ -2085,7 +2085,7 @@ public class Parser { // Load local value Node rvalue = _scope.in(var); if( var._extern!=0 ) - return parsePostfixName(rvalue,id,0); + return parsePostfixName(rvalue,id,pre); if( var._fref ) return parsePostfix(rvalue); // A constructor may read the unfinished object, but each field it @@ -2101,7 +2101,7 @@ public class Parser { Kind fk = _scope._kinds.at(fx); // Access instance field from 'self' if( kx+1 == fx && kk instanceof Kind.Func inst && FunNode.isInstance(inst._name) && fk instanceof Kind.Func method && !inExplicitConstructor() ) - return parsePostfixName(_scope.in(method._lexSize),id,0); + return parsePostfixName(_scope.in(method._lexSize),id,pre); // Check for a function-escaping variable; these require true // closures. Final constants are OK; final vars require a hidden var @@ -2119,13 +2119,13 @@ public class Parser { // Get the most recent sharpen global TypeStruct clz = (TypeStruct) TYPES.get( ((TypeMemPtr) clzptr.type())._obj._name ); TypeMemPtr clztmp = TypeMemPtr.make( (byte) 2, clz, true ); - return parsePostfixName( con( clztmp ), id,0 ); + return parsePostfixName( con( clztmp ), id,pre ); //return parsePostfixName(_scope.in(2),id); } } // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) { // Normal primary, check for postfix updates int selfx = _scope.kindFcn(var)._lexSize; // Might be a method call from inside a method, so no explicit "self". @@ -2138,7 +2138,8 @@ public class Parser { checkSharedFunction(var); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // Check for assign-update, "x += e0". diff --git a/chapter28a/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter28a/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..71612cc0 --- /dev/null +++ b/chapter28a/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,58 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,"prefixUpdate",null,TestC.CALL_CONVENTION,abi,driver.toString(),"",-1); + } + + @Test public void testModuleFieldWrites() { + CodeGen code=new CodeGen("int calls=0; val next_noInline={ -> ++calls; }; return next_noInline()+10*next_noInline();") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("21",Eval2.eval(code,0)); + } +} diff --git a/chapter28b/chapter28b.iml b/chapter28b/chapter28b.iml new file mode 100644 index 00000000..aa7365c4 --- /dev/null +++ b/chapter28b/chapter28b.iml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/chapter28b" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/chapter28b" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="graph" /> + <orderEntry type="module" module-name="print" /> + <orderEntry type="module" module-name="isa" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java b/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java index 87a3570a..9df7fd18 100644 --- a/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter28b/src/main/java/com/seaofnodes/simple/Parser.java @@ -1994,19 +1994,16 @@ public class Parser { int old = pos(); if( match("--") || match("++") ) { int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Inc vs Dec + int start = pos(); String name = _lexer.matchId(); - if( name!=null ) { - Var n = _scope.lookup(name); - if( n != null && !(n.type() instanceof TypeMemPtr) ) { - if( n._extern!=0 ) - return parsePostfixName(_scope.in(n),name,delta); - if( n._final && !_scope.canInit(n) ) - throw error("Cannot reassign final '"+n._name+"'"); - checkSharedFunction(n); - Node expr = convertExpr(peep(new AddNode(_scope.in(n),con(delta))),n.type()); - _scope.update(n,expr); - return expr; - } + Var var = name==null ? null : _scope.lookup(name); + if( var!=null && !(var.type() instanceof TypeMemPtr) ) { + if( var._extern==0 && var._final && !_scope.canInit(var) ) + throw error("Cannot reassign final '"+var._name+"'"); + // Resolve fields just as a read or postfix update does. Updating + // their declaration's local slot would silently lose the Store. + pos(start); + return parsePrimary(delta); } // Reset, try again pos(old); @@ -2027,7 +2024,10 @@ public class Parser { * </pre> * @return a primary {@link Node}, never {@code null} */ - private Node parsePrimary() { + private Node parsePrimary() { return parsePrimary(0); } + + // pre is +/-1 for a prefix update of a known name, zero otherwise. + private Node parsePrimary(int pre) { if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral(); if( _lexer.peek('"') ) return parsePostfix(newString(parseString())); if( matchx("true" ) ) return con(1); @@ -2086,7 +2086,7 @@ public class Parser { // Load local value Node rvalue = _scope.in(var); if( var._extern!=0 ) - return parsePostfixName(rvalue,id,0); + return parsePostfixName(rvalue,id,pre); if( var._fref ) return parsePostfix(rvalue); // A constructor may read the unfinished object, but each field it @@ -2102,7 +2102,7 @@ public class Parser { Kind fk = _scope._kinds.at(fx); // Access instance field from 'self' if( kx+1 == fx && kk instanceof Kind.Func inst && FunNode.isInstance(inst._name) && fk instanceof Kind.Func method && !inExplicitConstructor() ) - return parsePostfixName(_scope.in(method._lexSize),id,0); + return parsePostfixName(_scope.in(method._lexSize),id,pre); // Check for a function-escaping variable; these require true // closures. Final constants are OK; final vars require a hidden var @@ -2120,13 +2120,13 @@ public class Parser { // Get the most recent sharpen global TypeStruct clz = (TypeStruct) TYPES.get( ((TypeMemPtr) clzptr.type())._obj._name ); TypeMemPtr clztmp = TypeMemPtr.make( (byte) 2, clz, true ); - return parsePostfixName( con( clztmp ), id,0 ); + return parsePostfixName( con( clztmp ), id,pre ); //return parsePostfixName(_scope.in(2),id); } } // Check for assign-update, x += e0; - char ch = _lexer.matchOperAssign(); + char ch = pre==0 ? _lexer.matchOperAssign() : (char)pre; if( ch==0 ) { // Normal primary, check for postfix updates int selfx = _scope.kindFcn(var)._lexSize; // Might be a method call from inside a method, so no explicit "self". @@ -2139,7 +2139,8 @@ public class Parser { checkSharedFunction(var); Node op = opAssign(ch,rvalue, var.type() ); _scope.update(var,op); - return postfix(ch) ? rvalue.unkeep() : op; + if( postfix(ch) ) rvalue.unkeep(); + return pre==0 && postfix(ch) ? rvalue : op; } // Check for assign-update, "x += e0". diff --git a/chapter28b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java b/chapter28b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java new file mode 100644 index 00000000..71612cc0 --- /dev/null +++ b/chapter28b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java @@ -0,0 +1,58 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.*; + +public class PrefixUpdateTest { + @Test public void testMethodFieldWrites() { + String[] fields = {"int x=0", "int x=3", "u8 x=0", "f32 x=0"}; + String[] updates = {"++x", "--x", "++x", "++x"}; + String[] results = {"221", "112", "221", "221.0"}; + for(int i=0;i<fields.length;i++) + for(int seed:new int[]{2,13,123}) { + String src="struct C { "+fields[i]+"; val change_noInline={ -> "+updates[i]+"; }; }; " + +"var c=new C; val a=c.change_noInline(); val b=c.change_noInline(); return a+10*b+100*c.x;"; + CodeGen code=new CodeGen(src,seed,true).driver(CodeGen.Phase.TypeCheck); + assertEquals(fields[i],results[i],Eval2.eval(code,0)); + } + } + + @Test public void testLocalAndFinalUpdates() { + CodeGen code=new CodeGen("int x=arg; return ++x*100 + --x*10 + x;").driver(CodeGen.Phase.TypeCheck); + assertEquals("433",Eval2.eval(code,3)); + code=new CodeGen("struct C { int x=7; val f={int x -> ++x;}; }; var c=new C; return c.f(3)+10*c.x;") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("74",Eval2.eval(code,0)); + for(String src:new String[]{"val x=1; return ++x;", + "struct C { val x=1; val f={ -> ++x; }; }; var c=new C; return c.f();"}) { + try { + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + fail("final update"); + } catch(RuntimeException e) { + assertTrue(e.getMessage(),e.getMessage().contains("final")); + } + } + } + + @Test public void testNativeByteOverflow() throws java.io.IOException { + String src="val prefixUpdate={int arg -> " + +"struct C { u8 x=255; val inc_noInline={ -> ++x; }; }; " + +"var c=new C; val a=c.inc_noInline(); val b=c.inc_noInline(); return a+10*b+100*c.x; };"; + var dir=java.nio.file.Path.of("build/objs"); + java.nio.file.Files.createDirectories(dir); + var driver=dir.resolve("prefixUpdate.c"); + java.nio.file.Files.writeString(driver,""" + __attribute__((CALL_CONV)) extern long long prefixUpdate(long long); + int main(void) { return prefixUpdate(0)!=110; } + """); + String abi=TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"; + TestC.run(src,"prefixUpdate",null,TestC.CALL_CONVENTION,abi,driver.toString(),"",-1); + } + + @Test public void testModuleFieldWrites() { + CodeGen code=new CodeGen("int calls=0; val next_noInline={ -> ++calls; }; return next_noInline()+10*next_noInline();") + .driver(CodeGen.Phase.TypeCheck); + assertEquals("21",Eval2.eval(code,0)); + } +} diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md index 6e6d1d5c..1c2ffb85 100644 --- a/docs/chapter-backports.md +++ b/docs/chapter-backports.md @@ -111,32 +111,26 @@ language restriction into Chapters 10-24 or the named pre-module snapshots. Establish an earlier-chapter failure before changing compiler behavior. -### Prefix update of an enclosing class field +### Narrow stores in the ideal evaluator -The following returns 11 instead of 21 in numbered Chapter 25, Modules, and -Separate Compilation: +The prefix-update regression exposed an independent evaluator bug. In Chapter +23, this postfix-only example returns 25710 in Eval2 instead of 110: ```simple -int calls=0; -val next_noInline={ -> ++calls; }; -return next_noInline()+10*next_noInline(); -``` - -The prefix path updates an enclosing parser slot instead of class memory. -Postfix `calls++; return calls;` works and is used in the new chapter example. -This is independent of importing IR. The stage audit also reproduced the -instance-method form in Chapter 23, where methods first appear: - -```simple -struct C { int x=0; val inc={ -> ++x; }; }; +struct C { u8 x=255; val next_noInline={ -> x++; return x; }; }; var c=new C; -return c.inc()+10*c.inc(); +val a=c.next_noInline(); +val b=c.next_noInline(); +return a+10*b+100*c.x; ``` -It returns 11 instead of 21 in Chapters 23 and 26. Prefix lookup bypasses the -ordinary field-access path. A fix should share name resolution with other -reads/updates while preserving constructor locals, and propagate from 23 -through 28b. No fix is included in the numbering change. +`Eval2.store` copies the raw value into its Java object array. It does not +truncate to the field width, even though Store optimization removes masks that +machine stores perform implicitly. The native prefix-update regression checks +that the stored byte wraps and the expression returns the converted value. +Establish the first affected evaluator and check signed fields and floating +rounding before propagating a fix. Do not change valid generated code to match +an incorrect ideal evaluator. ### Constructor loop with break/continue: worklist assertion diff --git a/docs/intellij.md b/docs/intellij.md new file mode 100644 index 00000000..a488fca6 --- /dev/null +++ b/docs/intellij.md @@ -0,0 +1,43 @@ +# IntelliJ + +Open the repository folder as an existing IntelliJ project. Select JDK 21 in +**File > Project Structure > Project SDK** if needed. The native `.iml` setup +includes every numbered chapter and the shared Java code; no Maven import is +needed. Each chapter has separate main and test output under `build/idea` and +never depends on another chapter's compiler. Make's output stays separate. + +On a fresh checkout, run `make lib` to fetch JUnit, then close the project and run: + +```sh +python build-support/idea.py +``` + +Reopen the repository folder. The script refreshes the module list and test +configurations, and sets the JUnit template's working directory to +`$MODULE_WORKING_DIR$`. It preserves the selected SDK and existing personal run +configurations and breakpoints. Original settings are backed up in +`build/idea-backup`. Run it again after adding or renaming chapters, with the +project closed so IntelliJ cannot overwrite the new list with its old one. +After a chapter move, use **Build > Rebuild Project** once to remove old compiled +classes from reused chapter directories. + +Open a chapter's `src/test/java` file and use the gutter's **Debug** action on a +test method. Its configuration should use that chapter's module. You can also +select **chapter23 field write** to debug +`PrefixUpdateTest.testMethodFieldWrites`, or **chapterXX tests** to run that +chapter's suite. These shared configurations set the module, working directory, +assertions and build step explicitly. Suite discovery is limited to that module. + +Old personal run configurations may still name the former combined chapter25. +Recreate those from the intended chapter's test gutter, or change both their +module and working directory in **Run > Edit Configurations**. The same Java +class names occur in many chapters, so a class name alone cannot identify the +intended snapshot. + +Tests that compile native code still need the C toolchain on IntelliJ's PATH. +Tests using Chapter 28b's native runtime or saved `sys` library also need the +Make prerequisites: run `make -C chapter28b release CTAGS=` before debugging +those tests. IntelliJ's Java build does not build the native runtime or library. + +For the JUnit working directory and module classpath settings, see +[JetBrains' JUnit configuration reference](https://www.jetbrains.com/help/idea/run-debug-configuration-junit.html). diff --git a/docs/stage-audit.md b/docs/stage-audit.md index c375d159..dac2dc77 100644 --- a/docs/stage-audit.md +++ b/docs/stage-audit.md @@ -22,13 +22,39 @@ regression identities; they do not identify current chapter boundaries. and the former roadmap is now [docs/roadmap.md](roadmap.md). - The recursive-return diagnostic already passed. It remains enabled in the earlier snapshots and is enabled again in 28a/28b. The mutual-recursion test had an obsolete graph-string - expectation; it now checks its existing four execution results in 24b–28b. + expectation; it now checks its existing four execution results in 24b–28b. In 24a, local SCCP cannot infer the recursive return type; the same example checks that conservative diagnostic instead. This makes the boundary explicit. - Allocation/runtime measurement runs passed after the comparison correction; the changed spill goldens record the resulting smaller graphs. Historical published tables retain their original compiler labels and dates. +## Prefix-update follow-up + +Prefix `++` and `--` now use the same name lookup as ordinary reads and postfix +updates in Chapters 23 through 28b. A method's field goes through memory; a +constructor-local field stays in SSA. Previously, the prefix path updated the +field declaration's parser slot and silently lost the write. The two-call +reproducer now returns 21 instead of 11. Narrow update results stay alive while +Store folding drops a redundant conversion from its own input. + +`PrefixUpdateTest` covers increment/decrement, integer/float/byte fields, +multiple worklist seeds, local shadowing, final-field errors, native byte +wraparound, and module fields where supported. Existing final-constructor +coverage includes prefix updates in loops. + +The focused Linux checks pass in all eight affected chapters, including native +SystemV byte-wraparound execution and the existing final-constructor tests. +The early final-field rejection is retained so an illegal prefix update never +enters the optimizer as a Store; native data bindings still update their writable +contents despite having a fixed address. + +The Windows full suites cover 4,169 tests plus eight fuzzer regression runs; +shared printer and ISA checks pass. Focused reruns cover the final diagnostic +and prefix-result lifetime adjustments. The full Chapter 27 suite was rerun +after restricting the temporary keep to prefix results; its existing spill +expectations remain unchanged. No allocator goldens were updated. + ## Implemented, with narrower limits than earlier prose suggested - Separate compilation imports types and optimized IR without source parsing; @@ -47,10 +73,9 @@ regression identities; they do not identify current chapter boundaries. ## Existing failures and deferred validation -- Prefix update of a field from a method can lose the write, starting in - Chapter 23 (confirmed by two calls to `inc={ -> ++x; }`). The enclosing - module-field reproducer and the worklist failure below are retained in the - [queue](chapter-backports.md), rather than hidden by the split. +- The ideal evaluator does not truncate narrow stores in Chapter 23. A byte + update can therefore disagree with generated code; the postfix-only + reproducer is retained in the [queue](chapter-backports.md). - A constructor loop with both break and continue still reproduces a worklist assertion in Chapter 26. It is not caused by final-field mutability. - `testModule0` is an ignored historical incremental-build experiment. It diff --git a/graph/graph.iml b/graph/graph.iml index 99b14f9d..385f08e5 100644 --- a/graph/graph.iml +++ b/graph/graph.iml @@ -8,6 +8,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/isa/isa.iml b/isa/isa.iml index 2319435e..c6be1236 100644 --- a/isa/isa.iml +++ b/isa/isa.iml @@ -7,12 +7,13 @@ <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> - <excludeFolder url="file://$MODULE_DIR$/src/test-support" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + <excludeFolder url="file://$MODULE_DIR$/src/test-support" /> </content> <orderEntry type="inheritedJdk" /> - <orderEntry type="module" module-name="isa-eval" scope="TEST" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module" module-name="isa-eval" scope="TEST" /> </component> </module> diff --git a/isa/src/test-support/isa-eval.iml b/isa/src/test-support/isa-eval.iml index 733c5919..e72a3543 100644 --- a/isa/src/test-support/isa-eval.iml +++ b/isa/src/test-support/isa-eval.iml @@ -2,9 +2,13 @@ <module type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="false"> <output url="file://$MODULE_DIR$/../../../build/idea/production/isa-eval" /> + <output-test url="file://$MODULE_DIR$/../../../build/idea/test/isa-eval" /> <exclude-output /> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$/java" isTestSource="false" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/print/print.iml b/print/print.iml index c6c0d587..1f622215 100644 --- a/print/print.iml +++ b/print/print.iml @@ -9,6 +9,7 @@ <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> <excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/skills/chapter25-codex-notes.md b/skills/chapter25-codex-notes.md index 22c832f6..0e5a1957 100644 --- a/skills/chapter25-codex-notes.md +++ b/skills/chapter25-codex-notes.md @@ -11,7 +11,7 @@ The [completed split](../docs/chapter-split.md) maps the former combined 24/25 to 24a/24b (SCCP), 25 (incomplete types), 26 (construction), 27 (escape), and 28a/28b (modules/separate compilation). The old combined snapshots are retired. Earlier entries below retain historical chapter numbers and test names; old -Chapter 25 implementation references describe the lineage now in 25–28b, with +Chapter 25 implementation references describe the lineage now in 25–28b, with the complete implementation in 28b. Use current paths and teaching boundaries. The construction/escape border remains open to semantic review. Future work belongs in the [roadmap](../docs/roadmap.md), including the separate @@ -65,6 +65,12 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m ## Mutability and chapter boundaries +- Prefix updates must share ordinary name resolution: a method's bare field + name denotes memory, while constructor fields may still be local SSA values. + Do not update the declaration's scope slot before resolving that distinction. + Keep the returned value alive across Store folding; a narrow Store can remove + a conversion it does not need, while the prefix expression still needs it. + - Separate compilation retains the Modules parser's constructor local values and escape fixes while adding saved IR and import ownership. Save owned nested struct class declarations even when graph types omit them: callers need the diff --git a/transpile-tests/transpile-tests.iml b/transpile-tests/transpile-tests.iml new file mode 100644 index 00000000..8744f519 --- /dev/null +++ b/transpile-tests/transpile-tests.iml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../build/idea/production/transpile-tests" /> + <output-test url="file://$MODULE_DIR$/../build/idea/test/transpile-tests" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/build" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + <excludeFolder url="file://$MODULE_DIR$/lib" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit4"> + <CLASSES> + <root url="jar://$MODULE_DIR$/../chapter01/lib/junit-4.12.jar!/" /> + <root url="jar://$MODULE_DIR$/../chapter01/lib/hamcrest-core-1.3.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + </component> +</module> |
