diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-22 15:47:50 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-22 15:47:50 -0700 |
| commit | deafd9a6fa34938c0c8c9589097e8c84e39bfa1b (patch) | |
| tree | db39ecbad856f986b84e229dbc4c023473892976 /chapter-construction/src/test/java/com/seaofnodes/simple/ConstructionTest.java | |
| parent | dbcaebdefff26658debc29cae284ad4aa0da4c65 (diff) | |
| download | Simple-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.java | 98 |
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)); + } +} |
