summaryrefslogtreecommitdiff
path: root/chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-22 15:47:50 -0700
committerCliff Click <cliffc@acm.org>2026-10-22 15:47:50 -0700
commitdeafd9a6fa34938c0c8c9589097e8c84e39bfa1b (patch)
treedb39ecbad856f986b84e229dbc4c023473892976 /chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java
parentdbcaebdefff26658debc29cae284ad4aa0da4c65 (diff)
downloadSimple-main.tar.gz
Simple-main.zip
Split out the constructor chaptermain
Diffstat (limited to 'chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java')
-rw-r--r--chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java98
1 files changed, 98 insertions, 0 deletions
diff --git a/chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java b/chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java
new file mode 100644
index 00000000..dd20b746
--- /dev/null
+++ b/chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java
@@ -0,0 +1,98 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.isa.eval.EvalArm64;
+import com.seaofnodes.isa.eval.EvalRisc5;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ConstructionTest {
+ private static final String CONSTRUCT = """
+ struct Box {
+ int x=7;
+ int[] data;
+ new Box = { int n ->
+ data=new int[2];
+ x=n;
+ data[0]=x+1;
+ };
+ val get = { -> x+data[0]; };
+ };
+ val a=new Box(arg);
+ val b=new Box(arg+10);
+ return a.get()*100+b.get();
+ """;
+
+ @Test public void testConstructorCalls() throws java.io.IOException {
+ var code=new CodeGen(CONSTRUCT).driver(CodeGen.Phase.FinalizeIR);
+ assertEquals("525",Eval2.eval(code,2));
+ for( String cpu : new String[]{"arm","riscv"} ) {
+ code=new CodeGen(CONSTRUCT).driver(cpu,"SystemV",null);
+ byte[] memory=new byte[1<<20];
+ byte[] bits=code._encoding.bits();
+ System.arraycopy(bits,0,memory,0,bits.length);
+ if( cpu.equals("arm") ) {
+ EvalArm64 eval=new EvalArm64(memory,1<<16);
+ eval.regs[0]=2;
+ assertEquals(0,eval.step(10000));
+ assertEquals(525,eval.regs[0]);
+ } else {
+ EvalRisc5 eval=new EvalRisc5(memory,1<<16);
+ eval.regs[10]=2;
+ assertEquals(0,eval.step(10000));
+ assertEquals(525,eval.regs[10]);
+ }
+ }
+ // Field updates are local SSA values, including the old x used after
+ // updating y and x. Only constructor exits should write these fields.
+ String counter="struct Counter { int x; int y; int z; new Counter={int n -> while(x<n) { int old=x; y=x+1; x=y+1; z=old; }}; }; val c=new Counter(arg); return c.x*100+c.z;";
+ var loop=new CodeGen(counter)
+ .driver(CodeGen.Phase.LocalSched);
+ assertEquals("0",Eval2.eval(loop,0));
+ assertEquals("2018",Eval2.eval(loop,19));
+ loop._stop.walk(n -> {
+ if( n instanceof com.seaofnodes.simple.node.StoreNode st )
+ assertEquals("Constructor Stores belong outside the loop",0,st.cfg0().loopDepth());
+ return null;
+ });
+ var defaults=new CodeGen("struct Buffer { int[] data=new int[1]; }; val a=new Buffer; val b=new Buffer; a.data[0]=7; return b.data[0];")
+ .driver(CodeGen.Phase.TypeCheck);
+ assertEquals("0",Eval2.eval(defaults,0));
+ java.nio.file.Path dir=java.nio.file.Path.of("build/objs");
+ java.nio.file.Files.createDirectories(dir);
+ java.nio.file.Files.writeString(dir.resolve("construction.smp"),"val construct={int arg ->"+CONSTRUCT+"}; val count={int arg ->"+counter+"};");
+ java.nio.file.Files.writeString(dir.resolve("construction.c"),"""
+ extern long long construct(long long);
+ extern long long count(long long);
+ int main(void) { return construct(2)!=525 || construct(0)!=121 || count(19)!=2018 || count(0)!=0; }
+ """);
+ TestC.run("build/objs","construction","",-1);
+ }
+
+ @Test public void testCompletionBeforePublication() {
+ String src="struct Box { int[] data; new Box={int n -> if(n) data=new int[2];}; }; return new Box(arg);";
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(); }
+ catch( Parser.ParseException e ) {
+ assertEquals("'Box' is not fully initialized, field 'data' needs to be set in a constructor",e.getMessage());
+ }
+ for( String early : new String[]{
+ "val use={!Box b -> b.x;}; struct Box { int x; new Box={->use(self);}; }; return new Box;",
+ "struct Box { Box? next; new Box={->next=self;}; }; return new Box;",
+ "struct Wrap { Box? item; }; struct Box { new Box={->new Wrap { item=self; };}; }; return new Box;"
+ } ) {
+ try { new CodeGen(early).driver(CodeGen.Phase.TypeCheck); fail(); }
+ catch( Parser.ParseException e ) {
+ assertEquals("Cannot publish self before the constructor finishes",e.getMessage());
+ }
+ }
+ }
+
+ @Test public void testForwardMethod() {
+ var code=new CodeGen("val run={!Box b -> b.get();}; struct Box { int x=7; val get={->x;}; }; return run(new Box);")
+ .driver(CodeGen.Phase.FinalizeIR);
+ assertEquals("7",Eval2.eval(code,0));
+ code=new CodeGen("struct Byte { new Byte={int n -> self.x=n; self.x+=3;}; u8 x; }; return new Byte(255).x;")
+ .driver(CodeGen.Phase.FinalizeIR);
+ assertEquals("2",Eval2.eval(code,0));
+ }
+}