diff options
Diffstat (limited to 'chapter28b')
| -rw-r--r-- | chapter28b/chapter28b.iml | 31 | ||||
| -rw-r--r-- | chapter28b/src/main/java/com/seaofnodes/simple/Parser.java | 37 | ||||
| -rw-r--r-- | chapter28b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java | 58 |
3 files changed, 108 insertions, 18 deletions
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)); + } +} |
