summaryrefslogtreecommitdiff
path: root/chapter28b
diff options
context:
space:
mode:
Diffstat (limited to 'chapter28b')
-rw-r--r--chapter28b/chapter28b.iml31
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/Parser.java37
-rw-r--r--chapter28b/src/test/java/com/seaofnodes/simple/PrefixUpdateTest.java58
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));
+ }
+}