Manipulable AST

View: New views
15 Messages — Rating Filter:   Alert me  

Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

On the path towards porting my current AST to Janino, I discovered the
need for StatementLists.

StatementLists provide a way to put a place holder statement
somewhere, and then fill it in later with only things that are needed.
 The big difference between this and a block is that it very
explicitly does not introduce a new scope, so that it can be used to
inject variables that will be visible.

This patch also adds `SimpleCompiler.cook(CompilationUnit)` which
allows the user to directly cook an AST that they have generated by
hand.

Both of these things are tested in the new `AstTests.java` that is
included in this test.

If you would like me to make any clean ups or sign any copyright
declarations before this work can be included in Janino, I would be
very happy to.

Enjoy,
Matt

[statementlist.patch]

==== Patch <statementlist> level 1
Source: 1adc4bfb-2c32-0410-81b9-bf0f0eac59bd:/janino-stmt-container:72685
Target: eb4544d6-894b-0410-9319-a9612837a279:/trunk/janino:332
        (http://svn.codehaus.org/janino)
Log:
 r72684@spiceweasel:  fowles | 2008-04-30 10:51:04 -0400
 creating a local branch for statement container work
 
 r72685@spiceweasel:  fowles | 2008-04-30 11:00:16 -0400
 Add support for StatementList and tests
 

=== tests/src/AstTests.java
==================================================================
--- tests/src/AstTests.java (revision 332)
+++ tests/src/AstTests.java (patch statementlist level 1)
@@ -0,0 +1,278 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.CompileException;
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Location;
+import org.codehaus.janino.Mod;
+import org.codehaus.janino.SimpleCompiler;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.BasicType;
+import org.codehaus.janino.Java.Block;
+import org.codehaus.janino.Java.CompilationUnit;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableDeclarationStatement;
+import org.codehaus.janino.Java.MethodDeclarator;
+import org.codehaus.janino.Java.PackageMemberClassDeclaration;
+import org.codehaus.janino.Java.ReturnStatement;
+import org.codehaus.janino.Java.StatementList;
+import org.codehaus.janino.Java.Type;
+import org.codehaus.janino.Java.FunctionDeclarator.FormalParameter;
+import org.codehaus.janino.Parser.ParseException;
+import org.codehaus.janino.Scanner.ScanException;
+
+public class AstTests extends TestCase {
+
+    public static interface Evalable {
+        int eval(int a);
+    }
+    
+    public static Test suite() {
+        TestSuite s = new TestSuite(AstTests.class.getName());
+        s.addTest(new AstTests("testSimpleAst"));
+        s.addTest(new AstTests("testLocalVariable"));
+        s.addTest(new AstTests("testBlock"));
+        s.addTest(new AstTests("testStatementList"));
+        return s;
+    }
+
+    private static Object compileAndEval(CompilationUnit cu) throws CompileException,
+            ParseException, ScanException, IOException, ClassNotFoundException,
+            InstantiationException, IllegalAccessException,
+            NoSuchMethodException, InvocationTargetException {
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        
+        Class handMadeClass = loader.loadClass("HandMade");
+        
+        Object handMade = handMadeClass.newInstance();
+        Method calc = handMadeClass.getMethod("calculate", null);
+        Object res = calc.invoke(handMade, null);
+        return res;
+    }
+    
+    private static PackageMemberClassDeclaration createClass(CompilationUnit cu)
+            throws ParseException {
+        PackageMemberClassDeclaration clazz = new PackageMemberClassDeclaration(
+                getLocation(),
+                null,
+                Mod.PUBLIC,
+                "HandMade",
+                null,
+                new Type[]{}
+        );
+        cu.addPackageMemberTypeDeclaration(clazz);
+        return clazz;
+    };
+    
+    private static Type createDoubleType() {
+        return new BasicType(getLocation(), BasicType.DOUBLE);
+    }
+    
+    private static Literal createLiteral(double d) {
+        return new Literal( getLocation(), Double.valueOf(d) );
+    }
+    
+    
+    private static void createMethod(PackageMemberClassDeclaration clazz, Block body) {
+        MethodDeclarator method = new MethodDeclarator(
+                getLocation(),
+                null,
+                (short)(Mod.PUBLIC),
+                createDoubleType(),
+                "calculate",
+                new FormalParameter[0],
+                new Type[0],
+                body
+        );
+        clazz.addDeclaredMethod(method);
+    }
+    
+    private static LocalVariableDeclarationStatement createVarDecl(String name, double value) {
+        return new Java.LocalVariableDeclarationStatement(
+                getLocation(),
+                (short)0,
+                createDoubleType(),
+                new Java.VariableDeclarator[] {
+                    new Java.VariableDeclarator(
+                            getLocation(),
+                            name,
+                            0,
+                            createLiteral(value)
+                    )
+                }
+        );
+    }
+        
+
+    private static AmbiguousName createVariableRef(String name) {
+        return new Java.AmbiguousName(
+                getLocation(),
+                new String[] { name }
+        );
+    }
+    
+    /**
+     * "Clever" method to get a location from a stack trace
+     */
+    static private Location getLocation() {
+        Exception e = new Exception();
+        StackTraceElement ste = e.getStackTrace()[1];//we only care about our caller
+        return new Location(
+                ste.getFileName(),
+                (short)ste.getLineNumber(),
+                (short)0
+        );
+    }
+
+    public AstTests(String name) { super(name); }
+
+    public void testSimpleAst() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        createLiteral(3.0)
+                )
+        );
+        
+        createMethod(clazz, body);
+        
+        Object res = compileAndEval(cu);
+        assertEquals(Double.valueOf(3.0), res);
+    }
+    
+    public void testLocalVariable() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement( createVarDecl("x", 2.0) );                        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body);
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(6.0), res);
+    }
+
+    public void testBlock() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        Block sub = new Block(getLocation());
+        sub.addStatement( createVarDecl("x", 2.0) );                        
+        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body);
+        
+        try {
+            compileAndEval(cu);
+            fail("Block must limit the scope of variables in it");
+        } catch(CompileException ex) {
+            assertTrue(ex.getMessage().endsWith("Expression \"x\" is not an rvalue"));
+        }
+    }
+
+    public void testStatementList() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        StatementList sub = new StatementList(getLocation());
+        sub.addStatement( createVarDecl("x", 3.0) );                        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body);
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(9.0), res);
+    }
+    
+}
=== tests/src/AllTests.java
==================================================================
--- tests/src/AllTests.java (revision 332)
+++ tests/src/AllTests.java (patch statementlist level 1)
@@ -82,5 +82,6 @@
         this.addTest(ReportedBugs.suite());
         this.addTest(SandboxTests.suite());
         this.addTest(EvaluatorTests.suite());
+        this.addTest(AstTests.suite());
     }
 }
=== src/org/codehaus/janino/UnitCompiler.java
==================================================================
--- src/org/codehaus/janino/UnitCompiler.java (revision 332)
+++ src/org/codehaus/janino/UnitCompiler.java (patch statementlist level 1)
@@ -5467,6 +5467,27 @@
         // 6.5.2.BL1.B1.B7 Package name
         return new Java.Package(location, identifier);
     }
+    
+    /**
+     * Check to see if a local variable was declared in this statement.  Possibly recursing into a StatementList, as needed.
+     * @param varName  The name of the variable to find
+     * @param stmt     The statement in question
+     * @return         A local variable definition if found.  null if not found
+     * @throws CompileException
+     */
+    private Java.LocalVariable findLocalVariableInStatement(Java.BlockStatement stmt, String varName) throws CompileException {
+        if (stmt instanceof Java.LocalVariableDeclarationStatement) {
+            return this.findLocalVariable((Java.LocalVariableDeclarationStatement)stmt, varName);
+        }
+        if (stmt instanceof Java.StatementList) {
+            Java.StatementList sl = (Java.StatementList)stmt;
+            for (Iterator it = sl.statements.iterator();;) {
+                Java.LocalVariable lv = findLocalVariableInStatement((Java.BlockStatement)it.next(), varName);
+                if (lv != null) return lv;
+            }
+        }
+        return null;
+    }
 
     /**
      * Find a local variable declared by the given <code>blockStatement</code> or any enclosing
@@ -5481,19 +5502,15 @@
             {
                 if (s instanceof Java.ForStatement) {
                     Java.BlockStatement optionalForInit = ((Java.ForStatement) s).optionalInit;
-                    if (optionalForInit instanceof Java.LocalVariableDeclarationStatement) {
-                        Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) optionalForInit, name);
-                        if (lv != null) return lv;
-                    }
+                    Java.LocalVariable lv = this.findLocalVariableInStatement(optionalForInit, name);
+                    if (lv != null) return lv;
                 }
                 if (es instanceof Java.Block) {
                     Java.Block b = (Java.Block) es;
                     for (Iterator it = b.statements.iterator();;) {
                         Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                        if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                            Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
-                            if (lv != null) return lv;
-                        }
+                        Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
+                        if (lv != null) return lv;
                         if (bs2 == s) break;
                     }
                 }
@@ -5503,10 +5520,8 @@
                         Java.SwitchStatement.SwitchBlockStatementGroup sbgs = (Java.SwitchStatement.SwitchBlockStatementGroup) it2.next();
                         for (Iterator it = sbgs.blockStatements.iterator(); it.hasNext();) {
                             Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                            if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                                Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
-                                if (lv != null) return lv;
-                            }
+                            Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
+                            if (lv != null) return lv;
                             if (bs2 == s) break SBSGS;
                         }
                     }
=== src/org/codehaus/janino/Java.java
==================================================================
--- src/org/codehaus/janino/Java.java (revision 332)
+++ src/org/codehaus/janino/Java.java (patch statementlist level 1)
@@ -1230,7 +1230,56 @@
 
         public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitBlock(this); }
     }
+    
+    /**
+     * This is similar to a {@link Java.Block} except that it does not create a scope around it statements.
+     * It is useful for programmatically manipulating an AST
+     */
+    public final static class StatementList extends Statement {
+        public final List statements = new ArrayList(); // BlockStatement
 
+        public StatementList(Location location) {
+            super(location);
+        }
+
+        public void addStatement(BlockStatement statement) {
+            this.statements.add(statement);
+            statement.setEnclosingScope(this.getEnclosingScope());
+        }
+        
+        public void addStatements(
+            List statements // BlockStatement
+        ) {
+            Iterator stmtIter = statements.iterator();
+            while(stmtIter.hasNext()) {
+                addStatement((BlockStatement)stmtIter.next());
+            }
+        }
+        
+        public BlockStatement[] getStatements() {
+            return (BlockStatement[]) this.statements.toArray(new BlockStatement[this.statements.size()]);
+        }
+        
+        // set children to share this object's enclosing scope
+        public void setEnclosingScope(Scope enclosingScope) {
+            super.setEnclosingScope(enclosingScope);
+            Iterator stmtIter = this.statements.iterator();
+            while(stmtIter.hasNext()) {
+                BlockStatement stmt = (BlockStatement)stmtIter.next();
+                stmt.setEnclosingScope(enclosingScope);
+            }
+        }
+
+        // Compile time members.
+        public final void accept(Visitor.BlockStatementVisitor visitor) {
+            Iterator stmtIter = this.statements.iterator();
+            while(stmtIter.hasNext()) {
+                BlockStatement stmt = (BlockStatement)stmtIter.next();
+                stmt.accept(visitor);
+            }
+        }
+    }
+
     /**
      * Base class for statements that can be terminated abnormally with a
      * "break" statement.
=== src/org/codehaus/janino/SimpleCompiler.java
==================================================================
--- src/org/codehaus/janino/SimpleCompiler.java (revision 332)
+++ src/org/codehaus/janino/SimpleCompiler.java (patch statementlist level 1)
@@ -201,7 +201,7 @@
     public static final ClassLoader BOOT_CLASS_LOADER = new ClassLoader(null) {};
 
     /**
-     * Allowe references to the classes loaded through this parent class loader
+     * Allow references to the classes loaded through this parent class loader
      * (@see {@link #setParentClassLoader(ClassLoader)}), plus the extra
      * <code>auxiliaryClasses</code>.
      * <p>
@@ -233,7 +233,22 @@
             DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
         );
     }
+    
+    /**
+     * Cook this compilation unit directly.
+     *  See {@link Cookable.cook}
+     */
+    public void cook(Java.CompilationUnit compilationUnit)
+    throws CompileException, Parser.ParseException, Scanner.ScanException, IOException {
+        this.setUpClassLoaders();
 
+        // Compile the classes and load them.
+        this.compileToClassLoader(
+            compilationUnit,
+            DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
+        );
+    }
+
     /**
      * Initializes {@link #classLoader} and {@link #iClassLoader} from the configured
      * {@link #parentClassLoader} and {@link #optionalAuxiliaryClasses}. These are needed by

==== BEGIN SVK PATCH BLOCK ====
Version: svk v2.0.2 (linux)

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
Zg==
==== END SVK PATCH BLOCK ====


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email

Re: Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

The attached patch supersedes my last one and also include a test and
fix for the statement

return new byte[]{ (byte)1 };

which fails in `UnitCompiler.getType2` because of a missing case.

Once again, any pointers on things I can do to speed the inclusion of
patches into Janino would be greatly appreciated.

Matt

On Wed, Apr 30, 2008 at 11:13 AM, Matt Fowles <matt.fowles@...> wrote:

> All~
>
>  On the path towards porting my current AST to Janino, I discovered the
>  need for StatementLists.
>
>  StatementLists provide a way to put a place holder statement
>  somewhere, and then fill it in later with only things that are needed.
>   The big difference between this and a block is that it very
>  explicitly does not introduce a new scope, so that it can be used to
>  inject variables that will be visible.
>
>  This patch also adds `SimpleCompiler.cook(CompilationUnit)` which
>  allows the user to directly cook an AST that they have generated by
>  hand.
>
>  Both of these things are tested in the new `AstTests.java` that is
>  included in this test.
>
>  If you would like me to make any clean ups or sign any copyright
>  declarations before this work can be included in Janino, I would be
>  very happy to.
>
>  Enjoy,
>  Matt
>

[statementlist.patch]

==== Patch <statementlist> level 2
Source: 1adc4bfb-2c32-0410-81b9-bf0f0eac59bd:/janino-stmt-container:72835
Target: eb4544d6-894b-0410-9319-a9612837a279:/trunk/janino:332
        (http://svn.codehaus.org/janino)
Log:
 r72684@spiceweasel:  fowles | 2008-04-30 10:51:04 -0400
 creating a local branch for statement container work
 
 r72685@spiceweasel:  fowles | 2008-04-30 11:00:16 -0400
 Add support for StatementList and tests
 
 r72834@spiceweasel:  fowles | 2008-05-01 17:23:02 -0400
 comment out assertions since they are not valid in Java 1.2
 
 r72835@spiceweasel:  fowles | 2008-05-01 17:23:27 -0400
 Fix byte[] literals for ArrayInitiliazations
 

=== tests/src/AstTests.java
==================================================================
--- tests/src/AstTests.java (revision 332)
+++ tests/src/AstTests.java (patch statementlist level 2)
@@ -0,0 +1,323 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.CompileException;
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Location;
+import org.codehaus.janino.Mod;
+import org.codehaus.janino.SimpleCompiler;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayType;
+import org.codehaus.janino.Java.BasicType;
+import org.codehaus.janino.Java.Block;
+import org.codehaus.janino.Java.CompilationUnit;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableDeclarationStatement;
+import org.codehaus.janino.Java.MethodDeclarator;
+import org.codehaus.janino.Java.PackageMemberClassDeclaration;
+import org.codehaus.janino.Java.ReturnStatement;
+import org.codehaus.janino.Java.StatementList;
+import org.codehaus.janino.Java.Type;
+import org.codehaus.janino.Java.FunctionDeclarator.FormalParameter;
+import org.codehaus.janino.Parser.ParseException;
+import org.codehaus.janino.Scanner.ScanException;
+
+public class AstTests extends TestCase {
+
+    public static interface Evalable {
+        int eval(int a);
+    }
+    
+    public static Test suite() {
+        TestSuite s = new TestSuite(AstTests.class.getName());
+        s.addTest(new AstTests("testSimpleAst"));
+        s.addTest(new AstTests("testLocalVariable"));
+        s.addTest(new AstTests("testBlock"));
+        s.addTest(new AstTests("testStatementList"));
+        s.addTest(new AstTests("testByteArrayLiteral"));
+        return s;
+    }
+
+    private static Object compileAndEval(CompilationUnit cu) throws CompileException,
+            ParseException, ScanException, IOException, ClassNotFoundException,
+            InstantiationException, IllegalAccessException,
+            NoSuchMethodException, InvocationTargetException {
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        
+        Class handMadeClass = loader.loadClass("HandMade");
+        
+        Object handMade = handMadeClass.newInstance();
+        Method calc = handMadeClass.getMethod("calculate", null);
+        Object res = calc.invoke(handMade, null);
+        return res;
+    }
+    
+    private static ArrayType createByteArrayType() {
+        return new Java.ArrayType(
+                new Java.BasicType(
+                        getLocation(),
+                        Java.BasicType.BYTE
+                )
+        );
+    };
+    
+    private static PackageMemberClassDeclaration createClass(CompilationUnit cu)
+            throws ParseException {
+        PackageMemberClassDeclaration clazz = new PackageMemberClassDeclaration(
+                getLocation(),
+                null,
+                Mod.PUBLIC,
+                "HandMade",
+                null,
+                new Type[]{}
+        );
+        cu.addPackageMemberTypeDeclaration(clazz);
+        return clazz;
+    }
+    
+    private static Type createDoubleType() {
+        return new BasicType(getLocation(), BasicType.DOUBLE);
+    }
+    
+    private static Literal createLiteral(double d) {
+        return createLiteral(Double.valueOf(d));
+    }
+    
+    
+    private static Literal createLiteral(Object o) {
+        return new Literal( getLocation(), o );
+    }
+    
+    private static void createMethod(PackageMemberClassDeclaration clazz, Block body, Type returnType) {
+        MethodDeclarator method = new MethodDeclarator(
+                getLocation(),
+                null,
+                (short)(Mod.PUBLIC),
+                returnType,
+                "calculate",
+                new FormalParameter[0],
+                new Type[0],
+                body
+        );
+        clazz.addDeclaredMethod(method);
+    }
+        
+
+    private static LocalVariableDeclarationStatement createVarDecl(String name, double value) {
+        return new Java.LocalVariableDeclarationStatement(
+                getLocation(),
+                (short)0,
+                createDoubleType(),
+                new Java.VariableDeclarator[] {
+                    new Java.VariableDeclarator(
+                            getLocation(),
+                            name,
+                            0,
+                            createLiteral(value)
+                    )
+                }
+        );
+    }
+    
+    private static AmbiguousName createVariableRef(String name) {
+        return new Java.AmbiguousName(
+                getLocation(),
+                new String[] { name }
+        );
+    }
+
+    /**
+     * "Clever" method to get a location from a stack trace
+     */
+    static private Location getLocation() {
+        Exception e = new Exception();
+        StackTraceElement ste = e.getStackTrace()[1];//we only care about our caller
+        return new Location(
+                ste.getFileName(),
+                (short)ste.getLineNumber(),
+                (short)0
+        );
+    }
+
+    public AstTests(String name) { super(name); }
+    
+    public void testBlock() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        Block sub = new Block(getLocation());
+        sub.addStatement( createVarDecl("x", 2.0) );                        
+        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        try {
+            compileAndEval(cu);
+            fail("Block must limit the scope of variables in it");
+        } catch(CompileException ex) {
+            assertTrue(ex.getMessage().endsWith("Expression \"x\" is not an rvalue"));
+        }
+    }
+
+    public void testByteArrayLiteral() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Byte exp = Byte.valueOf((byte)1);
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.NewInitializedArray(
+                                getLocation(),
+                                createByteArrayType(),
+                                new Java.ArrayInitializer(
+                                        getLocation(),
+                                        new Java.Rvalue[] {
+                                            createLiteral(exp)
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                createByteArrayType()
+        );
+        
+        Object res = compileAndEval(cu);
+        assertEquals(exp.byteValue(), ((byte[])res)[0]);
+    }
+
+    public void testLocalVariable() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement( createVarDecl("x", 2.0) );                        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(6.0), res);
+    }
+    
+    public void testSimpleAst() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        createLiteral(3.0)
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertEquals(Double.valueOf(3.0), res);
+    }
+
+    public void testStatementList() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        StatementList sub = new StatementList(getLocation());
+        sub.addStatement( createVarDecl("x", 3.0) );                        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(9.0), res);
+    }
+}
=== tests/src/AllTests.java
==================================================================
--- tests/src/AllTests.java (revision 332)
+++ tests/src/AllTests.java (patch statementlist level 2)
@@ -82,5 +82,6 @@
         this.addTest(ReportedBugs.suite());
         this.addTest(SandboxTests.suite());
         this.addTest(EvaluatorTests.suite());
+        this.addTest(AstTests.suite());
     }
 }
=== src/org/codehaus/janino/UnitCompiler.java
==================================================================
--- src/org/codehaus/janino/UnitCompiler.java (revision 332)
+++ src/org/codehaus/janino/UnitCompiler.java (patch statementlist level 2)
@@ -4150,6 +4150,7 @@
         return this.getType(nia.arrayType);
     }
     private IClass getType2(Java.Literal l) {
+        if (l.value instanceof Byte     ) return IClass.BYTE;
         if (l.value instanceof Integer  ) return IClass.INT;
         if (l.value instanceof Long     ) return IClass.LONG;
         if (l.value instanceof Float    ) return IClass.FLOAT;
@@ -4162,6 +4163,7 @@
     }
     private IClass getType2(Java.ConstantValue cv) {
         IClass res = (
+            cv.constantValue instanceof Byte               ? IClass.BYTE    :
             cv.constantValue instanceof Integer            ? IClass.INT     :
             cv.constantValue instanceof Long               ? IClass.LONG    :
             cv.constantValue instanceof Float              ? IClass.FLOAT   :
@@ -5467,6 +5469,27 @@
         // 6.5.2.BL1.B1.B7 Package name
         return new Java.Package(location, identifier);
     }
+    
+    /**
+     * Check to see if a local variable was declared in this statement.  Possibly recursing into a StatementList, as needed.
+     * @param varName  The name of the variable to find
+     * @param stmt     The statement in question
+     * @return         A local variable definition if found.  null if not found
+     * @throws CompileException
+     */
+    private Java.LocalVariable findLocalVariableInStatement(Java.BlockStatement stmt, String varName) throws CompileException {
+        if (stmt instanceof Java.LocalVariableDeclarationStatement) {
+            return this.findLocalVariable((Java.LocalVariableDeclarationStatement)stmt, varName);
+        }
+        if (stmt instanceof Java.StatementList) {
+            Java.StatementList sl = (Java.StatementList)stmt;
+            for (Iterator it = sl.statements.iterator();;) {
+                Java.LocalVariable lv = findLocalVariableInStatement((Java.BlockStatement)it.next(), varName);
+                if (lv != null) return lv;
+            }
+        }
+        return null;
+    }
 
     /**
      * Find a local variable declared by the given <code>blockStatement</code> or any enclosing
@@ -5481,19 +5504,15 @@
             {
                 if (s instanceof Java.ForStatement) {
                     Java.BlockStatement optionalForInit = ((Java.ForStatement) s).optionalInit;
-                    if (optionalForInit instanceof Java.LocalVariableDeclarationStatement) {
-                        Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) optionalForInit, name);
-                        if (lv != null) return lv;
-                    }
+                    Java.LocalVariable lv = this.findLocalVariableInStatement(optionalForInit, name);
+                    if (lv != null) return lv;
                 }
                 if (es instanceof Java.Block) {
                     Java.Block b = (Java.Block) es;
                     for (Iterator it = b.statements.iterator();;) {
                         Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                        if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                            Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
-                            if (lv != null) return lv;
-                        }
+                        Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
+                        if (lv != null) return lv;
                         if (bs2 == s) break;
                     }
                 }
@@ -5503,10 +5522,8 @@
                         Java.SwitchStatement.SwitchBlockStatementGroup sbgs = (Java.SwitchStatement.SwitchBlockStatementGroup) it2.next();
                         for (Iterator it = sbgs.blockStatements.iterator(); it.hasNext();) {
                             Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                            if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                                Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
-                                if (lv != null) return lv;
-                            }
+                            Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
+                            if (lv != null) return lv;
                             if (bs2 == s) break SBSGS;
                         }
                     }
=== src/org/codehaus/janino/Java.java
==================================================================
--- src/org/codehaus/janino/Java.java (revision 332)
+++ src/org/codehaus/janino/Java.java (patch statementlist level 2)
@@ -77,6 +77,7 @@
         private final Location location;
 
         protected Located(Location location) {
+            //assert location != null;
             this.location = location;
         }
 
@@ -1230,7 +1231,56 @@
 
         public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitBlock(this); }
     }
+    
+    /**
+     * This is similar to a {@link Java.Block} except that it does not create a scope around it statements.
+     * It is useful for programmatically manipulating an AST
+     */
+    public final static class StatementList extends Statement {
+        public final List statements = new ArrayList(); // BlockStatement
 
+        public StatementList(Location location) {
+            super(location);
+        }
+
+        public void addStatement(BlockStatement statement) {
+            this.statements.add(statement);
+            statement.setEnclosingScope(this.getEnclosingScope());
+        }
+        
+        public void addStatements(
+            List statements // BlockStatement
+        ) {
+            Iterator stmtIter = statements.iterator();
+            while(stmtIter.hasNext()) {
+                addStatement((BlockStatement)stmtIter.next());
+            }
+        }
+        
+        public BlockStatement[] getStatements() {
+            return (BlockStatement[]) this.statements.toArray(new BlockStatement[this.statements.size()]);
+        }
+        
+        // set children to share this object's enclosing scope
+        public void setEnclosingScope(Scope enclosingScope) {
+            super.setEnclosingScope(enclosingScope);
+            Iterator stmtIter = this.statements.iterator();
+            while(stmtIter.hasNext()) {
+                BlockStatement stmt = (BlockStatement)stmtIter.next();
+                stmt.setEnclosingScope(enclosingScope);
+            }
+        }
+
+        // Compile time members.
+        public final void accept(Visitor.BlockStatementVisitor visitor) {
+            Iterator stmtIter = this.statements.iterator();
+            while(stmtIter.hasNext()) {
+                BlockStatement stmt = (BlockStatement)stmtIter.next();
+                stmt.accept(visitor);
+            }
+        }
+    }
+
     /**
      * Base class for statements that can be terminated abnormally with a
      * "break" statement.
=== src/org/codehaus/janino/SimpleCompiler.java
==================================================================
--- src/org/codehaus/janino/SimpleCompiler.java (revision 332)
+++ src/org/codehaus/janino/SimpleCompiler.java (patch statementlist level 2)
@@ -201,7 +201,7 @@
     public static final ClassLoader BOOT_CLASS_LOADER = new ClassLoader(null) {};
 
     /**
-     * Allowe references to the classes loaded through this parent class loader
+     * Allow references to the classes loaded through this parent class loader
      * (@see {@link #setParentClassLoader(ClassLoader)}), plus the extra
      * <code>auxiliaryClasses</code>.
      * <p>
@@ -233,7 +233,22 @@
             DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
         );
     }
+    
+    /**
+     * Cook this compilation unit directly.
+     *  See {@link Cookable.cook}
+     */
+    public void cook(Java.CompilationUnit compilationUnit)
+    throws CompileException, Parser.ParseException, Scanner.ScanException, IOException {
+        this.setUpClassLoaders();
 
+        // Compile the classes and load them.
+        this.compileToClassLoader(
+            compilationUnit,
+            DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
+        );
+    }
+
     /**
      * Initializes {@link #classLoader} and {@link #iClassLoader} from the configured
      * {@link #parentClassLoader} and {@link #optionalAuxiliaryClasses}. These are needed by

==== BEGIN SVK PATCH BLOCK ====
Version: svk v2.0.2 (linux)

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
==== END SVK PATCH BLOCK ====


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email

Re: Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

Please ignore my current patch.  Further testing has revealed some
serious bugs with it.  I am working on a more complete version as we
speak.

Thanks,
Matt

On Thu, May 1, 2008 at 5:47 PM, Matt Fowles <matt.fowles@...> wrote:

> All~
>
>  The attached patch supersedes my last one and also include a test and
>  fix for the statement
>
>  return new byte[]{ (byte)1 };
>
>  which fails in `UnitCompiler.getType2` because of a missing case.
>
>  Once again, any pointers on things I can do to speed the inclusion of
>  patches into Janino would be greatly appreciated.
>
>  Matt
>
>
>
>  On Wed, Apr 30, 2008 at 11:13 AM, Matt Fowles <matt.fowles@...> wrote:
>  > All~
>  >
>  >  On the path towards porting my current AST to Janino, I discovered the
>  >  need for StatementLists.
>  >
>  >  StatementLists provide a way to put a place holder statement
>  >  somewhere, and then fill it in later with only things that are needed.
>  >   The big difference between this and a block is that it very
>  >  explicitly does not introduce a new scope, so that it can be used to
>  >  inject variables that will be visible.
>  >
>  >  This patch also adds `SimpleCompiler.cook(CompilationUnit)` which
>  >  allows the user to directly cook an AST that they have generated by
>  >  hand.
>  >
>  >  Both of these things are tested in the new `AstTests.java` that is
>  >  included in this test.
>  >
>  >  If you would like me to make any clean ups or sign any copyright
>  >  declarations before this work can be included in Janino, I would be
>  >  very happy to.
>  >
>  >  Enjoy,
>  >  Matt
>  >
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

I am making very good progress targeting Janino's AST from my current
compiler.  The attached patch contains the total set of things I have
needed to add or fix so far.

Included in this patch is:

-  UnparseVisitor fix for precedence
  - a separate patch for just that bug is attached to the ticket
  - http://jira.codehaus.org/browse/JANINO-111

- Exposed the raw class data from SimpleCompiler
  - this allows my program to output .class or .jar files
  - tests for the raw class data

- improved support for Byte literals

- added class Java.StatementList
  - this allows code to be generated dynamically into an existing AST
(very necessary for my compiler)
  - update as necessary all the visitors for Java.StatementList
  - basic tests for Java.StatementList

- improved comments for Java.NewArray

Once again, any pointers on things I can do to speed the inclusion of
patches into Janino or to improve the patch in general are greatly appreciated.

Matt

On Tue, May 6, 2008 at 2:20 PM, Matt Fowles <matt.fowles@...> wrote:

> All~
>
>  Please ignore my current patch.  Further testing has revealed some
>  serious bugs with it.  I am working on a more complete version as we
>  speak.
>
>  Thanks,
>  Matt
>
>
>
>  On Thu, May 1, 2008 at 5:47 PM, Matt Fowles <matt.fowles@...> wrote:
>  > All~
>  >
>  >  The attached patch supersedes my last one and also include a test and
>  >  fix for the statement
>  >
>  >  return new byte[]{ (byte)1 };
>  >
>  >  which fails in `UnitCompiler.getType2` because of a missing case.
>  >
>  >  Once again, any pointers on things I can do to speed the inclusion of
>  >  patches into Janino would be greatly appreciated.
>  >
>  >  Matt
>  >
>  >
>  >
>  >  On Wed, Apr 30, 2008 at 11:13 AM, Matt Fowles <matt.fowles@...> wrote:
>  >  > All~
>  >  >
>  >  >  On the path towards porting my current AST to Janino, I discovered the
>  >  >  need for StatementLists.
>  >  >
>  >  >  StatementLists provide a way to put a place holder statement
>  >  >  somewhere, and then fill it in later with only things that are needed.
>  >  >   The big difference between this and a block is that it very
>  >  >  explicitly does not introduce a new scope, so that it can be used to
>  >  >  inject variables that will be visible.
>  >  >
>  >  >  This patch also adds `SimpleCompiler.cook(CompilationUnit)` which
>  >  >  allows the user to directly cook an AST that they have generated by
>  >  >  hand.
>  >  >
>  >  >  Both of these things are tested in the new `AstTests.java` that is
>  >  >  included in this test.
>  >  >
>  >  >  If you would like me to make any clean ups or sign any copyright
>  >  >  declarations before this work can be included in Janino, I would be
>  >  >  very happy to.
>  >  >
>  >  >  Enjoy,
>  >  >  Matt
>  >  >
>  >
>

[statementlist.patch]

==== Patch <statementlist> level 1
Source: 1adc4bfb-2c32-0410-81b9-bf0f0eac59bd:/janino-stmt-container:73185
Target: eb4544d6-894b-0410-9319-a9612837a279:/trunk/janino:338
        (http://svn.codehaus.org/janino)
Log:
 r72684@spiceweasel:  fowles | 2008-04-30 10:51:04 -0400
 creating a local branch for statement container work
 
 r72685@spiceweasel:  fowles | 2008-04-30 11:00:16 -0400
 Add support for StatementList and tests
 
 r72834@spiceweasel:  fowles | 2008-05-01 17:23:02 -0400
 comment out assertions since they are not valid in Java 1.2
 
 r72835@spiceweasel:  fowles | 2008-05-01 17:23:27 -0400
 Fix byte[] literals for ArrayInitiliazations
 
 r72849@spiceweasel:  fowles | 2008-05-01 22:49:37 -0400
 switch location null check to an IllegalArgumentException
 
 r72851@spiceweasel:  fowles | 2008-05-01 23:37:27 -0400
 Add a useful test
 
 r72852@spiceweasel:  fowles | 2008-05-01 23:45:48 -0400
 more tests
 
 r72853@spiceweasel:  fowles | 2008-05-02 00:17:46 -0400
 apparently some janino tests use null locations
 
 r72854@spiceweasel:  fowles | 2008-05-02 00:17:57 -0400
 found a way to make the test fail!
 
 r72915@spiceweasel:  fowles | 2008-05-02 16:00:07 -0400
 refactor a bunch of duplicate code
 
 r72916@spiceweasel:  fowles | 2008-05-02 16:25:08 -0400
 fix the problem with fully qualified names in this compilation unit
 
 r72941@spiceweasel:  fowles | 2008-05-02 16:37:33 -0400
 remove some debugging code
 
 r72942@spiceweasel:  fowles | 2008-05-02 23:55:16 -0400
 make test go three levels deep and clean up style
 
 r73062@spiceweasel:  fowles | 2008-05-05 17:23:33 -0400
 Fix a few small bugs with StatementLists
 
 r73063@spiceweasel:  fowles | 2008-05-05 17:35:48 -0400
 Fix a missing case for Byte literal
 
 r73064@spiceweasel:  fowles | 2008-05-05 17:35:58 -0400
 fix an indent
 
 r73065@spiceweasel:  fowles | 2008-05-05 17:38:47 -0400
 manually set the line encoding
 
 r73094@spiceweasel:  fowles | 2008-05-06 13:12:03 -0400
 Expose access to the underlying class data and write a test for it
 
 r73095@spiceweasel:  fowles | 2008-05-06 14:21:15 -0400
 make StatementList a more first class citizen and teach everyone how to deal with it.
 
 r73121@spiceweasel:  fowles | 2008-05-06 15:47:11 -0400
 Handle an unexpected null case more gracefully
 
 r73130@spiceweasel:  fowles | 2008-05-06 16:34:56 -0400
 add some javadoc
 
 r73135@spiceweasel:  fowles | 2008-05-06 18:28:17 -0400
 add suport for Operator precedence to UnparseVisitor and a basic test for it
 
 r73136@spiceweasel:  fowles | 2008-05-07 14:12:28 -0400
 add some tests for unparse and precedence
 fix a few bugs they found
 
 r73183@spiceweasel:  fowles | 2008-05-07 14:42:42 -0400
 remove a line ending change I made
 
 r73184@spiceweasel:  fowles | 2008-05-07 14:46:21 -0400
 somehow I messed up the line endings on this file
 
 r73185@spiceweasel:  fowles | 2008-05-07 14:50:45 -0400
 Fix some indenting I screwed up
 

=== tests/src/UnparseTests.java
==================================================================
--- tests/src/UnparseTests.java (revision 338)
+++ tests/src/UnparseTests.java (patch statementlist level 1)
@@ -0,0 +1,332 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Parser;
+import org.codehaus.janino.Scanner;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Visitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayAccessExpression;
+import org.codehaus.janino.Java.ArrayLength;
+import org.codehaus.janino.Java.Assignment;
+import org.codehaus.janino.Java.Atom;
+import org.codehaus.janino.Java.BinaryOperation;
+import org.codehaus.janino.Java.Cast;
+import org.codehaus.janino.Java.ClassLiteral;
+import org.codehaus.janino.Java.ConditionalExpression;
+import org.codehaus.janino.Java.ConstantValue;
+import org.codehaus.janino.Java.Crement;
+import org.codehaus.janino.Java.FieldAccess;
+import org.codehaus.janino.Java.FieldAccessExpression;
+import org.codehaus.janino.Java.Instanceof;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableAccess;
+import org.codehaus.janino.Java.MethodInvocation;
+import org.codehaus.janino.Java.NewAnonymousClassInstance;
+import org.codehaus.janino.Java.NewArray;
+import org.codehaus.janino.Java.NewClassInstance;
+import org.codehaus.janino.Java.NewInitializedArray;
+import org.codehaus.janino.Java.ParameterAccess;
+import org.codehaus.janino.Java.ParenthesizedExpression;
+import org.codehaus.janino.Java.QualifiedThisReference;
+import org.codehaus.janino.Java.SuperclassFieldAccessExpression;
+import org.codehaus.janino.Java.SuperclassMethodInvocation;
+import org.codehaus.janino.Java.ThisReference;
+import org.codehaus.janino.Java.UnaryOperation;
+
+public class UnparseTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(UnparseTests.class.getName());
+        s.addTest(new UnparseTests("testSimple"));
+        s.addTest(new UnparseTests("testParens"));
+        s.addTest(new UnparseTests("testMany"));
+        return s;
+    }
+
+    public UnparseTests(String name) { super(name); }
+    
+    private static void helpTestExpr(String input, String expect, boolean simplify) throws Exception {
+        Parser p = new Parser(new Scanner(
+                null, new ByteArrayInputStream(input.getBytes()
+        )));
+        Atom expr = p.parseExpression();
+        if(simplify) {
+            expr = stripUnnecessaryParenExprs(expr);
+        }
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        expr.accept(uv);
+        assertEquals(expect, sw.toString());    
+    }
+    
+    private static Java.Rvalue[] stripUnnecessaryParenExprs(Java.Rvalue[] rvalues) {
+        Java.Rvalue[] res = new Java.Rvalue[rvalues.length];
+        for(int i = 0; i < res.length; ++i) {
+            res[i] = stripUnnecessaryParenExprs(rvalues[i]);
+        }
+        return res;
+    }
+    
+    private static Java.Atom stripUnnecessaryParenExprs(Java.Atom atom) {
+        if(atom instanceof Java.Rvalue) {
+            return stripUnnecessaryParenExprs((Java.Rvalue)atom);
+        }
+        return atom;
+    }
+    
+    private static Java.Lvalue stripUnnecessaryParenExprs(Java.Lvalue lvalue) {
+        return (Java.Lvalue)stripUnnecessaryParenExprs((Java.Rvalue)lvalue);
+    }
+    
+    private static Java.Rvalue stripUnnecessaryParenExprs(Java.Rvalue rvalue) {
+        if(rvalue == null) { return null; }
+        final Java.Rvalue[] res = new Java.Rvalue[1];
+        Visitor.RvalueVisitor rv = new Visitor.RvalueVisitor() {
+            public void visitArrayLength(ArrayLength al) {
+                res[0] = new Java.ArrayLength(al.getLocation(),
+                        stripUnnecessaryParenExprs(al.lhs));
+            }
+
+            public void visitAssignment(Assignment a) {
+                res[0] = new Java.Assignment(a.getLocation(),
+                        stripUnnecessaryParenExprs(a.lhs),
+                        a.operator,
+                        stripUnnecessaryParenExprs(a.rhs)
+                );
+            }
+
+            public void visitBinaryOperation(BinaryOperation bo) {
+                res[0] = new Java.BinaryOperation(bo.getLocation(),
+                        stripUnnecessaryParenExprs(bo.lhs),
+                        bo.op,
+                        stripUnnecessaryParenExprs(bo.rhs)
+                );
+            }
+
+            public void visitCast(Cast c) {
+                res[0] = new Java.Cast(c.getLocation(),
+                        c.targetType,
+                        stripUnnecessaryParenExprs(c.value));
+            }
+
+            public void visitClassLiteral(ClassLiteral cl) {
+                res[0] = cl; //too much effort
+            }
+
+            public void visitConditionalExpression(ConditionalExpression ce) {
+                res[0] = new Java.ConditionalExpression(ce.getLocation(),
+                        stripUnnecessaryParenExprs(ce.lhs),
+                        stripUnnecessaryParenExprs(ce.mhs),
+                        stripUnnecessaryParenExprs(ce.rhs));
+            }
+
+            public void visitConstantValue(ConstantValue cv) {
+                res[0] = cv;
+            }
+
+            public void visitCrement(Crement c) {
+                if(c.pre) {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            c.operator,
+                            stripUnnecessaryParenExprs(c.operand));
+                } else {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            stripUnnecessaryParenExprs(c.operand),
+                            c.operator);
+                }
+            }
+
+            public void visitInstanceof(Instanceof io) {
+                res[0] = new Java.Instanceof(io.getLocation(),
+                        stripUnnecessaryParenExprs(io.lhs),
+                        io.rhs);
+            }
+
+            public void visitLiteral(Literal l) {
+                res[0] = l;
+            }
+
+            public void visitMethodInvocation(MethodInvocation mi) {
+                res[0] = new Java.MethodInvocation(mi.getLocation(),
+                        stripUnnecessaryParenExprs(mi.optionalTarget),
+                        mi.methodName,
+                        stripUnnecessaryParenExprs(mi.arguments));
+            }
+
+            public void visitNewAnonymousClassInstance(
+                    NewAnonymousClassInstance naci) {
+                res[0] = naci; //too much effort
+            }
+
+            public void visitNewArray(NewArray na) {
+                res[0] = new Java.NewArray(na.getLocation(),
+                        na.type,
+                        stripUnnecessaryParenExprs(na.dimExprs),
+                        na.dims);
+            }
+
+            public void visitNewClassInstance(NewClassInstance nci) {
+                res[0] = new Java.NewClassInstance(nci.getLocation(),
+                        stripUnnecessaryParenExprs(nci.optionalQualification),
+                        nci.type,
+                        stripUnnecessaryParenExprs(nci.arguments));
+            }
+
+            public void visitNewInitializedArray(NewInitializedArray nia) {
+                res[0] = nia; //too much effort
+            }
+
+            public void visitParameterAccess(ParameterAccess pa) {
+                res[0] = pa;
+            }
+
+            public void visitQualifiedThisReference(QualifiedThisReference qtr) {
+                res[0] = qtr;
+            }
+
+            public void visitSuperclassMethodInvocation(
+                    SuperclassMethodInvocation smi) {
+                res[0] = new Java.SuperclassMethodInvocation(smi.getLocation(),
+                        smi.methodName,
+                        stripUnnecessaryParenExprs(smi.arguments));
+            }
+
+            public void visitThisReference(ThisReference tr) {
+                res[0] = tr;
+            }
+
+            public void visitUnaryOperation(UnaryOperation uo) {
+                res[0] = new Java.UnaryOperation(uo.getLocation(),
+                        uo.operator,
+                        stripUnnecessaryParenExprs(uo.operand));
+            }
+
+            public void visitAmbiguousName(AmbiguousName an) {
+                res[0] = an;
+            }
+
+            public void visitArrayAccessExpression(ArrayAccessExpression aae) {
+                res[0] = new Java.ArrayAccessExpression(aae.getLocation(),
+                        stripUnnecessaryParenExprs(aae.lhs),
+                        stripUnnecessaryParenExprs(aae.index));
+            }
+
+            public void visitFieldAccess(FieldAccess fa) {
+                res[0] = new Java.FieldAccess(fa.getLocation(),
+                        stripUnnecessaryParenExprs(fa.lhs),
+                        fa.field);
+            }
+
+            public void visitFieldAccessExpression(FieldAccessExpression fae) {
+                res[0] = new Java.FieldAccessExpression(fae.getLocation(),
+                        stripUnnecessaryParenExprs(fae.lhs),
+                        fae.fieldName);
+            }
+
+            public void visitLocalVariableAccess(LocalVariableAccess lva) {
+                res[0] = lva;
+            }
+
+            public void visitParenthesizedExpression(ParenthesizedExpression pe) {
+                res[0] = stripUnnecessaryParenExprs(pe.value);
+            }
+
+            public void visitSuperclassFieldAccessExpression(
+                    SuperclassFieldAccessExpression scfae) {
+                res[0] = scfae;
+            }
+            
+        };
+        rvalue.accept(rv);
+        return res[0];
+    }
+    
+    public void testSimple() throws Exception {
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", false);
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", true);
+    }
+    
+    public void testParens() throws Exception {
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", false);
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", true);
+    }
+    
+    public void testMany() throws Exception {
+        final String[][] exprs = new String[][] {
+              //input                                  expected simplified                    expect non-simplified
+            { "((1)+2)",                               "1 + 2",                               "((1) + 2)"           },
+            { "1 + 2 * 3",                             null,                                  null                  },
+            { "1 + (2 * 3)",                           "1 + 2 * 3",                           null                  },
+            { "3 - (2 - 1)",                           null,                                  null                  },
+            { "true ? 1 : 2",                          null,                                  null                  },
+            { "(true ? false : true) ? 1 : 2",         null,                                  null                  },
+            { "true ? false : (true ? false : true)",  "true ? false : true ? false : true",  null                  },
+            { "-(-(2))",                               "-(-2)",                               null                  },
+            { "- - 2",                                 "-(-2)",                               "-(-2)"               },
+            { "x && (y || z)",                         null,                                  null                  },
+            { "(x && y) || z",                         "x && y || z",                         null                  },
+            { "x = (y = z)",                           "x = y = z",                           null                  },
+            { "(--x) + 3",                             "--x + 3",                             null                  },
+            { "(baz.bar).foo(x, (3 + 4) * 5)",         "baz.bar.foo(x, (3 + 4) * 5)",         null                  },
+            { "!(bar instanceof Integer)",             null,                                  null                  },
+            { "(true ? foo : bar).baz()",              null,                                  null                  },
+        };
+        
+        for(int i = 0; i < exprs.length; ++i) {
+            String input = exprs[i][0];
+            String expectSimplify = exprs[i][1];
+            if(expectSimplify == null) {
+                expectSimplify = input;
+            }
+            
+            String expectNoSimplify = exprs[i][2];
+            if(expectNoSimplify == null) {
+                expectNoSimplify = input;
+            }
+            
+            helpTestExpr(input, expectSimplify, true);
+            helpTestExpr(input, expectNoSimplify, false);
+        }
+    }
+
+}
=== tests/src/AstTests.java
==================================================================
--- tests/src/AstTests.java (revision 338)
+++ tests/src/AstTests.java (patch statementlist level 1)
@@ -0,0 +1,396 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.CompileException;
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Location;
+import org.codehaus.janino.Mod;
+import org.codehaus.janino.SimpleCompiler;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayType;
+import org.codehaus.janino.Java.BasicType;
+import org.codehaus.janino.Java.Block;
+import org.codehaus.janino.Java.CompilationUnit;
+import org.codehaus.janino.Java.ExpressionStatement;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableDeclarationStatement;
+import org.codehaus.janino.Java.MethodDeclarator;
+import org.codehaus.janino.Java.PackageMemberClassDeclaration;
+import org.codehaus.janino.Java.ReturnStatement;
+import org.codehaus.janino.Java.Rvalue;
+import org.codehaus.janino.Java.StatementList;
+import org.codehaus.janino.Java.Type;
+import org.codehaus.janino.Java.FunctionDeclarator.FormalParameter;
+import org.codehaus.janino.Parser.ParseException;
+import org.codehaus.janino.Scanner.ScanException;
+
+public class AstTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(AstTests.class.getName());
+        s.addTest(new AstTests("testSimpleAst"));
+        s.addTest(new AstTests("testLocalVariable"));
+        s.addTest(new AstTests("testBlock"));
+        s.addTest(new AstTests("testStatementList"));
+        s.addTest(new AstTests("testByteArrayLiteral"));
+        s.addTest(new AstTests("testClassRef"));
+        s.addTest(new AstTests("testPrecedence"));
+        return s;
+    }
+    
+    public AstTests(String name) { super(name); }
+
+    private static Object compileAndEval(CompilationUnit cu) throws CompileException,
+            ParseException, ScanException, IOException, ClassNotFoundException,
+            InstantiationException, IllegalAccessException,
+            NoSuchMethodException, InvocationTargetException {
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        
+        Class handMadeClass = loader.loadClass("HandMade");
+        
+        Object handMade = handMadeClass.newInstance();
+        Method calc = handMadeClass.getMethod("calculate", null);
+        Object res = calc.invoke(handMade, null);
+        return res;
+    }
+    
+    private static ArrayType createByteArrayType() {
+        return new Java.ArrayType(
+                new Java.BasicType(
+                        getLocation(),
+                        Java.BasicType.BYTE
+                )
+        );
+    };
+    
+    private static PackageMemberClassDeclaration createClass(CompilationUnit cu)
+            throws ParseException {
+        PackageMemberClassDeclaration clazz = new PackageMemberClassDeclaration(
+                getLocation(),
+                null,
+                Mod.PUBLIC,
+                "HandMade",
+                null,
+                new Type[]{}
+        );
+        cu.addPackageMemberTypeDeclaration(clazz);
+        return clazz;
+    }
+    
+    private static Type createDoubleType() {
+        return new BasicType(getLocation(), BasicType.DOUBLE);
+    }
+    
+    private static Java.UnaryOperation createOp(String op, Rvalue l) {
+        return new Java.UnaryOperation(getLocation(), op, l);
+    }
+    
+    private static Java.BinaryOperation createOp(Rvalue l1, String op, Rvalue l2) {
+        return new Java.BinaryOperation(getLocation(), l1, op, l2);
+    }
+    
+    private static Literal createLiteral(double d) {
+        return createLiteral(Double.valueOf(d));
+    }
+    
+    
+    private static Literal createLiteral(Object o) {
+        return new Literal( getLocation(), o );
+    }
+    
+    private static void createMethod(PackageMemberClassDeclaration clazz, Block body, Type returnType) {
+        MethodDeclarator method = new MethodDeclarator(
+                getLocation(),
+                null,
+                (short)(Mod.PUBLIC),
+                returnType,
+                "calculate",
+                new FormalParameter[0],
+                new Type[0],
+                body
+        );
+        clazz.addDeclaredMethod(method);
+    }
+        
+
+    private static LocalVariableDeclarationStatement createVarDecl(String name, double value) {
+        return new Java.LocalVariableDeclarationStatement(
+                getLocation(),
+                (short)0,
+                createDoubleType(),
+                new Java.VariableDeclarator[] {
+                    new Java.VariableDeclarator(
+                            getLocation(),
+                            name,
+                            0,
+                            createLiteral(value)
+                    )
+                }
+        );
+    }
+    
+    private static AmbiguousName createVariableRef(String name) {
+        return new Java.AmbiguousName(
+                getLocation(),
+                new String[] { name }
+        );
+    }
+
+    /**
+     * "Clever" method to get a location from a stack trace
+     */
+    static private Location getLocation() {
+        Exception e = new Exception();
+        StackTraceElement ste = e.getStackTrace()[1];//we only care about our caller
+        return new Location(
+                ste.getFileName(),
+                (short)ste.getLineNumber(),
+                (short)0
+        );
+    }
+
+    
+    public void testBlock() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        Block sub = new Block(getLocation());
+        sub.addStatement( createVarDecl("x", 2.0) );                        
+        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        try {
+            compileAndEval(cu);
+            fail("Block must limit the scope of variables in it");
+        } catch(CompileException ex) {
+            assertTrue(ex.getMessage().endsWith("Expression \"x\" is not an rvalue"));
+        }
+    }
+
+    public void testByteArrayLiteral() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Byte exp = Byte.valueOf((byte)1);
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.NewInitializedArray(
+                                getLocation(),
+                                createByteArrayType(),
+                                new Java.ArrayInitializer(
+                                        getLocation(),
+                                        new Java.Rvalue[] {
+                                            createLiteral(exp)
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                createByteArrayType()
+        );
+        
+        Object res = compileAndEval(cu);
+        assertEquals(exp.byteValue(), ((byte[])res)[0]);
+    }
+
+    public void testLocalVariable() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement( createVarDecl("x", 2.0) );                        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(6.0), res);
+    }
+    
+    public void testSimpleAst() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        createLiteral(3.0)
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertEquals(Double.valueOf(3.0), res);
+    }
+
+    public void testStatementList() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        StatementList sub = new StatementList(getLocation());
+        sub.addStatement( createVarDecl("x", 3.0) );                        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(9.0), res);
+    }
+    
+    public void testClassRef() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.ClassLiteral(
+                                getLocation(),
+                                new Java.ReferenceType(
+                                        getLocation(),
+                                        new String[] {
+                                            "HandMade"
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                new Java.ReferenceType(
+                        getLocation(),
+                        new String[] { "java", "lang", "Class" }
+                )
+        );
+        
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        Class handMadeClass = loader.loadClass("HandMade");
+        Method calc = handMadeClass.getMethod("calculate", null);
+        
+        Object handMade = handMadeClass.newInstance();
+        Object res = calc.invoke(handMade, null);
+        assertEquals(handMadeClass, res);
+    }
+    
+    public void testPrecedence() throws Exception {
+        ExpressionStatement es = new Java.ExpressionStatement(
+                new Java.Assignment(
+                        getLocation(),
+                        new Java.AmbiguousName(
+                                getLocation(),
+                                new String[] { "x" }
+                        ),
+                        "=",
+                        createOp(
+                                createLiteral(1), "*",
+                                createOp(createLiteral(2), "+", createLiteral(3))
+                        )
+                )
+        );
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        uv.visitExpressionStatement(es);
+        assertEquals("x = 1.0D * (2.0D + 3.0D);", sw.toString());
+    }
+}
=== tests/src/ClassDataTests.java
==================================================================
--- tests/src/ClassDataTests.java (revision 338)
+++ tests/src/ClassDataTests.java (patch statementlist level 1)
@@ -0,0 +1,119 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.SimpleCompiler;
+
+public class ClassDataTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(ClassDataTests.class.getName());
+        s.addTest(new ClassDataTests("testSimple"));
+        return s;
+    }
+
+    public ClassDataTests(String name) { super(name); }
+    
+    public void testSimple() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.cook("package test.simple;\n" +
+                "public class Simple {\n" +
+                "    public int test1() {\n" +
+                "        return 1;\n" +
+                "    }\n" +
+                "    public double test2() {\n" +
+                "        return 3.14;\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class scClass = sc.getClassLoader().loadClass("test.simple.Simple");
+        Class javaClass = getJavaClassLoader(sc).loadClass("test.simple.Simple");
+        testClasses(scClass, javaClass);
+    }
+    
+    private static void testClasses(Class c1, Class c2) throws Exception {
+        Object obj1 = c1.newInstance();
+        Object obj2 = c2.newInstance();
+        
+        Method[] methods = c1.getMethods();
+        for(int i = 0; i < methods.length; ++i) {
+            if(methods[i].getName().startsWith("test")) {
+                System.out.println("Testing equiavalence for " + c1.getCanonicalName() +"."+ methods[i].getName());
+                Method m1 = c1.getMethod(methods[i].getName(), null);
+                Method m2 = c2.getMethod(methods[i].getName(), null);
+                
+                Object res1 = m1.invoke(obj1, null);
+                Object res2 = m2.invoke(obj2, null);
+                assertEquals(res1, res2);
+            }
+        }
+    }
+    
+    private static ClassLoader getJavaClassLoader(SimpleCompiler sc) throws Exception {
+        Map classes = sc.getClassMap(); // String -> byte[]
+        File tmp = File.createTempFile("classDataTest", "tmp");
+        tmp.delete();
+        if(!tmp.mkdirs()) {
+            throw new RuntimeException("Failed to create directory: " + tmp.getCanonicalPath());
+        }
+        Iterator it = classes.entrySet().iterator();
+        while(it.hasNext()) {
+            Map.Entry e = (Map.Entry)it.next();
+            String name = (String)e.getKey();
+            byte[] data = (byte[])e.getValue();
+            
+            final File output = new File(tmp, name.replace('.', File.separatorChar) + ".class");
+            output.getParentFile().mkdirs();
+            FileOutputStream fos = new FileOutputStream(output);
+            fos.write(data);
+            fos.close();
+        }
+        
+        return new URLClassLoader(new URL[] {tmp.toURL()}, null);
+        
+    }
+    
+  
+}
=== tests/src/AllTests.java
==================================================================
--- tests/src/AllTests.java (revision 338)
+++ tests/src/AllTests.java (patch statementlist level 1)
@@ -82,5 +82,8 @@
         this.addTest(ReportedBugs.suite());
         this.addTest(SandboxTests.suite());
         this.addTest(EvaluatorTests.suite());
+        this.addTest(AstTests.suite());
+        this.addTest(ClassDataTests.suite());
+        this.addTest(UnparseTests.suite());
     }
 }
=== src/org/codehaus/janino/UnitCompiler.java
==================================================================
--- src/org/codehaus/janino/UnitCompiler.java (revision 338)
+++ src/org/codehaus/janino/UnitCompiler.java (patch statementlist level 1)
@@ -34,15 +34,39 @@
 
 package org.codehaus.janino;
 
-import java.io.*;
-import java.util.*;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
 
-import org.codehaus.janino.IClass.*;
-import org.codehaus.janino.Java.*;
-import org.codehaus.janino.Java.CompilationUnit.*;
-import org.codehaus.janino.Visitor.*;
-import org.codehaus.janino.util.*;
-import org.codehaus.janino.util.enumerator.*;
+import org.codehaus.janino.IClass.IField;
+import org.codehaus.janino.IClass.IInvocable;
+import org.codehaus.janino.IClass.IMethod;
+import org.codehaus.janino.Java.FieldAccess;
+import org.codehaus.janino.Java.Invocation;
+import org.codehaus.janino.Java.Locatable;
+import org.codehaus.janino.Java.Rvalue;
+import org.codehaus.janino.Java.SimpleType;
+import org.codehaus.janino.Java.StatementList;
+import org.codehaus.janino.Java.SuperclassFieldAccessExpression;
+import org.codehaus.janino.Java.CompilationUnit.ImportDeclaration;
+import org.codehaus.janino.Java.CompilationUnit.SingleStaticImportDeclaration;
+import org.codehaus.janino.Java.CompilationUnit.SingleTypeImportDeclaration;
+import org.codehaus.janino.Java.CompilationUnit.StaticImportOnDemandDeclaration;
+import org.codehaus.janino.Java.CompilationUnit.TypeImportOnDemandDeclaration;
+import org.codehaus.janino.Visitor.ImportVisitor;
+import org.codehaus.janino.util.ClassFile;
+import org.codehaus.janino.util.enumerator.EnumeratorSet;
 
 /**
  * This class actually implements the Java<sup>TM</sup> compiler. It is
@@ -351,7 +375,7 @@
 
         compileDeclaredMethods(cd, cf);
 
-        
+
         // Compile declared constructors.
         // As a side effect of compiling methods and constructors, synthetic "class-dollar"
         // methods (which implement class literals) are generated on-the fly.
@@ -547,17 +571,17 @@
         // Create interface initialization method iff there is any initialization code.
         if (this.generatesCode(b)) {
             Java.MethodDeclarator md = new Java.MethodDeclarator(
-                decl.getLocation(),                               // location
-                null,                                           // optionalDocComment
-                (short) (Mod.STATIC | Mod.PUBLIC),              // modifiers
-                new Java.BasicType(                             // type
-                    decl.getLocation(),
-                    Java.BasicType.VOID
-                ),
-                "<clinit>",                                     // name
-                new Java.FunctionDeclarator.FormalParameter[0], // formalParameters
-                new Java.ReferenceType[0],                      // thrownExcaptions
-                b                                               // optionalBody
+                    decl.getLocation(),                               // location
+                    null,                                           // optionalDocComment
+                    (short) (Mod.STATIC | Mod.PUBLIC),              // modifiers
+                    new Java.BasicType(                             // type
+                            decl.getLocation(),
+                            Java.BasicType.VOID
+                    ),
+                    "<clinit>",                                     // name
+                    new Java.FunctionDeclarator.FormalParameter[0], // formalParameters
+                    new Java.ReferenceType[0],                      // thrownExcaptions
+                    b                                               // optionalBody
             );
             md.setDeclaringType(decl);
             this.compile(md, cf);
@@ -590,7 +614,7 @@
             ));
         }
     }
-    
+
     /**
      * Compile all of the methods for this declaration
      * <p>
@@ -643,10 +667,11 @@
             public void visitThrowStatement                   (Java.ThrowStatement                    ts  ) { try { res[0] = UnitCompiler.this.compile2(ts  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitBreakStatement                   (Java.BreakStatement                    bs  ) { try { res[0] = UnitCompiler.this.compile2(bs  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitContinueStatement                (Java.ContinueStatement                 cs  ) { try { res[0] = UnitCompiler.this.compile2(cs  ); } catch (CompileException e) { throw new UCE(e); } }
-            public void visitEmptyStatement                   (Java.EmptyStatement                    es  ) {       res[0] = UnitCompiler.this.compile2(es  );                                                                }
+            public void visitEmptyStatement                   (Java.EmptyStatement                    es  ) {       res[0] = UnitCompiler.this.compile2(es  );                                                    }
             public void visitLocalClassDeclarationStatement   (Java.LocalClassDeclarationStatement    lcds) { try { res[0] = UnitCompiler.this.compile2(lcds); } catch (CompileException e) { throw new UCE(e); } }
             public void visitAlternateConstructorInvocation   (Java.AlternateConstructorInvocation    aci ) { try { res[0] = UnitCompiler.this.compile2(aci ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperConstructorInvocation       (Java.SuperConstructorInvocation        sci ) { try { res[0] = UnitCompiler.this.compile2(sci ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (StatementList                          sl  ) { try { res[0] = UnitCompiler.this.compile2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
         };
         try {
             bs.accept(bsv);
@@ -658,13 +683,14 @@
     private boolean compile2(Java.Initializer i) throws CompileException {
         return this.compile(i.block);
     }
-    private boolean compile2(Java.Block b) throws CompileException {
+    //takes a List<BlockStatement>
+    private boolean compile2ListStatement(List l) throws CompileException {
         this.codeContext.saveLocalVariables();
         try {
             boolean previousStatementCanCompleteNormally = true;
-            for (int i = 0; i < b.statements.size(); ++i) {
-                Java.BlockStatement bs = (Java.BlockStatement) b.statements.get(i);
-                if (!previousStatementCanCompleteNormally) {
+            for (int i = 0; i < l.size(); ++i) {
+                Java.BlockStatement bs = (Java.BlockStatement) l.get(i);
+                if (!previousStatementCanCompleteNormally && this.generatesCode(bs)) {
                     this.compileError("Statement is unreachable", bs.getLocation());
                     break;
                 }
@@ -675,6 +701,12 @@
             this.codeContext.restoreLocalVariables();
         }
     }
+    private boolean compile2(Java.Block b) throws CompileException {
+        return compile2ListStatement(b.statements);
+    }
+    private boolean compile2(Java.StatementList sl) throws CompileException {
+        return compile2ListStatement(sl.statements);
+    }
     private boolean compile2(Java.DoStatement ds) throws CompileException {
         Object cvc = this.getConstantValue(ds.condition);
         if (cvc != null) {
@@ -1667,6 +1699,9 @@
 
             // Compile the function body.
             try {
+                if(fd.optionalBody == null) {
+                    this.compileError("Method must have a body", fd.getLocation());
+                }
                 boolean canCompleteNormally = this.compile(fd.optionalBody);
                 if (canCompleteNormally) {
                     if (this.getReturnType(fd) != IClass.VOID) this.compileError("Method must return a value", fd.getLocation());
@@ -3555,6 +3590,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { try { res[0] = UnitCompiler.this.generatesCode2(fd  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) {       res[0] = UnitCompiler.this.generatesCode2(ls  );                                                                }
             public void visitBlock                            (Java.Block                             b   ) { try { res[0] = UnitCompiler.this.generatesCode2(b   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { try { res[0] = UnitCompiler.this.generatesCode2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) {       res[0] = UnitCompiler.this.generatesCode2(es  );                                                                }
             public void visitIfStatement                      (Java.IfStatement                       is  ) {       res[0] = UnitCompiler.this.generatesCode2(is  );                                                                }
             public void visitForStatement                     (Java.ForStatement                      fs  ) {       res[0] = UnitCompiler.this.generatesCode2(fs  );                                                                }
@@ -3584,12 +3620,19 @@
     public boolean generatesCode2(Java.EmptyStatement es) { return false; }
     public boolean generatesCode2(Java.LocalClassDeclarationStatement lcds) { return false; }
     public boolean generatesCode2(Java.Initializer i) throws CompileException { return this.generatesCode(i.block); }
-    public boolean generatesCode2(Java.Block b) throws CompileException {
-        for (int i = 0; i < b.statements.size(); ++i) {
-            if (this.generatesCode(((Java.BlockStatement) b.statements.get(i)))) return true;
+    //takes a List<Java.BlockStatement>
+    public boolean generatesCode2ListStatements(List l) throws CompileException {
+        for (int i = 0; i < l.size(); ++i) {
+            if (this.generatesCode(((Java.BlockStatement) l.get(i)))) return true;
         }
         return false;
     }
+    public boolean generatesCode2(Java.Block b) throws CompileException {
+        return generatesCode2ListStatements(b.statements);
+    }
+    public boolean generatesCode2(Java.StatementList sl) throws CompileException {
+        return generatesCode2ListStatements(sl.statements);
+    }
     public boolean generatesCode2(Java.FieldDeclaration fd) throws CompileException {
         // Code is only generated if at least one of the declared variables has a
         // non-constant-final initializer.
@@ -3622,6 +3665,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { UnitCompiler.this.leave2(fd,   optionalStackValueType); }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) { UnitCompiler.this.leave2(ls,   optionalStackValueType); }
             public void visitBlock                            (Java.Block                             b   ) { UnitCompiler.this.leave2(b,    optionalStackValueType); }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { UnitCompiler.this.leave2(sl,   optionalStackValueType); }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) { UnitCompiler.this.leave2(es,   optionalStackValueType); }
             public void visitIfStatement                      (Java.IfStatement                       is  ) { UnitCompiler.this.leave2(is,   optionalStackValueType); }
             public void visitForStatement                     (Java.ForStatement                      fs  ) { UnitCompiler.this.leave2(fs,   optionalStackValueType); }
@@ -3910,7 +3954,7 @@
                 String className = Java.join(rt.identifiers, ".");
                 IClass result = findClassByName(rt.getLocation(), className);
                 if (result != null) return result;
-
+                
                 this.compileError("Class \"" + className + "\" not found", rt.getLocation());
                 return this.iClassLoader.OBJECT;
             }
@@ -3928,7 +3972,7 @@
             return this.iClassLoader.OBJECT;
         }
     }
-
+    
     private IClass getType2(Java.RvalueMemberType rvmt) throws CompileException {
         IClass rvt = this.getType(rvmt.rvalue);
         IClass memberType = this.findMemberType(rvt, rvmt.identifier, rvmt.getLocation());
@@ -4147,6 +4191,7 @@
         return this.getType(nia.arrayType);
     }
     private IClass getType2(Java.Literal l) {
+        if (l.value instanceof Byte     ) return IClass.BYTE;
         if (l.value instanceof Integer  ) return IClass.INT;
         if (l.value instanceof Long     ) return IClass.LONG;
         if (l.value instanceof Float    ) return IClass.FLOAT;
@@ -4159,6 +4204,7 @@
     }
     private IClass getType2(Java.ConstantValue cv) {
         IClass res = (
+            cv.constantValue instanceof Byte               ? IClass.BYTE    :
             cv.constantValue instanceof Integer            ? IClass.INT     :
             cv.constantValue instanceof Long               ? IClass.LONG    :
             cv.constantValue instanceof Float              ? IClass.FLOAT   :
@@ -5465,6 +5511,27 @@
     }
 
     /**
+     * Check to see if a local variable was declared in this statement.  Possibly recursing into a StatementList, as needed.
+     * @param varName  The name of the variable to find
+     * @param stmt     The statement in question
+     * @return         A local variable definition if found.  null if not found
+     * @throws CompileException
+     */
+    private Java.LocalVariable findLocalVariableInStatement(Java.BlockStatement stmt, String varName) throws CompileException {
+        if (stmt instanceof Java.LocalVariableDeclarationStatement) {
+            return this.findLocalVariable((Java.LocalVariableDeclarationStatement)stmt, varName);
+        }
+        if (stmt instanceof Java.StatementList) {
+            Java.StatementList sl = (Java.StatementList)stmt;
+            for (Iterator it = sl.statements.iterator(); it.hasNext();) {
+                Java.LocalVariable lv = findLocalVariableInStatement((Java.BlockStatement)it.next(), varName);
+                if (lv != null) return lv;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Find a local variable declared by the given <code>blockStatement</code> or any enclosing
      * scope up to the {@link Java.FunctionDeclarator}.
      */
@@ -5477,19 +5544,15 @@
             {
                 if (s instanceof Java.ForStatement) {
                     Java.BlockStatement optionalForInit = ((Java.ForStatement) s).optionalInit;
-                    if (optionalForInit instanceof Java.LocalVariableDeclarationStatement) {
-                        Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) optionalForInit, name);
+                    Java.LocalVariable lv = this.findLocalVariableInStatement(optionalForInit, name);
                         if (lv != null) return lv;
                     }
-                }
                 if (es instanceof Java.Block) {
                     Java.Block b = (Java.Block) es;
                     for (Iterator it = b.statements.iterator();;) {
                         Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                        if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                            Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                        Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                             if (lv != null) return lv;
-                        }
                         if (bs2 == s) break;
                     }
                 }
@@ -5499,10 +5562,8 @@
                         Java.SwitchStatement.SwitchBlockStatementGroup sbgs = (Java.SwitchStatement.SwitchBlockStatementGroup) it2.next();
                         for (Iterator it = sbgs.blockStatements.iterator(); it.hasNext();) {
                             Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                            if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                                Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                            Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                                 if (lv != null) return lv;
-                            }
                             if (bs2 == s) break SBSGS;
                         }
                     }
=== src/org/codehaus/janino/Java.java
==================================================================
--- src/org/codehaus/janino/Java.java (revision 338)
+++ src/org/codehaus/janino/Java.java (patch statementlist level 1)
@@ -77,6 +77,7 @@
         private final Location location;
 
         protected Located(Location location) {
+            //assert location != null;
             this.location = location;
         }
 
@@ -1227,10 +1228,52 @@
         }
 
         // Compile time members.
-
         public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitBlock(this); }
     }
+    
+    /**
+     * This is similar to a {@link Java.Block} except that it does not create a scope around it statements.
+     * It is useful for programmatically manipulating an AST
+     */
+    public final static class StatementList extends Statement {
+        public final List statements = new ArrayList(); // BlockStatement
 
+        public StatementList(Location location) {
+            super(location);
+        }
+
+        public void addStatement(BlockStatement statement) {
+            this.statements.add(statement);
+            statement.setEnclosingScope(this.getEnclosingScope());
+        }
+        
+        public void addStatements(
+            List statements // BlockStatement
+        ) {
+            Iterator stmtIter = statements.iterator();
+            while(stmtIter.hasNext()) {
+                addStatement((BlockStatement)stmtIter.next());
+            }
+        }
+        
+        public BlockStatement[] getStatements() {
+            return (BlockStatement[]) this.statements.toArray(new BlockStatement[this.statements.size()]);
+        }
+        
+        // set children to share this object's enclosing scope
+        public void setEnclosingScope(Scope enclosingScope) {
+            super.setEnclosingScope(enclosingScope);
+            Iterator stmtIter = this.statements.iterator();
+            while(stmtIter.hasNext()) {
+                BlockStatement stmt = (BlockStatement)stmtIter.next();
+                stmt.setEnclosingScope(enclosingScope);
+            }
+        }
+
+        // Compile time members.
+        public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitStatementList(this); }
+    }
+
     /**
      * Base class for statements that can be terminated abnormally with a
      * "break" statement.
@@ -2674,6 +2717,23 @@
         public final Rvalue[] dimExprs;
         public final int      dims;
 
+        /**
+         * Create a new array with dimension dimExprs.length + dims
+         * <p>
+         * e.g. byte[12][][] is created with
+         *     new NewArray(
+         *         null,
+         *         Java.BasicType(NULL, Java.BasicType.BYTE),
+         *         new Rvalue[] {
+         *             new Java.Literal(null, Integer.valueOf(12)
+         *         },
+         *         2
+         *     )
+         * @param location  the location of this element
+         * @param type      the base type of the array
+         * @param dimExprs  sizes for dimensions being allocated with specific sizes
+         * @param dims      the number of dimensions that are not yet allocated
+         */
         public NewArray(
             Location location,
             Type     type,
=== src/org/codehaus/janino/SimpleCompiler.java
==================================================================
--- src/org/codehaus/janino/SimpleCompiler.java (revision 338)
+++ src/org/codehaus/janino/SimpleCompiler.java (patch statementlist level 1)
@@ -75,6 +75,7 @@
     private IClassLoader         iClassLoader = null;
 
     private ClassLoader result = null;
+    private Map classes = null; // String className => byte[] data
 
     public static void main(String[] args) throws Exception {
         if (args.length >= 1 && args[0].equals("-help")) {
@@ -201,7 +202,7 @@
     public static final ClassLoader BOOT_CLASS_LOADER = new ClassLoader(null) {};
 
     /**
-     * Allowe references to the classes loaded through this parent class loader
+     * Allow references to the classes loaded through this parent class loader
      * (@see {@link #setParentClassLoader(ClassLoader)}), plus the extra
      * <code>auxiliaryClasses</code>.
      * <p>
@@ -233,7 +234,22 @@
             DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
         );
     }
+    
+    /**
+     * Cook this compilation unit directly.
+     *  See {@link Cookable.cook}
+     */
+    public void cook(Java.CompilationUnit compilationUnit)
+    throws CompileException, Parser.ParseException, Scanner.ScanException, IOException {
+        this.setUpClassLoaders();
 
+        // Compile the classes and load them.
+        this.compileToClassLoader(
+            compilationUnit,
+            DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
+        );
+    }
+
     /**
      * Initializes {@link #classLoader} and {@link #iClassLoader} from the configured
      * {@link #parentClassLoader} and {@link #optionalAuxiliaryClasses}. These are needed by
@@ -329,6 +345,20 @@
         if (this.result == null) throw new IllegalStateException("Must only be called after \"cook()\"");
         return this.result;
     }
+    
+    /**
+     * Returns a Map<String, byte[]> of the previously compiled classes.  These classes are
+     * suitable for use in a {@link ByteArrayClassLoader} or writing to a .class file.
+     * <p>
+     * This method must only be called after {@link #cook(Scanner)}.
+     * <p>
+     * This method must not be called for instances of derived classes.
+     */
+    public Map getClassMap() {
+        if (this.getClass() != SimpleCompiler.class) throw new IllegalStateException("Must not be called on derived instances");
+        if (this.classes == null) throw new IllegalStateException("Must only be called after \"cook()\"");
+        return this.classes;
+    }
 
     /**
      * Two {@link SimpleCompiler}s are regarded equal iff
@@ -409,7 +439,7 @@
         ).compileUnit(debuggingInformation);
 
         // Convert the class files to bytes and store them in a Map.
-        Map classes = new HashMap(); // String className => byte[] data
+        classes = new HashMap(); // String className => byte[] data
         for (int i = 0; i < classFiles.length; ++i) {
             ClassFile cf = classFiles[i];
             classes.put(cf.getThisClassName(), cf.toByteArray());
=== src/org/codehaus/janino/Scanner.java
==================================================================
--- src/org/codehaus/janino/Scanner.java (revision 338)
+++ src/org/codehaus/janino/Scanner.java (patch statementlist level 1)
@@ -457,6 +457,9 @@
         if (v instanceof Boolean) {
             return v.toString();
         }
+        if (v instanceof Byte) {
+            return v.toString();
+        }
         if (v == null) {
             return "null";
         }
=== src/org/codehaus/janino/Visitor.java
==================================================================
--- src/org/codehaus/janino/Visitor.java (revision 338)
+++ src/org/codehaus/janino/Visitor.java (patch statementlist level 1)
@@ -73,6 +73,7 @@
         void visitFieldDeclaration(Java.FieldDeclaration fd);
         void visitLabeledStatement(Java.LabeledStatement ls);
         void visitBlock(Java.Block b);
+        void visitStatementList(Java.StatementList sl);
         void visitExpressionStatement(Java.ExpressionStatement es);
         void visitIfStatement(Java.IfStatement is);
         void visitForStatement(Java.ForStatement fs);
=== src/org/codehaus/janino/UnparseVisitor.java
==================================================================
--- src/org/codehaus/janino/UnparseVisitor.java (revision 338)
+++ src/org/codehaus/janino/UnparseVisitor.java (patch statementlist level 1)
@@ -47,6 +47,101 @@
 public class UnparseVisitor implements Visitor.ComprehensiveVisitor {
     private final AutoIndentWriter aiw;
     private final PrintWriter      pw;
+    
+    private final Stack curPrecedence;
+    
+    /**
+     * Install op as the next op's precedence level
+     * @param op a string that it is the precedenceMap
+     */
+    private void pushPrecedence(String op) {
+        Object o = precedenceMap.get(op);
+        if(o == null) {
+            throw new RuntimeException("Illegal operator for precedence: " + op);
+        }
+        curPrecedence.push(o);
+    }
+    
+    /**
+     * Remove the current precedence setting
+     */
+    private void popPrecedence() {
+        curPrecedence.pop();
+    }
+    
+    /**
+     * Give the precedence value a slight nudge to account for associativity of operators
+     */
+    private void adjustPrecedenceForAssociativity(boolean higher) {
+        double cur = ((Double)curPrecedence.pop()).doubleValue();
+        cur += higher ? 0.5 : -0.5;
+        curPrecedence.push(Double.valueOf(cur));
+    }
+    
+    private boolean needsParens(String op) {
+        //return false;
+        if(curPrecedence.isEmpty()) { return false; }
+        double cur = ((Double)curPrecedence.peek()).doubleValue();
+        double nxt = ((Double)precedenceMap.get(op)).doubleValue();
+        return cur > nxt;
+    }
+    
+    // this provides a mapping from operators to precedence levels
+    // - higher numbers are more tightly binding
+    // - ambiguous cases are just given special tokens
+    // - "reset" is a special token for bracketing operations
+    private static final Map precedenceMap = new HashMap(); // Map<String, Double>
+    static {
+        precedenceMap.put("[]",         Double.valueOf(15));
+        precedenceMap.put(".",          Double.valueOf(15));
+        precedenceMap.put("()",         Double.valueOf(15));
+        precedenceMap.put("methodcall", Double.valueOf(15));
+        // ++ and -- are ambiguous as they are both post and prefix
+        // so instead we are just putting the names
+        precedenceMap.put("postfix",    Double.valueOf(15));
+        precedenceMap.put("prefix",     Double.valueOf(14));
+        //unary - is ambiguous just use prefix
+        //precedenceMap.put("-",          Double.valueOf(14));
+        precedenceMap.put("~",          Double.valueOf(14));
+        precedenceMap.put("!",          Double.valueOf(14));
+        precedenceMap.put("cast",       Double.valueOf(13));
+        precedenceMap.put("new",        Double.valueOf(13));
+        precedenceMap.put("*",          Double.valueOf(12));
+        precedenceMap.put("/",          Double.valueOf(12));
+        precedenceMap.put("%",          Double.valueOf(12));
+        precedenceMap.put("+",          Double.valueOf(11));
+        precedenceMap.put("-",          Double.valueOf(11));
+        precedenceMap.put("<<",         Double.valueOf(10));
+        precedenceMap.put(">>",         Double.valueOf(10));
+        precedenceMap.put(">>>",        Double.valueOf(10));
+        precedenceMap.put(">=",         Double.valueOf(9));
+        precedenceMap.put("<=",         Double.valueOf(9));
+        precedenceMap.put("<",          Double.valueOf(9));
+        precedenceMap.put(">",          Double.valueOf(9));
+        precedenceMap.put("instanceof", Double.valueOf(9));
+        precedenceMap.put("==",         Double.valueOf(8));
+        precedenceMap.put("!=",         Double.valueOf(8));
+        precedenceMap.put("&",          Double.valueOf(7));
+        precedenceMap.put("^",          Double.valueOf(6));
+        precedenceMap.put("|",          Double.valueOf(5));
+        precedenceMap.put("&&",         Double.valueOf(4));
+        precedenceMap.put("||",         Double.valueOf(4));
+        precedenceMap.put("?:",         Double.valueOf(3));
+        precedenceMap.put("=",          Double.valueOf(2));
+        precedenceMap.put("+=",         Double.valueOf(2));
+        precedenceMap.put("-=",         Double.valueOf(2));
+        precedenceMap.put("*=",         Double.valueOf(2));
+        precedenceMap.put("/=",         Double.valueOf(2));
+        precedenceMap.put("%=",         Double.valueOf(2));
+        precedenceMap.put("&=",         Double.valueOf(2));
+        precedenceMap.put("^=",         Double.valueOf(2));
+        precedenceMap.put("|=",         Double.valueOf(2));
+        precedenceMap.put("<<=",        Double.valueOf(2));
+        precedenceMap.put(">>=",        Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put("reset",      Double.valueOf(0));
+    }
 
     /**
      * Testing of parsing/unparsing.
@@ -84,6 +179,7 @@
     public UnparseVisitor(Writer w) {
         this.aiw = new AutoIndentWriter(w);
         this.pw = new PrintWriter(this.aiw, true);
+        this.curPrecedence = new Stack();
     }
 
     public void unparseCompilationUnit(Java.CompilationUnit cu) {
@@ -177,11 +273,18 @@
     }
     public void visitBlock(Java.Block b) {
         this.pw.println('{');
-        for (Iterator it = b.statements.iterator(); it.hasNext();) {
+        visitListStatements(b.statements);
+        this.pw.print('}');
+    }
+    public void visitStatementList(Java.StatementList sl) {
+        visitListStatements(sl.statements);
+    }
+    //takes a List<Java.BlockStatement>
+    private void visitListStatements(List l) {
+        for (Iterator it = l.iterator(); it.hasNext();) {
             ((Java.BlockStatement) it.next()).accept(this);
             this.pw.println();
         }
-        this.pw.print('}');
     }
     public void visitBreakStatement(Java.BreakStatement bs) {
         this.pw.print("break");
@@ -204,8 +307,10 @@
         this.pw.print(';');
     }
     public void visitExpressionStatement(Java.ExpressionStatement es) {
+        pushPrecedence("reset"); //this might be an expression in a nested class body or something
         ((Java.Atom) es.rvalue).accept(this);
         this.pw.print(';');
+        popPrecedence();
     }
     public void visitForStatement(Java.ForStatement fs) {
         this.pw.print("for (");
@@ -334,12 +439,16 @@
         this.pw.print(' ' + fp.name);
     }
     public void visitMethodInvocation(Java.MethodInvocation mi) {
+        if(needsParens("methodcall")) { this.pw.print('('); }
         if (mi.optionalTarget != null) {
+            pushPrecedence(".");
             mi.optionalTarget.accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print(mi.methodName);
         this.unparseFunctionInvocationArguments(mi.arguments);
+        if(needsParens("methodcall")) { this.pw.print(')'); }
     }
     public void visitAlternateConstructorInvocation(Java.AlternateConstructorInvocation aci) {
         this.pw.print("this");
@@ -354,28 +463,47 @@
         this.unparseFunctionInvocationArguments(sci.arguments);
     }
     public void visitNewClassInstance(Java.NewClassInstance nci) {
+        if(needsParens("new")) { this.pw.print('('); }
         if (nci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) nci.optionalQualification).accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print("new " + nci.type.toString());
         this.unparseFunctionInvocationArguments(nci.arguments);
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitAssignment(Java.Assignment a) {
+        if(needsParens(a.operator)) { this.pw.print('('); }
+        pushPrecedence(a.operator);
         ((Java.Atom) a.lhs).accept(this);
         this.pw.print(' ' + a.operator + ' ');
         ((Java.Atom) a.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(a.operator)) { this.pw.print(')'); }
     }
     public void visitAmbiguousName(Java.AmbiguousName an) { this.pw.print(an.toString()); }
     public void visitArrayAccessExpression(Java.ArrayAccessExpression aae) {
+        if(needsParens("[]")) { this.pw.print('('); }
+        pushPrecedence("[]");
         ((Java.Atom) aae.lhs).accept(this);
+        popPrecedence();
+        
         this.pw.print('[');
+        pushPrecedence("reset");
         ((Java.Atom) aae.index).accept(this);
+        popPrecedence();
         this.pw.print(']');
+        if(needsParens("[]")) { this.pw.print(')'); }
     }
     public void visitArrayLength(Java.ArrayLength al) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) al.lhs).accept(this);
         this.pw.print(".length");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitArrayType(Java.ArrayType at) {
         ((Java.Atom) at.componentType).accept(this);
@@ -385,44 +513,80 @@
         this.pw.print(bt.toString());
     }
     public void visitBinaryOperation(Java.BinaryOperation bo) {
+        if(needsParens(bo.op)) { this.pw.print('('); }
+        pushPrecedence(bo.op);
         ((Java.Atom) bo.lhs).accept(this);
         this.pw.print(' ' + bo.op + ' ');
+        adjustPrecedenceForAssociativity(true); //binary ops are all left -> right associative
         ((Java.Atom) bo.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(bo.op)) { this.pw.print(')'); }
     }
     public void visitCast(Java.Cast c) {
+        if(needsParens("cast")) { this.pw.print('('); }
+        pushPrecedence("cast");
         this.pw.print('(');
         ((Java.Atom) c.targetType).accept(this);
         this.pw.print(") ");
         ((Java.Atom) c.value).accept(this);
+        popPrecedence();
+        if(needsParens("cast")) { this.pw.print(')'); }
     }
     public void visitClassLiteral(Java.ClassLiteral cl) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) cl.type).accept(this);
         this.pw.print(".class");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitConditionalExpression(Java.ConditionalExpression ce) {
+        if(needsParens("?:")) { this.pw.print('('); }
+        pushPrecedence("?:");
+        adjustPrecedenceForAssociativity(true); //ternary is right -> left associative
         ((Java.Atom) ce.lhs).accept(this);
+        adjustPrecedenceForAssociativity(false); //back to normal
         this.pw.print(" ? ");
         ((Java.Atom) ce.mhs).accept(this);
         this.pw.print(" : ");
         ((Java.Atom) ce.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("?:")) { this.pw.print(')'); }
     }
     public void visitConstantValue(Java.ConstantValue cv) { this.pw.print(cv.toString()); }
     public void visitCrement(Java.Crement c) {
-        this.pw.print(
-            c.pre ?
-            c.operator + c.operand :
-            c.operand + c.operator
-        );
+        String prec = c.pre ? "prefix" : "postfix";
+        if(needsParens(prec)) { this.pw.print('('); }
+        pushPrecedence(prec);
+        if(c.pre) {
+            this.pw.print(c.operator);
+            this.pw.print(c.operand);
+        } else {
+            this.pw.print(c.operand);
+            this.pw.print(c.operator);
+        }
+        popPrecedence();
+        if(needsParens(prec)) { this.pw.print(')'); }
     }
     public void visitFieldAccess(Java.FieldAccess fa) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fa.lhs.accept(this);
         this.pw.print('.' + fa.field.getName());
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitFieldAccessExpression(Java.FieldAccessExpression fae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fae.lhs.accept(this);
         this.pw.print('.' + fae.fieldName);
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         if (scfae.optionalQualification != null) {
             scfae.optionalQualification.accept((Visitor.TypeVisitor) this);
             this.pw.print(".super." + scfae.fieldName);
@@ -430,37 +594,57 @@
         {
             this.pw.print("super." + scfae.fieldName);
         }
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitInstanceof(Java.Instanceof io) {
+        if(needsParens("instanceof")) { this.pw.print('('); }
+        pushPrecedence("instanceof");
         ((Java.Atom) io.lhs).accept(this);
         this.pw.print(" instanceof ");
         ((Java.Atom) io.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("instanceof")) { this.pw.print(')'); }
     }
     public void visitLiteral(Java.Literal l) { this.pw.print(l.toString()); }
     public void visitLocalVariableAccess(Java.LocalVariableAccess lva) { this.pw.print(lva.toString()); }
     public void visitNewArray(Java.NewArray na) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         ((Java.Atom) na.type).accept(this);
         for (int i = 0; i < na.dimExprs.length; ++i) {
             this.pw.print('[');
+            pushPrecedence("reset");
             ((Java.Atom) na.dimExprs[i]).accept(this);
+            popPrecedence();
             this.pw.print(']');
         }
         for (int i = 0; i < na.dims; ++i) {
             this.pw.print("[]");
         }
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitNewInitializedArray(Java.NewInitializedArray nai) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         nai.arrayType.accept(this);
         this.pw.print(" ");
         this.unparseArrayInitializerOrRvalue(nai.arrayInitializer);
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitPackage(Java.Package p) { this.pw.print(p.toString()); }
     public void visitParameterAccess(Java.ParameterAccess pa) { this.pw.print(pa.toString()); }
     public void visitQualifiedThisReference(Java.QualifiedThisReference qtr) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) qtr.qualification).accept(this);
         this.pw.print(".this");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitReferenceType(Java.ReferenceType rt) { this.pw.print(rt.toString()); }
     public void visitRvalueMemberType(Java.RvalueMemberType rmt) { this.pw.print(rmt.toString()); }
@@ -473,12 +657,19 @@
         this.pw.print("this");
     }
     public void visitUnaryOperation(Java.UnaryOperation uo) {
+        if(needsParens("prefix")) { this.pw.print('('); }
+        pushPrecedence("prefix");
         this.pw.print(uo.operator);
+        this.adjustPrecedenceForAssociativity(true); //handle cases like "- - 3" as -(-3)
         ((Java.Atom) uo.operand).accept(this);
+        popPrecedence();
+        if(needsParens("prefix")) { this.pw.print(')'); }
     }
     public void visitParenthesizedExpression(Java.ParenthesizedExpression pe) {
         this.pw.print('(');
+        pushPrecedence("reset");
         ((Java.Atom) pe.value).accept(this);
+        popPrecedence();
         this.pw.print(')');
     }
 
@@ -508,11 +699,13 @@
             } else
             {
                 this.pw.print("{ ");
+                pushPrecedence("reset");
                 this.unparseArrayInitializerOrRvalue(ai.values[0]);
                 for (int i = 1; i < ai.values.length; ++i) {
                     this.pw.print(", ");
                     this.unparseArrayInitializerOrRvalue(ai.values[i]);
                 }
+                popPrecedence();
                 this.pw.print(" }");
             }
         } else
@@ -524,22 +717,32 @@
     public void visitAnonymousClassDeclaration(Java.AnonymousClassDeclaration acd) {
         ((Java.Atom) acd.baseType).accept(this);
         this.pw.println(" {");
+        pushPrecedence("reset");
         this.unparseClassDeclarationBody(acd);
+        popPrecedence();
         this.pw.print('}');
     }
     public void visitNewAnonymousClassInstance(Java.NewAnonymousClassInstance naci) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         if (naci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) naci.optionalQualification).accept(this);
+            popPrecedence();
             this.pw.print('.');
         }
         this.pw.print("new " + naci.anonymousClassDeclaration.baseType.toString() + '(');
+        pushPrecedence("reset");
         for (int i = 0; i < naci.arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) naci.arguments[i]).accept(this);
         }
+        popPrecedence(); //pop the reset
         this.pw.println(") {");
         this.unparseClassDeclarationBody(naci.anonymousClassDeclaration);
         this.pw.print('}');
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     // Multi-line!
     private void unparseClassDeclarationBody(Java.ClassDeclaration cd) {
@@ -607,10 +810,12 @@
     }
     private void unparseFunctionInvocationArguments(Java.Rvalue[] arguments) {
         this.pw.print('(');
+        pushPrecedence("reset");
         for (int i = 0; i < arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) arguments[i]).accept(this);
         }
+        popPrecedence();
         this.pw.print(')');
     }
 }
=== src/org/codehaus/janino/util/Traverser.java
==================================================================
--- src/org/codehaus/janino/util/Traverser.java (revision 338)
+++ src/org/codehaus/janino/util/Traverser.java (patch statementlist level 1)
@@ -70,6 +70,7 @@
         public final void visitFieldDeclaration(Java.FieldDeclaration fd)                                     { Traverser.this.traverseFieldDeclaration(fd); }
         public final void visitLabeledStatement(Java.LabeledStatement ls)                                     { Traverser.this.traverseLabeledStatement(ls); }
         public final void visitBlock(Java.Block b)                                                            { Traverser.this.traverseBlock(b); }
+        public final void visitStatementList(Java.StatementList sl)                                           { Traverser.this.traverseStatementList(sl); }
         public final void visitExpressionStatement(Java.ExpressionStatement es)                               { Traverser.this.traverseExpressionStatement(es); }
         public final void visitIfStatement(Java.IfStatement is)                                               { Traverser.this.traverseIfStatement(is); }
         public final void visitForStatement(Java.ForStatement fs)                                             { Traverser.this.traverseForStatement(fs); }
@@ -220,6 +221,12 @@
         }
         this.traverseStatement(b);
     }
+    public void traverseStatementList(Java.StatementList sl) {
+        for (Iterator it = sl.statements.iterator(); it.hasNext();) {
+            ((Java.Statement) it.next()).accept(this.cv);
+        }
+        this.traverseStatement(sl);
+    }
 
     public void traverseExpressionStatement(Java.ExpressionStatement es) {
         es.rvalue.accept((Visitor.RvalueVisitor) this.cv);

==== BEGIN SVK PATCH BLOCK ====
Version: svk v2.0.2 (linux)

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
==== END SVK PATCH BLOCK ====


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email

Re: Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

This patch includes fixes for

http://jira.codehaus.org/browse/JANINO-110
http://jira.codehaus.org/browse/JANINO-111
http://jira.codehaus.org/browse/JANINO-112
http://jira.codehaus.org/browse/JANINO-113

- added class Java.StatementList
- this allows code to be generated dynamically into an existing AST
(very necessary for my compiler)
  - update as necessary all the visitors for Java.StatementList
  - basic tests for Java.StatementList

- improved comments for Java.NewArray

It does not contain my earlier change which exposed byte[] in
SimpleCompiler.  It turns out that I actually wanted to be using
UnitCompiler and just didn't know it.  The change from SimpleCompiler
to a custom Compiler (based on Compiler) that uses UnitCompiler
internally has greatly cleaned up the code for my project.  We may
wish to improve the documentation around this point, as the example
did not provide an in depth enough example.  I will attempt to further
write up my experiences from integrating Janino once the project is
further advanced.

Once again, any pointers on things I can do to speed the inclusion of
patches into Janino or to improve the patch in general are greatly appreciated.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

This version includes further updates to problems I found in

http://jira.codehaus.org/browse/JANINO-113

Matt

On Fri, May 9, 2008 at 10:40 PM, Matt Fowles <matt.fowles@...> wrote:

> All~
>
>  This patch includes fixes for
>
>  http://jira.codehaus.org/browse/JANINO-110
>
> http://jira.codehaus.org/browse/JANINO-111
>  http://jira.codehaus.org/browse/JANINO-112
>  http://jira.codehaus.org/browse/JANINO-113
>
>
>  - added class Java.StatementList
>  - this allows code to be generated dynamically into an existing AST
>  (very necessary for my compiler)
>   - update as necessary all the visitors for Java.StatementList
>   - basic tests for Java.StatementList
>
>  - improved comments for Java.NewArray
>
>  It does not contain my earlier change which exposed byte[] in
>  SimpleCompiler.  It turns out that I actually wanted to be using
>  UnitCompiler and just didn't know it.  The change from SimpleCompiler
>  to a custom Compiler (based on Compiler) that uses UnitCompiler
>  internally has greatly cleaned up the code for my project.  We may
>  wish to improve the documentation around this point, as the example
>  did not provide an in depth enough example.  I will attempt to further
>  write up my experiences from integrating Janino once the project is
>  further advanced.
>
>
>
>  Once again, any pointers on things I can do to speed the inclusion of
>  patches into Janino or to improve the patch in general are greatly appreciated.
>

[statementlist.patch]

==== Patch <statementlist> level 4
Source: 1adc4bfb-2c32-0410-81b9-bf0f0eac59bd:/janino-stmt-container:73444
Target: eb4544d6-894b-0410-9319-a9612837a279:/trunk/janino:338
        (http://svn.codehaus.org/janino)
Log:
 r72684@spiceweasel:  fowles | 2008-04-30 10:51:04 -0400
 creating a local branch for statement container work
 
 r72685@spiceweasel:  fowles | 2008-04-30 11:00:16 -0400
 Add support for StatementList and tests
 
 r72834@spiceweasel:  fowles | 2008-05-01 17:23:02 -0400
 comment out assertions since they are not valid in Java 1.2
 
 r72835@spiceweasel:  fowles | 2008-05-01 17:23:27 -0400
 Fix byte[] literals for ArrayInitiliazations
 
 r72849@spiceweasel:  fowles | 2008-05-01 22:49:37 -0400
 switch location null check to an IllegalArgumentException
 
 r72851@spiceweasel:  fowles | 2008-05-01 23:37:27 -0400
 Add a useful test
 
 r72852@spiceweasel:  fowles | 2008-05-01 23:45:48 -0400
 more tests
 
 r72853@spiceweasel:  fowles | 2008-05-02 00:17:46 -0400
 apparently some janino tests use null locations
 
 r72854@spiceweasel:  fowles | 2008-05-02 00:17:57 -0400
 found a way to make the test fail!
 
 r72915@spiceweasel:  fowles | 2008-05-02 16:00:07 -0400
 refactor a bunch of duplicate code
 
 r72916@spiceweasel:  fowles | 2008-05-02 16:25:08 -0400
 fix the problem with fully qualified names in this compilation unit
 
 r72941@spiceweasel:  fowles | 2008-05-02 16:37:33 -0400
 remove some debugging code
 
 r72942@spiceweasel:  fowles | 2008-05-02 23:55:16 -0400
 make test go three levels deep and clean up style
 
 r73062@spiceweasel:  fowles | 2008-05-05 17:23:33 -0400
 Fix a few small bugs with StatementLists
 
 r73063@spiceweasel:  fowles | 2008-05-05 17:35:48 -0400
 Fix a missing case for Byte literal
 
 r73064@spiceweasel:  fowles | 2008-05-05 17:35:58 -0400
 fix an indent
 
 r73065@spiceweasel:  fowles | 2008-05-05 17:38:47 -0400
 manually set the line encoding
 
 r73094@spiceweasel:  fowles | 2008-05-06 13:12:03 -0400
 Expose access to the underlying class data and write a test for it
 
 r73095@spiceweasel:  fowles | 2008-05-06 14:21:15 -0400
 make StatementList a more first class citizen and teach everyone how to deal with it.
 
 r73121@spiceweasel:  fowles | 2008-05-06 15:47:11 -0400
 Handle an unexpected null case more gracefully
 
 r73130@spiceweasel:  fowles | 2008-05-06 16:34:56 -0400
 add some javadoc
 
 r73135@spiceweasel:  fowles | 2008-05-06 18:28:17 -0400
 add suport for Operator precedence to UnparseVisitor and a basic test for it
 
 r73136@spiceweasel:  fowles | 2008-05-07 14:12:28 -0400
 add some tests for unparse and precedence
 fix a few bugs they found
 
 r73183@spiceweasel:  fowles | 2008-05-07 14:42:42 -0400
 remove a line ending change I made
 
 r73184@spiceweasel:  fowles | 2008-05-07 14:46:21 -0400
 somehow I messed up the line endings on this file
 
 r73185@spiceweasel:  fowles | 2008-05-07 14:50:45 -0400
 Fix some indenting I screwed up
 
 r73237@spiceweasel:  fowles | 2008-05-08 13:19:08 -0400
 expose a few fields in UnparseVisitor to allow subclasses to play tricks
 
 r73257@spiceweasel:  fowles | 2008-05-08 16:43:56 -0400
 fix and test access to protected variables from within an inner class
 
 r73258@spiceweasel:  fowles | 2008-05-08 17:55:02 -0400
 add a test for accessing super class protected variables (both within and across packages)
 
 r73290@spiceweasel:  fowles | 2008-05-09 16:13:51 -0400
 implement synthetic functions to access protected variables of enclosing scopes
 tests too
 
 r73353@spiceweasel:  fowles | 2008-05-09 22:09:15 -0400
 restore import statements to be wildcards
 
 r73354@spiceweasel:  fowles | 2008-05-09 22:24:09 -0400
 remove exposing class data in SimpleCompiler, I should have been using UnitCompiler all along
 
 r73444@spiceweasel:  fowles | 2008-05-13 11:09:06 -0400
 Fix some stack badness that IndirectFieldAccess
 

=== tests/src/UnparseTests.java
==================================================================
--- tests/src/UnparseTests.java (revision 338)
+++ tests/src/UnparseTests.java (patch statementlist level 4)
@@ -0,0 +1,337 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Parser;
+import org.codehaus.janino.Scanner;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Visitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayAccessExpression;
+import org.codehaus.janino.Java.ArrayLength;
+import org.codehaus.janino.Java.Assignment;
+import org.codehaus.janino.Java.Atom;
+import org.codehaus.janino.Java.BinaryOperation;
+import org.codehaus.janino.Java.Cast;
+import org.codehaus.janino.Java.ClassLiteral;
+import org.codehaus.janino.Java.ConditionalExpression;
+import org.codehaus.janino.Java.ConstantValue;
+import org.codehaus.janino.Java.Crement;
+import org.codehaus.janino.Java.FieldAccess;
+import org.codehaus.janino.Java.FieldAccessExpression;
+import org.codehaus.janino.Java.IndirectFieldAccess;
+import org.codehaus.janino.Java.Instanceof;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableAccess;
+import org.codehaus.janino.Java.MethodInvocation;
+import org.codehaus.janino.Java.NewAnonymousClassInstance;
+import org.codehaus.janino.Java.NewArray;
+import org.codehaus.janino.Java.NewClassInstance;
+import org.codehaus.janino.Java.NewInitializedArray;
+import org.codehaus.janino.Java.ParameterAccess;
+import org.codehaus.janino.Java.ParenthesizedExpression;
+import org.codehaus.janino.Java.QualifiedThisReference;
+import org.codehaus.janino.Java.SuperclassFieldAccessExpression;
+import org.codehaus.janino.Java.SuperclassMethodInvocation;
+import org.codehaus.janino.Java.ThisReference;
+import org.codehaus.janino.Java.UnaryOperation;
+
+public class UnparseTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(UnparseTests.class.getName());
+        s.addTest(new UnparseTests("testSimple"));
+        s.addTest(new UnparseTests("testParens"));
+        s.addTest(new UnparseTests("testMany"));
+        return s;
+    }
+
+    public UnparseTests(String name) { super(name); }
+    
+    private static void helpTestExpr(String input, String expect, boolean simplify) throws Exception {
+        Parser p = new Parser(new Scanner(
+                null, new ByteArrayInputStream(input.getBytes()
+        )));
+        Atom expr = p.parseExpression();
+        if(simplify) {
+            expr = stripUnnecessaryParenExprs(expr);
+        }
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        expr.accept(uv);
+        assertEquals(expect, sw.toString());    
+    }
+    
+    private static Java.Rvalue[] stripUnnecessaryParenExprs(Java.Rvalue[] rvalues) {
+        Java.Rvalue[] res = new Java.Rvalue[rvalues.length];
+        for(int i = 0; i < res.length; ++i) {
+            res[i] = stripUnnecessaryParenExprs(rvalues[i]);
+        }
+        return res;
+    }
+    
+    private static Java.Atom stripUnnecessaryParenExprs(Java.Atom atom) {
+        if(atom instanceof Java.Rvalue) {
+            return stripUnnecessaryParenExprs((Java.Rvalue)atom);
+        }
+        return atom;
+    }
+    
+    private static Java.Lvalue stripUnnecessaryParenExprs(Java.Lvalue lvalue) {
+        return (Java.Lvalue)stripUnnecessaryParenExprs((Java.Rvalue)lvalue);
+    }
+    
+    private static Java.Rvalue stripUnnecessaryParenExprs(Java.Rvalue rvalue) {
+        if(rvalue == null) { return null; }
+        final Java.Rvalue[] res = new Java.Rvalue[1];
+        Visitor.RvalueVisitor rv = new Visitor.RvalueVisitor() {
+            public void visitArrayLength(ArrayLength al) {
+                res[0] = new Java.ArrayLength(al.getLocation(),
+                        stripUnnecessaryParenExprs(al.lhs));
+            }
+
+            public void visitAssignment(Assignment a) {
+                res[0] = new Java.Assignment(a.getLocation(),
+                        stripUnnecessaryParenExprs(a.lhs),
+                        a.operator,
+                        stripUnnecessaryParenExprs(a.rhs)
+                );
+            }
+
+            public void visitBinaryOperation(BinaryOperation bo) {
+                res[0] = new Java.BinaryOperation(bo.getLocation(),
+                        stripUnnecessaryParenExprs(bo.lhs),
+                        bo.op,
+                        stripUnnecessaryParenExprs(bo.rhs)
+                );
+            }
+
+            public void visitCast(Cast c) {
+                res[0] = new Java.Cast(c.getLocation(),
+                        c.targetType,
+                        stripUnnecessaryParenExprs(c.value));
+            }
+
+            public void visitClassLiteral(ClassLiteral cl) {
+                res[0] = cl; //too much effort
+            }
+
+            public void visitConditionalExpression(ConditionalExpression ce) {
+                res[0] = new Java.ConditionalExpression(ce.getLocation(),
+                        stripUnnecessaryParenExprs(ce.lhs),
+                        stripUnnecessaryParenExprs(ce.mhs),
+                        stripUnnecessaryParenExprs(ce.rhs));
+            }
+
+            public void visitConstantValue(ConstantValue cv) {
+                res[0] = cv;
+            }
+
+            public void visitCrement(Crement c) {
+                if(c.pre) {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            c.operator,
+                            stripUnnecessaryParenExprs(c.operand));
+                } else {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            stripUnnecessaryParenExprs(c.operand),
+                            c.operator);
+                }
+            }
+
+            public void visitInstanceof(Instanceof io) {
+                res[0] = new Java.Instanceof(io.getLocation(),
+                        stripUnnecessaryParenExprs(io.lhs),
+                        io.rhs);
+            }
+
+            public void visitLiteral(Literal l) {
+                res[0] = l;
+            }
+
+            public void visitMethodInvocation(MethodInvocation mi) {
+                res[0] = new Java.MethodInvocation(mi.getLocation(),
+                        stripUnnecessaryParenExprs(mi.optionalTarget),
+                        mi.methodName,
+                        stripUnnecessaryParenExprs(mi.arguments));
+            }
+
+            public void visitNewAnonymousClassInstance(
+                    NewAnonymousClassInstance naci) {
+                res[0] = naci; //too much effort
+            }
+
+            public void visitNewArray(NewArray na) {
+                res[0] = new Java.NewArray(na.getLocation(),
+                        na.type,
+                        stripUnnecessaryParenExprs(na.dimExprs),
+                        na.dims);
+            }
+
+            public void visitNewClassInstance(NewClassInstance nci) {
+                res[0] = new Java.NewClassInstance(nci.getLocation(),
+                        stripUnnecessaryParenExprs(nci.optionalQualification),
+                        nci.type,
+                        stripUnnecessaryParenExprs(nci.arguments));
+            }
+
+            public void visitNewInitializedArray(NewInitializedArray nia) {
+                res[0] = nia; //too much effort
+            }
+
+            public void visitParameterAccess(ParameterAccess pa) {
+                res[0] = pa;
+            }
+
+            public void visitQualifiedThisReference(QualifiedThisReference qtr) {
+                res[0] = qtr;
+            }
+
+            public void visitSuperclassMethodInvocation(
+                    SuperclassMethodInvocation smi) {
+                res[0] = new Java.SuperclassMethodInvocation(smi.getLocation(),
+                        smi.methodName,
+                        stripUnnecessaryParenExprs(smi.arguments));
+            }
+
+            public void visitThisReference(ThisReference tr) {
+                res[0] = tr;
+            }
+
+            public void visitUnaryOperation(UnaryOperation uo) {
+                res[0] = new Java.UnaryOperation(uo.getLocation(),
+                        uo.operator,
+                        stripUnnecessaryParenExprs(uo.operand));
+            }
+
+            public void visitAmbiguousName(AmbiguousName an) {
+                res[0] = an;
+            }
+
+            public void visitArrayAccessExpression(ArrayAccessExpression aae) {
+                res[0] = new Java.ArrayAccessExpression(aae.getLocation(),
+                        stripUnnecessaryParenExprs(aae.lhs),
+                        stripUnnecessaryParenExprs(aae.index));
+            }
+
+            public void visitFieldAccess(FieldAccess fa) {
+                res[0] = new Java.FieldAccess(fa.getLocation(),
+                        stripUnnecessaryParenExprs(fa.lhs),
+                        fa.field);
+            }
+
+            public void visitFieldAccessExpression(FieldAccessExpression fae) {
+                res[0] = new Java.FieldAccessExpression(fae.getLocation(),
+                        stripUnnecessaryParenExprs(fae.lhs),
+                        fae.fieldName);
+            }
+
+            public void visitLocalVariableAccess(LocalVariableAccess lva) {
+                res[0] = lva;
+            }
+
+            public void visitParenthesizedExpression(ParenthesizedExpression pe) {
+                res[0] = stripUnnecessaryParenExprs(pe.value);
+            }
+
+            public void visitSuperclassFieldAccessExpression(
+                    SuperclassFieldAccessExpression scfae) {
+                res[0] = scfae;
+            }
+
+            public void visitIndirectFieldAccess(IndirectFieldAccess fa) {
+                res[0] = fa;
+            }
+            
+        };
+        rvalue.accept(rv);
+        return res[0];
+    }
+    
+    public void testSimple() throws Exception {
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", false);
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", true);
+    }
+    
+    public void testParens() throws Exception {
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", false);
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", true);
+    }
+    
+    public void testMany() throws Exception {
+        final String[][] exprs = new String[][] {
+              //input                                  expected simplified                    expect non-simplified
+            { "((1)+2)",                               "1 + 2",                               "((1) + 2)"           },
+            { "1 + 2 * 3",                             null,                                  null                  },
+            { "1 + (2 * 3)",                           "1 + 2 * 3",                           null                  },
+            { "3 - (2 - 1)",                           null,                                  null                  },
+            { "true ? 1 : 2",                          null,                                  null                  },
+            { "(true ? false : true) ? 1 : 2",         null,                                  null                  },
+            { "true ? false : (true ? false : true)",  "true ? false : true ? false : true",  null                  },
+            { "-(-(2))",                               "-(-2)",                               null                  },
+            { "- - 2",                                 "-(-2)",                               "-(-2)"               },
+            { "x && (y || z)",                         null,                                  null                  },
+            { "(x && y) || z",                         "x && y || z",                         null                  },
+            { "x = (y = z)",                           "x = y = z",                           null                  },
+            { "(--x) + 3",                             "--x + 3",                             null                  },
+            { "(baz.bar).foo(x, (3 + 4) * 5)",         "baz.bar.foo(x, (3 + 4) * 5)",         null                  },
+            { "!(bar instanceof Integer)",             null,                                  null                  },
+            { "(true ? foo : bar).baz()",              null,                                  null                  },
+        };
+        
+        for(int i = 0; i < exprs.length; ++i) {
+            String input = exprs[i][0];
+            String expectSimplify = exprs[i][1];
+            if(expectSimplify == null) {
+                expectSimplify = input;
+            }
+            
+            String expectNoSimplify = exprs[i][2];
+            if(expectNoSimplify == null) {
+                expectNoSimplify = input;
+            }
+            
+            helpTestExpr(input, expectSimplify, true);
+            helpTestExpr(input, expectNoSimplify, false);
+        }
+    }
+
+}
=== tests/src/EvaluatorTests.java
==================================================================
--- tests/src/EvaluatorTests.java (revision 338)
+++ tests/src/EvaluatorTests.java (patch statementlist level 4)
@@ -53,6 +53,10 @@
         s.addTest(new EvaluatorTests("testGuessParameterNames"));
         s.addTest(new EvaluatorTests("testAssertNotCooked"));
         s.addTest(new EvaluatorTests("testAccessingCompilingClass"));
+        s.addTest(new EvaluatorTests("testProtectedAccessAcrossPackages"));
+        s.addTest(new EvaluatorTests("testProtectedAccessWithinPackage"));
+        s.addTest(new EvaluatorTests("testComplicatedSyntheticAccess"));
+        s.addTest(new EvaluatorTests("testStaticInitAccessProtected"));
         return s;
     }
 
@@ -249,4 +253,153 @@
         //the above loops become empty
         assertEquals(8, numTests);
     }
+    
+    public void testProtectedAccessAcrossPackages() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
+        sc.cook("package test;\n" +
+                "public class Top extends for_sandbox_tests.ProtectedVariable {\n" +
+                "    public class Inner {\n" +
+                "        public int get() {\n" +
+                "            return var;\n" +
+                "        }\n" +
+                "    } \n" +
+                "}"
+        );
+    }
+    
+    public void testProtectedAccessWithinPackage() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
+        sc.cook("package for_sandbox_tests;\n" +
+                "public class Top extends for_sandbox_tests.ProtectedVariable {\n" +
+                "    public class Inner {\n" +
+                "        public int get() {\n" +
+                "            return var;\n" +
+                "        }\n" +
+                "        public void set() {\n" +
+                "            var += 10;\n" +
+                "        }\n" +
+                "        public int getS() {\n" +
+                "            return svar;\n" +
+                "        }\n" +
+                "        public void setS() {\n" +
+                "            svar += 10;\n" +
+                "        }\n" +
+                "    } \n" +
+                "    public Inner createInner() {\n" +
+                "        return new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("for_sandbox_tests.Top");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        Object i = createInner.invoke(t, null);
+        
+        Class innerClass = sc.getClassLoader().loadClass("for_sandbox_tests.Top$Inner");
+        Method get = innerClass.getDeclaredMethod("get", null);
+        Method getS = innerClass.getDeclaredMethod("getS", null);
+        Method set = innerClass.getDeclaredMethod("set", null);
+        Method setS = innerClass.getDeclaredMethod("setS", null);
+        
+        Object res;
+        {   // non-static
+            res = get.invoke(i, null);
+            assertEquals(Integer.valueOf(1), res);
+            set.invoke(i, null);
+            res = get.invoke(i, null);
+            assertEquals(Integer.valueOf(11), res);
+        }
+        {   //static
+            res = getS.invoke(i, null);
+            assertEquals(Integer.valueOf(2), res);
+            setS.invoke(i, null);
+            res = getS.invoke(i, null);
+            assertEquals(Integer.valueOf(12), res);
+        }
+    }
+    
+    public void testComplicatedSyntheticAccess() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
+        sc.cook("package for_sandbox_tests;\n" +
+                "public class L0 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "    public class L1 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "        public class L2 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "            public class Inner {\n" +
+                "                public int getL2() { return L0.L1.L2.this.var; }\n" +
+                "                public int getL1() { return L0.L1.this.var; }\n" +
+                "                public int getL0() { return L0.this.var; }\n" +
+                "                public int setL2() { return L2.this.var = 2; }\n" +
+                "                public int setL1() { return L1.this.var = 1; }\n" +
+                "                public int setL0() { return L0.this.var = 0; }\n" +
+                "            }\n" +
+                "        }\n" +
+                "    }\n" +
+                "    public L0.L1.L2.Inner createInner() {\n" +
+                "        return new L0().new L1().new L2().new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("for_sandbox_tests.L0");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        Object inner = createInner.invoke(t, null);
+        
+        Class innerClass = inner.getClass();
+        Method[] gets = new Method[] {
+                innerClass.getMethod("getL0", null),
+                innerClass.getMethod("getL1", null),
+                innerClass.getMethod("getL2", null),
+        };
+        Method[] sets = new Method[] {
+                innerClass.getMethod("setL0", null),
+                innerClass.getMethod("setL1", null),
+                innerClass.getMethod("setL2", null),
+        };
+        for(int i = 0; i < 3; ++i) {
+            Object g1 = gets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(1), g1);
+            Object s1 = sets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(i), s1);
+            Object g2 = gets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(i), g2);
+        }
+    }
+    
+    public void testStaticInitAccessProtected() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.cook("package test;\n" +
+                "public class Outer extends for_sandbox_tests.ProtectedVariable  {\n" +
+                "    public class Inner {\n" +
+                "        {\n" +
+                "            int t = var;\n" +
+                "            var = svar;\n" +
+                "            svar = t;\n" +
+                "        }\n" +
+                "        private final int i = var;\n" +
+                "        private final int j = svar;\n" +
+                "        {\n" +
+                "            int t = var;\n" +
+                "            var = svar;\n" +
+                "            svar = t;\n" +
+                "        }\n" +
+                "        private final int[] a = new int[] { i, j };\n" +
+                "    }\n" +
+                "    public Inner createInner() {\n" +
+                "        return new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("test.Outer");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        assertNotNull(t);
+        Object inner = createInner.invoke(t, null);
+        assertNotNull(inner);
+    }
 }
=== tests/src/for_sandbox_tests/ProtectedVariable.java
==================================================================
--- tests/src/for_sandbox_tests/ProtectedVariable.java (revision 338)
+++ tests/src/for_sandbox_tests/ProtectedVariable.java (patch statementlist level 4)
@@ -0,0 +1,6 @@
+package for_sandbox_tests;
+
+public class ProtectedVariable {
+    protected int var = 1;
+    protected static int svar = 2;
+}
=== tests/src/AstTests.java
==================================================================
--- tests/src/AstTests.java (revision 338)
+++ tests/src/AstTests.java (patch statementlist level 4)
@@ -0,0 +1,396 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.CompileException;
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Location;
+import org.codehaus.janino.Mod;
+import org.codehaus.janino.SimpleCompiler;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayType;
+import org.codehaus.janino.Java.BasicType;
+import org.codehaus.janino.Java.Block;
+import org.codehaus.janino.Java.CompilationUnit;
+import org.codehaus.janino.Java.ExpressionStatement;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableDeclarationStatement;
+import org.codehaus.janino.Java.MethodDeclarator;
+import org.codehaus.janino.Java.PackageMemberClassDeclaration;
+import org.codehaus.janino.Java.ReturnStatement;
+import org.codehaus.janino.Java.Rvalue;
+import org.codehaus.janino.Java.StatementList;
+import org.codehaus.janino.Java.Type;
+import org.codehaus.janino.Java.FunctionDeclarator.FormalParameter;
+import org.codehaus.janino.Parser.ParseException;
+import org.codehaus.janino.Scanner.ScanException;
+
+public class AstTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(AstTests.class.getName());
+        s.addTest(new AstTests("testSimpleAst"));
+        s.addTest(new AstTests("testLocalVariable"));
+        s.addTest(new AstTests("testBlock"));
+        s.addTest(new AstTests("testStatementList"));
+        s.addTest(new AstTests("testByteArrayLiteral"));
+        s.addTest(new AstTests("testClassRef"));
+        s.addTest(new AstTests("testPrecedence"));
+        return s;
+    }
+    
+    public AstTests(String name) { super(name); }
+
+    private static Object compileAndEval(CompilationUnit cu) throws CompileException,
+            ParseException, ScanException, IOException, ClassNotFoundException,
+            InstantiationException, IllegalAccessException,
+            NoSuchMethodException, InvocationTargetException {
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        
+        Class handMadeClass = loader.loadClass("HandMade");
+        
+        Object handMade = handMadeClass.newInstance();
+        Method calc = handMadeClass.getMethod("calculate", null);
+        Object res = calc.invoke(handMade, null);
+        return res;
+    }
+    
+    private static ArrayType createByteArrayType() {
+        return new Java.ArrayType(
+                new Java.BasicType(
+                        getLocation(),
+                        Java.BasicType.BYTE
+                )
+        );
+    };
+    
+    private static PackageMemberClassDeclaration createClass(CompilationUnit cu)
+            throws ParseException {
+        PackageMemberClassDeclaration clazz = new PackageMemberClassDeclaration(
+                getLocation(),
+                null,
+                Mod.PUBLIC,
+                "HandMade",
+                null,
+                new Type[]{}
+        );
+        cu.addPackageMemberTypeDeclaration(clazz);
+        return clazz;
+    }
+    
+    private static Type createDoubleType() {
+        return new BasicType(getLocation(), BasicType.DOUBLE);
+    }
+    
+    private static Java.UnaryOperation createOp(String op, Rvalue l) {
+        return new Java.UnaryOperation(getLocation(), op, l);
+    }
+    
+    private static Java.BinaryOperation createOp(Rvalue l1, String op, Rvalue l2) {
+        return new Java.BinaryOperation(getLocation(), l1, op, l2);
+    }
+    
+    private static Literal createLiteral(double d) {
+        return createLiteral(Double.valueOf(d));
+    }
+    
+    
+    private static Literal createLiteral(Object o) {
+        return new Literal( getLocation(), o );
+    }
+    
+    private static void createMethod(PackageMemberClassDeclaration clazz, Block body, Type returnType) {
+        MethodDeclarator method = new MethodDeclarator(
+                getLocation(),
+                null,
+                (short)(Mod.PUBLIC),
+                returnType,
+                "calculate",
+                new FormalParameter[0],
+                new Type[0],
+                body
+        );
+        clazz.addDeclaredMethod(method);
+    }
+        
+
+    private static LocalVariableDeclarationStatement createVarDecl(String name, double value) {
+        return new Java.LocalVariableDeclarationStatement(
+                getLocation(),
+                (short)0,
+                createDoubleType(),
+                new Java.VariableDeclarator[] {
+                    new Java.VariableDeclarator(
+                            getLocation(),
+                            name,
+                            0,
+                            createLiteral(value)
+                    )
+                }
+        );
+    }
+    
+    private static AmbiguousName createVariableRef(String name) {
+        return new Java.AmbiguousName(
+                getLocation(),
+                new String[] { name }
+        );
+    }
+
+    /**
+     * "Clever" method to get a location from a stack trace
+     */
+    static private Location getLocation() {
+        Exception e = new Exception();
+        StackTraceElement ste = e.getStackTrace()[1];//we only care about our caller
+        return new Location(
+                ste.getFileName(),
+                (short)ste.getLineNumber(),
+                (short)0
+        );
+    }
+
+    
+    public void testBlock() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        Block sub = new Block(getLocation());
+        sub.addStatement( createVarDecl("x", 2.0) );                        
+        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        try {
+            compileAndEval(cu);
+            fail("Block must limit the scope of variables in it");
+        } catch(CompileException ex) {
+            assertTrue(ex.getMessage().endsWith("Expression \"x\" is not an rvalue"));
+        }
+    }
+
+    public void testByteArrayLiteral() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Byte exp = Byte.valueOf((byte)1);
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.NewInitializedArray(
+                                getLocation(),
+                                createByteArrayType(),
+                                new Java.ArrayInitializer(
+                                        getLocation(),
+                                        new Java.Rvalue[] {
+                                            createLiteral(exp)
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                createByteArrayType()
+        );
+        
+        Object res = compileAndEval(cu);
+        assertEquals(exp.byteValue(), ((byte[])res)[0]);
+    }
+
+    public void testLocalVariable() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement( createVarDecl("x", 2.0) );                        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(6.0), res);
+    }
+    
+    public void testSimpleAst() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        createLiteral(3.0)
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertEquals(Double.valueOf(3.0), res);
+    }
+
+    public void testStatementList() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        StatementList sub = new StatementList(getLocation());
+        sub.addStatement( createVarDecl("x", 3.0) );                        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(9.0), res);
+    }
+    
+    public void testClassRef() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.ClassLiteral(
+                                getLocation(),
+                                new Java.ReferenceType(
+                                        getLocation(),
+                                        new String[] {
+                                            "HandMade"
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                new Java.ReferenceType(
+                        getLocation(),
+                        new String[] { "java", "lang", "Class" }
+                )
+        );
+        
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        Class handMadeClass = loader.loadClass("HandMade");
+        Method calc = handMadeClass.getMethod("calculate", null);
+        
+        Object handMade = handMadeClass.newInstance();
+        Object res = calc.invoke(handMade, null);
+        assertEquals(handMadeClass, res);
+    }
+    
+    public void testPrecedence() throws Exception {
+        ExpressionStatement es = new Java.ExpressionStatement(
+                new Java.Assignment(
+                        getLocation(),
+                        new Java.AmbiguousName(
+                                getLocation(),
+                                new String[] { "x" }
+                        ),
+                        "=",
+                        createOp(
+                                createLiteral(1), "*",
+                                createOp(createLiteral(2), "+", createLiteral(3))
+                        )
+                )
+        );
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        uv.visitExpressionStatement(es);
+        assertEquals("x = 1.0D * (2.0D + 3.0D);", sw.toString());
+    }
+}
=== tests/src/AllTests.java
==================================================================
--- tests/src/AllTests.java (revision 338)
+++ tests/src/AllTests.java (patch statementlist level 4)
@@ -82,5 +82,7 @@
         this.addTest(ReportedBugs.suite());
         this.addTest(SandboxTests.suite());
         this.addTest(EvaluatorTests.suite());
+        this.addTest(AstTests.suite());
+        this.addTest(UnparseTests.suite());
     }
 }
=== src/org/codehaus/janino/UnitCompiler.java
==================================================================
--- src/org/codehaus/janino/UnitCompiler.java (revision 338)
+++ src/org/codehaus/janino/UnitCompiler.java (patch statementlist level 4)
@@ -346,12 +346,12 @@
                 if (tbd.isStatic()) b.addButDontEncloseStatement((Java.BlockStatement) tbd);
             }
 
-            maybeCreateInitMethod(cd, cf, b);
+            this.maybeCreateInitMethod(cd, cf, b);
         }
 
-        compileDeclaredMethods(cd, cf);
+        this.compileDeclaredMethods(cd, cf);
 
-        
+
         // Compile declared constructors.
         // As a side effect of compiling methods and constructors, synthetic "class-dollar"
         // methods (which implement class literals) are generated on-the fly.
@@ -365,9 +365,13 @@
                 if (syntheticFieldCount != cd.syntheticFields.size()) throw new RuntimeException("SNO: Compilation of constructor \"" + cds[i] + "\" (" + cds[i].getLocation() +") added synthetic fields!?");
             }
         }
+        
+        // A side effect of this call may create synthetic functions to access
+        // protected parent variables
+        this.compileDeclaredMemberTypes(cd, cf);
 
         // Compile the aforementioned extras.
-        compileDeclaredMethods(cd, cf, declaredMethodCount);
+        this.compileDeclaredMethods(cd, cf, declaredMethodCount);
 
         // Class and instance variables.
         for (Iterator it = cd.variableDeclaratorsAndInitializers.iterator(); it.hasNext();) {
@@ -387,7 +391,6 @@
             );
         }
 
-        compileDeclaredMemberTypes(cd, cf);
 
         // Add the generated class file to a thread-local store.
         this.generatedClassFiles.add(cf);
@@ -547,17 +550,17 @@
         // Create interface initialization method iff there is any initialization code.
         if (this.generatesCode(b)) {
             Java.MethodDeclarator md = new Java.MethodDeclarator(
-                decl.getLocation(),                               // location
-                null,                                           // optionalDocComment
-                (short) (Mod.STATIC | Mod.PUBLIC),              // modifiers
-                new Java.BasicType(                             // type
-                    decl.getLocation(),
-                    Java.BasicType.VOID
-                ),
-                "<clinit>",                                     // name
-                new Java.FunctionDeclarator.FormalParameter[0], // formalParameters
-                new Java.ReferenceType[0],                      // thrownExcaptions
-                b                                               // optionalBody
+                    decl.getLocation(),                               // location
+                    null,                                           // optionalDocComment
+                    (short) (Mod.STATIC | Mod.PUBLIC),              // modifiers
+                    new Java.BasicType(                             // type
+                            decl.getLocation(),
+                            Java.BasicType.VOID
+                    ),
+                    "<clinit>",                                     // name
+                    new Java.FunctionDeclarator.FormalParameter[0], // formalParameters
+                    new Java.ReferenceType[0],                      // thrownExcaptions
+                    b                                               // optionalBody
             );
             md.setDeclaringType(decl);
             this.compile(md, cf);
@@ -590,7 +593,7 @@
             ));
         }
     }
-    
+
     /**
      * Compile all of the methods for this declaration
      * <p>
@@ -643,10 +646,11 @@
             public void visitThrowStatement                   (Java.ThrowStatement                    ts  ) { try { res[0] = UnitCompiler.this.compile2(ts  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitBreakStatement                   (Java.BreakStatement                    bs  ) { try { res[0] = UnitCompiler.this.compile2(bs  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitContinueStatement                (Java.ContinueStatement                 cs  ) { try { res[0] = UnitCompiler.this.compile2(cs  ); } catch (CompileException e) { throw new UCE(e); } }
-            public void visitEmptyStatement                   (Java.EmptyStatement                    es  ) {       res[0] = UnitCompiler.this.compile2(es  );                                                                }
+            public void visitEmptyStatement                   (Java.EmptyStatement                    es  ) {       res[0] = UnitCompiler.this.compile2(es  );                                                    }
             public void visitLocalClassDeclarationStatement   (Java.LocalClassDeclarationStatement    lcds) { try { res[0] = UnitCompiler.this.compile2(lcds); } catch (CompileException e) { throw new UCE(e); } }
             public void visitAlternateConstructorInvocation   (Java.AlternateConstructorInvocation    aci ) { try { res[0] = UnitCompiler.this.compile2(aci ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperConstructorInvocation       (Java.SuperConstructorInvocation        sci ) { try { res[0] = UnitCompiler.this.compile2(sci ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (StatementList                          sl  ) { try { res[0] = UnitCompiler.this.compile2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
         };
         try {
             bs.accept(bsv);
@@ -658,13 +662,14 @@
     private boolean compile2(Java.Initializer i) throws CompileException {
         return this.compile(i.block);
     }
-    private boolean compile2(Java.Block b) throws CompileException {
+    //takes a List<BlockStatement>
+    private boolean compile2ListStatement(List l) throws CompileException {
         this.codeContext.saveLocalVariables();
         try {
             boolean previousStatementCanCompleteNormally = true;
-            for (int i = 0; i < b.statements.size(); ++i) {
-                Java.BlockStatement bs = (Java.BlockStatement) b.statements.get(i);
-                if (!previousStatementCanCompleteNormally) {
+            for (int i = 0; i < l.size(); ++i) {
+                Java.BlockStatement bs = (Java.BlockStatement) l.get(i);
+                if (!previousStatementCanCompleteNormally && this.generatesCode(bs)) {
                     this.compileError("Statement is unreachable", bs.getLocation());
                     break;
                 }
@@ -675,6 +680,12 @@
             this.codeContext.restoreLocalVariables();
         }
     }
+    private boolean compile2(Java.Block b) throws CompileException {
+        return compile2ListStatement(b.statements);
+    }
+    private boolean compile2(Java.StatementList sl) throws CompileException {
+        return compile2ListStatement(sl.statements);
+    }
     private boolean compile2(Java.DoStatement ds) throws CompileException {
         Object cvc = this.getConstantValue(ds.condition);
         if (cvc != null) {
@@ -1667,6 +1678,9 @@
 
             // Compile the function body.
             try {
+                if(fd.optionalBody == null) {
+                    this.compileError("Method must have a body", fd.getLocation());
+                }
                 boolean canCompleteNormally = this.compile(fd.optionalBody);
                 if (canCompleteNormally) {
                     if (this.getReturnType(fd) != IClass.VOID) this.compileError("Method must return a value", fd.getLocation());
@@ -1770,6 +1784,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName              an   ) { try { UnitCompiler.this.compile2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression      aae  ) { try { UnitCompiler.this.compile2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                fa   ) { try { UnitCompiler.this.compile2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess        ifa  ) { try { UnitCompiler.this.compile2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression      fae  ) { try { UnitCompiler.this.compile2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compile2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess        lva  ) { try { UnitCompiler.this.compile2(lva  ); } catch (CompileException e) { throw new UCE(e); } }
@@ -1953,6 +1968,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { UnitCompiler.this.compileBoolean2(an   , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { UnitCompiler.this.compileBoolean2(aae  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { UnitCompiler.this.compileBoolean2(fa   , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { UnitCompiler.this.compileBoolean2(ifa  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { UnitCompiler.this.compileBoolean2(fae  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compileBoolean2(scfae, dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) { try { UnitCompiler.this.compileBoolean2(lva  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
@@ -2194,6 +2210,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.compileContext2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.compileContext2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.compileContext2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.compileContext2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.compileContext2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.compileContext2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.compileContext2(lva  );                                                                }
@@ -2221,6 +2238,15 @@
             return 1;
         }
     }
+    private int compileContext2(Java.IndirectFieldAccess fa) throws CompileException {
+        if (fa.field.isStatic()) {
+            this.getType(this.toTypeOrCE(fa.lhs));
+            return 0;
+        } else {
+            this.compileGetValue(this.toRvalueOrCE(fa.lhs));
+            return 1;
+        }
+    }
     private int compileContext2(Java.ArrayLength al) throws CompileException {
         if (!this.compileGetValue(al.lhs).isArray()) this.compileError("Cannot determine length of non-array type", al.getLocation());
         return 1;
@@ -2289,6 +2315,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.compileGet2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.compileGet2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.compileGet2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.compileGet2(ifa   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.compileGet2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.compileGet2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.compileGet2(lva  );                                                                }
@@ -2319,6 +2346,30 @@
     private IClass compileGet2(Java.LocalVariableAccess lva) {
         return this.load((Locatable) lva, lva.localVariable);
     }
+    private IClass compileGet2(Java.IndirectFieldAccess fa) throws CompileException {
+        String syntheticMethodName = "get$" + fa.field.getName();
+        AbstractTypeDeclaration type = fa.typeDeclaration;
+        
+        //create the accessor if its here yet
+        Java.MethodDeclarator method = type.getMethodDeclaration(syntheticMethodName);
+        if(method == null) {
+            this.declareIndirectGetMethod(fa);
+            method = type.getMethodDeclaration(syntheticMethodName);
+        }
+        
+
+        // the class or instance will already be on the stack as appropriate
+        // so just invoke the method we generated
+        IClass.IMethod iMethod = this.toIMethod(method);
+        
+        this.writeOpcode(fa, Opcode.INVOKESTATIC);
+        this.writeConstantMethodrefInfo(
+            iMethod.getDeclaringIClass().getDescriptor(), // classFD
+            iMethod.getName(),                            // methodName
+            iMethod.getDescriptor()                       // methodMD
+        );
+        return fa.field.getType();
+    }
     private IClass compileGet2(Java.FieldAccess fa) throws CompileException {
         this.checkAccessible(fa.field, fa.getEnclosingBlockStatement());
         if (fa.field.isStatic()) {
@@ -2393,17 +2444,7 @@
 
         // Check if synthetic method "static Class class$(String className)" is already
         // declared.
-        boolean classDollarMethodDeclared = false;
-        {
-            for (Iterator it = declaringType.declaredMethods.iterator(); it.hasNext();) {
-                Java.MethodDeclarator md = (Java.MethodDeclarator) it.next();
-                if (md.name.equals("class$")) {
-                    classDollarMethodDeclared = true;
-                    break;
-                }
-            }
-        }
-        if (!classDollarMethodDeclared) this.declareClassDollarMethod(cl);
+        if (declaringType.getMethodDeclaration("class$") == null) this.declareClassDollarMethod(cl);
 
         // Determine the statics of the declaring class (this is where static fields
         // declarations are found).
@@ -3288,6 +3329,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.getConstantValue2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.getConstantValue2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.getConstantValue2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.getConstantValue2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.getConstantValue2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.getConstantValue2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getConstantValue2(lva  );                                                    }
@@ -3310,6 +3352,9 @@
     private Object getConstantValue2(Java.FieldAccess fa) throws CompileException {
         return fa.field.getConstantValue();
     }
+    private Object getConstantValue2(Java.IndirectFieldAccess ifa) throws CompileException {
+        return ifa.field.getConstantValue();
+    }
     private Object getConstantValue2(Java.UnaryOperation uo) throws CompileException {
         if (uo.operator.equals("+")) return this.getConstantValue(uo.operand);
         if (uo.operator.equals("-")) return this.getNegatedConstantValue(uo.operand);
@@ -3506,6 +3551,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(an   );                                                    }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(fa   );                                                    }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(ifa  );                                                    }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(lva  );                                                    }
@@ -3555,6 +3601,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { try { res[0] = UnitCompiler.this.generatesCode2(fd  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) {       res[0] = UnitCompiler.this.generatesCode2(ls  );                                                                }
             public void visitBlock                            (Java.Block                             b   ) { try { res[0] = UnitCompiler.this.generatesCode2(b   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { try { res[0] = UnitCompiler.this.generatesCode2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) {       res[0] = UnitCompiler.this.generatesCode2(es  );                                                                }
             public void visitIfStatement                      (Java.IfStatement                       is  ) {       res[0] = UnitCompiler.this.generatesCode2(is  );                                                                }
             public void visitForStatement                     (Java.ForStatement                      fs  ) {       res[0] = UnitCompiler.this.generatesCode2(fs  );                                                                }
@@ -3584,12 +3631,19 @@
     public boolean generatesCode2(Java.EmptyStatement es) { return false; }
     public boolean generatesCode2(Java.LocalClassDeclarationStatement lcds) { return false; }
     public boolean generatesCode2(Java.Initializer i) throws CompileException { return this.generatesCode(i.block); }
-    public boolean generatesCode2(Java.Block b) throws CompileException {
-        for (int i = 0; i < b.statements.size(); ++i) {
-            if (this.generatesCode(((Java.BlockStatement) b.statements.get(i)))) return true;
+    //takes a List<Java.BlockStatement>
+    public boolean generatesCode2ListStatements(List l) throws CompileException {
+        for (int i = 0; i < l.size(); ++i) {
+            if (this.generatesCode(((Java.BlockStatement) l.get(i)))) return true;
         }
         return false;
     }
+    public boolean generatesCode2(Java.Block b) throws CompileException {
+        return generatesCode2ListStatements(b.statements);
+    }
+    public boolean generatesCode2(Java.StatementList sl) throws CompileException {
+        return generatesCode2ListStatements(sl.statements);
+    }
     public boolean generatesCode2(Java.FieldDeclaration fd) throws CompileException {
         // Code is only generated if at least one of the declared variables has a
         // non-constant-final initializer.
@@ -3622,6 +3676,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { UnitCompiler.this.leave2(fd,   optionalStackValueType); }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) { UnitCompiler.this.leave2(ls,   optionalStackValueType); }
             public void visitBlock                            (Java.Block                             b   ) { UnitCompiler.this.leave2(b,    optionalStackValueType); }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { UnitCompiler.this.leave2(sl,   optionalStackValueType); }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) { UnitCompiler.this.leave2(es,   optionalStackValueType); }
             public void visitIfStatement                      (Java.IfStatement                       is  ) { UnitCompiler.this.leave2(is,   optionalStackValueType); }
             public void visitForStatement                     (Java.ForStatement                      fs  ) { UnitCompiler.this.leave2(fs,   optionalStackValueType); }
@@ -3687,6 +3742,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { UnitCompiler.this.compileSet2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { UnitCompiler.this.compileSet2(aae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { UnitCompiler.this.compileSet2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { UnitCompiler.this.compileSet2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { UnitCompiler.this.compileSet2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compileSet2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       UnitCompiler.this.compileSet2(lva  );                                                    }
@@ -3721,6 +3777,32 @@
             fa.field.getDescriptor()                       // fieldFD
         );
     }
+    private void compileSet2(Java.IndirectFieldAccess fa) throws CompileException {
+        String syntheticMethodName = "set$" + fa.field.getName();
+        AbstractTypeDeclaration type = fa.typeDeclaration;
+        
+        //create the accessor if its here yet
+        Java.MethodDeclarator method = type.getMethodDeclaration(syntheticMethodName);
+        if(method == null) {
+            this.declareIndirectSetMethod(fa);
+            method = type.getMethodDeclaration(syntheticMethodName);
+        }
+        
+        // the value we wish to assign is already on the stack,
+        // so we just need to put our new value in place
+        // and then invoke the method we generated
+        IClass.IMethod iMethod = this.toIMethod(method);
+        if(!fa.field.isStatic()) {
+            compileGet(this.toRvalueOrCE(fa.lhs));
+        }
+        
+        this.writeOpcode(fa, Opcode.INVOKESTATIC);
+        this.writeConstantMethodrefInfo(
+            iMethod.getDeclaringIClass().getDescriptor(), // classFD
+            iMethod.getName(),                            // methodName
+            iMethod.getDescriptor()                       // methodMD
+        );
+    }
     private void compileSet2(Java.ArrayAccessExpression aae) throws CompileException {
         this.writeOpcode(aae, Opcode.IASTORE + UnitCompiler.ilfdabcs(this.getType(aae)));
     }
@@ -3775,6 +3857,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.getType2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.getType2(aae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.getType2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.getType2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.getType2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.getType2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getType2(lva  );                                                                }
@@ -3910,7 +3993,7 @@
                 String className = Java.join(rt.identifiers, ".");
                 IClass result = findClassByName(rt.getLocation(), className);
                 if (result != null) return result;
-
+                
                 this.compileError("Class \"" + className + "\" not found", rt.getLocation());
                 return this.iClassLoader.OBJECT;
             }
@@ -3928,7 +4011,7 @@
             return this.iClassLoader.OBJECT;
         }
     }
-
+    
     private IClass getType2(Java.RvalueMemberType rvmt) throws CompileException {
         IClass rvt = this.getType(rvmt.rvalue);
         IClass memberType = this.findMemberType(rvt, rvmt.identifier, rvmt.getLocation());
@@ -3951,6 +4034,9 @@
     private IClass getType2(Java.FieldAccess fa) throws CompileException {
         return fa.field.getType();
     }
+    private IClass getType2(Java.IndirectFieldAccess ifa) throws CompileException {
+        return ifa.field.getType();
+    }
     private IClass getType2(Java.ArrayLength al) {
         return IClass.INT;
     }
@@ -4147,6 +4233,7 @@
         return this.getType(nia.arrayType);
     }
     private IClass getType2(Java.Literal l) {
+        if (l.value instanceof Byte     ) return IClass.BYTE;
         if (l.value instanceof Integer  ) return IClass.INT;
         if (l.value instanceof Long     ) return IClass.LONG;
         if (l.value instanceof Float    ) return IClass.FLOAT;
@@ -4159,6 +4246,7 @@
     }
     private IClass getType2(Java.ConstantValue cv) {
         IClass res = (
+            cv.constantValue instanceof Byte               ? IClass.BYTE    :
             cv.constantValue instanceof Integer            ? IClass.INT     :
             cv.constantValue instanceof Long               ? IClass.LONG    :
             cv.constantValue instanceof Float              ? IClass.FLOAT   :
@@ -4212,6 +4300,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.isType2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.isType2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) {       res[0] = UnitCompiler.this.isType2(fa   );                                                    }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) {       res[0] = UnitCompiler.this.isType2(ifa  );                                                    }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.isType2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.isType2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.isType2(lva  );                                                    }
@@ -4343,11 +4432,16 @@
         // At this point, the member is PROTECTED accessible.
 
         // Check whether the class declaring the context block statement is a subclass of the
-        // class declaring the member.
-        if (!iClassDeclaringMember.isAssignableFrom(iClassDeclaringContextBlockStatement)) {
-            return "Protected member cannot be accessed from type \"" + iClassDeclaringContextBlockStatement + "\", which is neither declared in the same package as nor is a subclass of \"" + iClassDeclaringMember + "\".";
-        }
-        return null;
+        // class declaring the member or a nested class whose parent is a subclass
+        IClass parentClass = iClassDeclaringContextBlockStatement;
+        do {
+            if (iClassDeclaringMember.isAssignableFrom(parentClass)) {
+                return null;
+            }
+            parentClass = parentClass.getOuterIClass();
+        } while(parentClass != null);
+        
+        return "Protected member cannot be accessed from type \"" + iClassDeclaringContextBlockStatement + "\", which is neither declared in the same package as nor is a subclass of \"" + iClassDeclaringMember + "\".";
     }
 
     /**
@@ -5311,7 +5405,8 @@
         for (Java.Scope s = scope; !(s instanceof Java.CompilationUnit); s = s.getEnclosingScope()) {
             if (s instanceof Java.BlockStatement && enclosingBlockStatement == null) enclosingBlockStatement = (Java.BlockStatement) s;
             if (s instanceof Java.TypeDeclaration) {
-                final IClass etd = UnitCompiler.this.resolve((Java.AbstractTypeDeclaration) s);
+                final Java.AbstractTypeDeclaration enclosingTypeDecl = (Java.AbstractTypeDeclaration)s;
+                final IClass etd = UnitCompiler.this.resolve(enclosingTypeDecl);
                 final IClass.IField f = this.findIField(etd, identifier, location);
                 if (f != null) {
                     if (f.isStatic()) {
@@ -5323,7 +5418,7 @@
                         this.warning("IANSFEI", "Implicit access to non-static field \"" + identifier + "\" of enclosing instance (better write \"" + f.getDeclaringIClass() + ".this." + f.getName() + "\")", location);
                     }
 
-                    Java.Type ct = new Java.SimpleType(scopeTypeDeclaration.getLocation(), (IClass) etd);
+                    Java.SimpleType ct = new Java.SimpleType(scopeTypeDeclaration.getLocation(), (IClass) etd);
                     Java.Atom lhs;
                     if (scopeTBD.isStatic()) {
 
@@ -5343,13 +5438,29 @@
                             lhs = new Java.QualifiedThisReference(location, ct);
                         }
                     }
-                    Java.FieldAccess fa = new Java.FieldAccess(
-                        location,
-                        lhs,
-                        f
-                    );
-                    fa.setEnclosingBlockStatement(enclosingBlockStatement);
-                    return fa;
+                    Java.Rvalue res;
+                    
+                    // accessing an enclosing class's parent's protected variable
+                    // requires an indirect access, seems simple enough ;-)
+                    if(f.getAccess() == Access.PROTECTED
+                            && f.getDeclaringIClass() != etd
+                            && scopeTypeDeclaration != enclosingTypeDecl
+                    ) {
+                        res = new Java.IndirectFieldAccess(
+                                location,
+                                lhs,
+                                f,
+                                enclosingTypeDecl
+                        );
+                    } else {
+                        res = new Java.FieldAccess(
+                                location,
+                                lhs,
+                                f
+                        );
+                    }
+                    res.setEnclosingBlockStatement(enclosingBlockStatement);
+                    return res;
                 }
             }
         }
@@ -5465,6 +5576,27 @@
     }
 
     /**
+     * Check to see if a local variable was declared in this statement.  Possibly recursing into a StatementList, as needed.
+     * @param varName  The name of the variable to find
+     * @param stmt     The statement in question
+     * @return         A local variable definition if found.  null if not found
+     * @throws CompileException
+     */
+    private Java.LocalVariable findLocalVariableInStatement(Java.BlockStatement stmt, String varName) throws CompileException {
+        if (stmt instanceof Java.LocalVariableDeclarationStatement) {
+            return this.findLocalVariable((Java.LocalVariableDeclarationStatement)stmt, varName);
+        }
+        if (stmt instanceof Java.StatementList) {
+            Java.StatementList sl = (Java.StatementList)stmt;
+            for (Iterator it = sl.statements.iterator(); it.hasNext();) {
+                Java.LocalVariable lv = findLocalVariableInStatement((Java.BlockStatement)it.next(), varName);
+                if (lv != null) return lv;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Find a local variable declared by the given <code>blockStatement</code> or any enclosing
      * scope up to the {@link Java.FunctionDeclarator}.
      */
@@ -5477,19 +5609,15 @@
             {
                 if (s instanceof Java.ForStatement) {
                     Java.BlockStatement optionalForInit = ((Java.ForStatement) s).optionalInit;
-                    if (optionalForInit instanceof Java.LocalVariableDeclarationStatement) {
-                        Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) optionalForInit, name);
+                    Java.LocalVariable lv = this.findLocalVariableInStatement(optionalForInit, name);
                         if (lv != null) return lv;
                     }
-                }
                 if (es instanceof Java.Block) {
                     Java.Block b = (Java.Block) es;
                     for (Iterator it = b.statements.iterator();;) {
                         Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                        if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                            Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                        Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                             if (lv != null) return lv;
-                        }
                         if (bs2 == s) break;
                     }
                 }
@@ -5499,10 +5627,8 @@
                         Java.SwitchStatement.SwitchBlockStatementGroup sbgs = (Java.SwitchStatement.SwitchBlockStatementGroup) it2.next();
                         for (Iterator it = sbgs.blockStatements.iterator(); it.hasNext();) {
                             Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                            if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                                Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                            Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                                 if (lv != null) return lv;
-                            }
                             if (bs2 == s) break SBSGS;
                         }
                     }
@@ -5539,6 +5665,30 @@
         }
         return null;
     }
+    
+    private Java.AbstractTypeDeclaration findTypeDeclaration(Java.Rvalue rvalue) {
+        Java.Scope s = rvalue.getEnclosingBlockStatement().getEnclosingScope();
+        for(; !(s instanceof Java.CompilationUnit); s = s.getEnclosingScope()) {
+            if(s instanceof Java.AbstractTypeDeclaration) {
+                return (Java.AbstractTypeDeclaration)s;
+            }
+        }
+        return null;
+    }
+    
+    private Java.AbstractTypeDeclaration findTypeDeclaration(IClass iclass) {
+        List types = new ArrayList(); // Java.AbstractTypeDeclaration
+        Java.CompilationUnit cu = this.compilationUnit;
+        types.addAll(cu.packageMemberTypeDeclarations);
+        
+        for(int i = 0; i < types.size(); ++i) {
+            Java.AbstractTypeDeclaration td = (Java.AbstractTypeDeclaration) types.get(i);
+            IClass option = this.resolve(td);
+            if(option == iclass) { return td; }
+            types.addAll(td.getMemberTypeDeclarations());
+        }
+        return null;
+    }
 
     private void determineValue(Java.FieldAccessExpression fae) throws CompileException {
         if (fae.value != null) return;
@@ -5562,11 +5712,28 @@
                 };
                 return;
             }
-            fae.value = new Java.FieldAccess(
-                fae.getLocation(),
-                fae.lhs,
-                iField
-            );
+
+            // accessing an enclosing class's parent's protected variable
+            // requires an indirect access, seems simple enough ;-)
+            Java.AbstractTypeDeclaration scopeClass = this.findTypeDeclaration(fae);
+            Java.AbstractTypeDeclaration enclosingTypeDecl = this.findTypeDeclaration(lhsType);
+            if(iField.getAccess() == Access.PROTECTED
+                    && iField.getDeclaringIClass() != lhsType
+                    && scopeClass != enclosingTypeDecl
+            ) {
+                fae.value = new Java.IndirectFieldAccess(
+                        fae.getLocation(),
+                        fae.lhs,
+                        iField,
+                        enclosingTypeDecl
+                );
+            } else {
+                fae.value = new Java.FieldAccess(
+                        fae.getLocation(),
+                        fae.lhs,
+                        iField
+                );
+            }
         }
         fae.value.setEnclosingBlockStatement(fae.getEnclosingBlockStatement());
     }
@@ -6660,6 +6827,147 @@
         return importedClass;
     }
     private final Map onDemandImportableTypes = new HashMap();   // String simpleTypeName => IClass
+    private void declareIndirectSetMethod(Java.IndirectFieldAccess ifa) throws CompileException {
+        // Method "set$XXX" is not yet declared; declare it like
+        //
+        // static field access looks like:
+        //   static FIELD_TYPE set$var(FIELD_TYPE value) {
+        //       return Class.var = value;
+        //   }
+        //
+        // non-static field access looks like:
+        //   static FIELD_TYPE set$var(FIELD_TYPE value, Class arg) {
+        //       return arg.var = value;
+        //   }
+        Location loc = ifa.getLocation();
+        
+        Java.Lvalue accessExpr;
+        Java.FunctionDeclarator.FormalParameter[] params;
+        Java.FunctionDeclarator.FormalParameter newValueParam = new Java.FunctionDeclarator.FormalParameter(
+                loc,
+                false,
+                new Java.SimpleType(loc, ifa.field.getType()),
+                "value"
+        );
+        Java.SimpleType classType = new Java.SimpleType(loc, ifa.typeDeclaration.resolvedType);
+        if(ifa.field.isStatic()) {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    newValueParam
+            };
+            
+            accessExpr = new Java.FieldAccessExpression(
+                    loc,
+                    classType,
+                    ifa.field.getName()
+            );
+        } else {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    newValueParam,
+                    new Java.FunctionDeclarator.FormalParameter(
+                            loc,
+                            false,
+                            classType,
+                            "parentClass"
+                    ),
+            };
+            accessExpr = new Java.AmbiguousName(
+                    loc,
+                    new String[] { "parentClass", ifa.field.getName() }
+            );
+        }
+        
+        Java.ReturnStatement ret = new Java.ReturnStatement(
+                loc,
+                new Java.Assignment(
+                        loc,
+                        accessExpr,
+                        "=",
+                        new Java.AmbiguousName(
+                                loc,
+                                new String[] { "value" }
+                        )
+                )
+        );
+        Java.Block body = new Java.Block(loc);
+        body.addStatement(ret);
+        
+        Java.MethodDeclarator method = new Java.MethodDeclarator(
+                loc,                                                  // location
+                null,                                                 // optionalDocComment
+                Mod.STATIC,                                           // modifiers
+                new Java.SimpleType(loc, ifa.field.getType()),        // type
+                "set$"+ifa.field.getName(),                           // name
+                params,                                               // formalPameters
+                new Java.Type[0],                                     // thrownExceptions
+                body                                                  // optionalBody
+        );
+        
+        ifa.typeDeclaration.addDeclaredMethod(method);
+        ifa.typeDeclaration.invalidateMethodCaches();
+    }
+    
+    private void declareIndirectGetMethod(Java.IndirectFieldAccess ifa) throws CompileException {
+        // Method "get$XXX" is not yet declared; declare it like
+        //
+        // static field access looks like:
+        //   static FIELD_TYPE get$var() {
+        //       return Class.var;
+        //   }
+        //
+        // non-static field access looks like:
+        //   static FIELD_TYPE get$var(Class arg) {
+        //       return arg.var;
+        //   }
+        Location loc = ifa.getLocation();
+        
+        Java.Rvalue getExpr;
+        Java.FunctionDeclarator.FormalParameter[] params;
+        if(ifa.field.isStatic()) {
+            params = new Java.FunctionDeclarator.FormalParameter[0];
+            getExpr = new Java.FieldAccessExpression(
+                    loc,
+                    new Java.SimpleType(
+                            loc,
+                            ifa.typeDeclaration.resolvedType
+                    ),
+                    ifa.field.getName()
+            );
+        } else {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    new Java.FunctionDeclarator.FormalParameter(
+                            loc,
+                            false,
+                            new Java.SimpleType(
+                                    loc,
+                                    ifa.typeDeclaration.resolvedType
+                            ),
+                            "parentClass"
+                    )
+            };
+            getExpr = new Java.AmbiguousName(
+                    loc,
+                    new String[] { "parentClass", ifa.field.getName() }
+            );
+        }
+        
+        Java.ReturnStatement ret = new Java.ReturnStatement(loc, getExpr);
+        Java.Block body = new Java.Block(loc);
+        body.addStatement(ret);
+        
+        Java.MethodDeclarator method = new Java.MethodDeclarator(
+                loc,                                                  // location
+                null,                                                 // optionalDocComment
+                Mod.STATIC,                                           // modifiers
+                new Java.SimpleType(loc, ifa.field.getType()),        // type
+                "get$"+ifa.field.getName(),                           // name
+                params,                                               // formalPameters
+                new Java.Type[0],                                     // thrownExceptions
+                body                                                  // optionalBody
+        );
+        
+        ifa.typeDeclaration.addDeclaredMethod(method);
+        ifa.typeDeclaration.invalidateMethodCaches();
+    }
 
     private void declareClassDollarMethod(Java.ClassLiteral cl) {
 
@@ -6766,12 +7074,7 @@
         );
 
         declaringType.addDeclaredMethod(cdmd);
-
-        // Invalidate several caches.
-        if (declaringType.resolvedType != null) {
-            declaringType.resolvedType.declaredIMethods = null;
-            declaringType.resolvedType.declaredIMethodCache = null;
-        }
+        declaringType.invalidateMethodCaches();
     }
 
     private IClass pushConstant(Locatable l, Object value) {
=== src/org/codehaus/janino/Java.java
==================================================================
--- src/org/codehaus/janino/Java.java (revision 338)
+++ src/org/codehaus/janino/Java.java (patch statementlist level 4)
@@ -36,6 +36,9 @@
 
 import java.util.*;
 
+import org.codehaus.janino.Visitor.AtomVisitor;
+import org.codehaus.janino.Visitor.LvalueVisitor;
+import org.codehaus.janino.Visitor.RvalueVisitor;
 import org.codehaus.janino.util.*;
 import org.codehaus.janino.util.iterator.*;
 
@@ -77,6 +80,7 @@
         private final Location location;
 
         protected Located(Location location) {
+            //assert location != null;
             this.location = location;
         }
 
@@ -353,6 +357,13 @@
             this.declaredMethods.add(method);
             method.setDeclaringType(this);
         }
+        
+        public void invalidateMethodCaches() {
+            if (this.resolvedType != null) {
+                this.resolvedType.declaredIMethods = null;
+                this.resolvedType.declaredIMethodCache = null;
+            }
+        }
 
         // Implement TypeDeclaration.
         public void addMemberTypeDeclaration(MemberTypeDeclaration mcoid) {
@@ -369,6 +380,13 @@
             }
             return null;
         }
+        public MethodDeclarator getMethodDeclaration(String name) {
+            for (Iterator it = this.declaredMethods.iterator(); it.hasNext();) {
+                MethodDeclarator md = (MethodDeclarator) it.next();
+                if (md.name.equals(name)) return md;
+            }
+            return null;
+        }
         public String createLocalTypeName(String localTypeName) {
             return (
                 this.getClassName()
@@ -1227,10 +1245,52 @@
         }
 
         // Compile time members.
-
         public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitBlock(this); }
     }
+    
+    /**
+     * This is similar to a {@link Java.Block} except that it does not create a scope around it statements.
+     * It is useful for programmatically manipulating an AST
+     */
+    public final static class StatementList extends Statement {
+        public final List statements = new ArrayList(); // BlockStatement
 
+        public StatementList(Location location) {
+            super(location);
+        }
+
+        public void addStatement(BlockStatement statement) {
+            this.statements.add(statement);
+            statement.setEnclosingScope(this.getEnclosingScope());
+        }
+        
+        public void addStatements(
+            List statements // BlockStatement
+        ) {
+            Iterator stmtIter = statements.iterator();
+            while(stmtIter.hasNext()) {
+                addStatement((BlockStatement)stmtIter.next());
+            }
+        }
+        
+        public BlockStatement[] getStatements() {
+            return (BlockStatement[]) this.statements.toArray(new BlockStatement[this.statements.size()]);
+        }
+        
+        // set children to share this object's enclosing scope
+        public void setEnclosingScope(Scope enclosingScope) {
+            super.setEnclosingScope(enclosingScope);
+            Iterator stmtIter = this.statements.iterator();
+            while(stmtIter.hasNext()) {
+                BlockStatement stmt = (BlockStatement)stmtIter.next();
+                stmt.setEnclosingScope(enclosingScope);
+            }
+        }
+
+        // Compile time members.
+        public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitStatementList(this); }
+    }
+
     /**
      * Base class for statements that can be terminated abnormally with a
      * "break" statement.
@@ -1978,7 +2038,39 @@
         public final void accept(Visitor.RvalueVisitor visitor) { visitor.visitLocalVariableAccess(this); }
         public final void accept(Visitor.AtomVisitor visitor) { visitor.visitLocalVariableAccess(this); }
     }
+    
+    /**
+     * Representation of an access to a field that requires an indirection through a generated method.  
+     * <p>
+     * Typically accessing a protected member of the enclosing class's parent class.
+     */
+    public static final class IndirectFieldAccess extends Lvalue {
+        public final Atom                    lhs;
+        public final IClass.IField           field;
+        public final AbstractTypeDeclaration typeDeclaration;
 
+        public IndirectFieldAccess(
+            Location                location,
+            Atom                    lhs,
+            IClass.IField           field,
+            AbstractTypeDeclaration typeDeclaration
+        ) {
+            super(location);
+            this.lhs   = lhs;
+            this.field = field;
+            this.typeDeclaration = typeDeclaration;
+        }
+
+        // Compile time members.
+
+        // Implement "Atom".
+        public String toString() { return this.lhs.toString() + '.' + this.field.toString(); }
+
+        public void accept(LvalueVisitor lvv) { lvv.visitIndirectFieldAccess(this); }
+        public void accept(RvalueVisitor rvv) { rvv.visitIndirectFieldAccess(this); }
+        public void accept(AtomVisitor   vis) { vis.visitIndirectFieldAccess(this); }
+    }
+
     /**
      * Representation of an access to a field of a class or an interface. (Does not implement the
      * "array length" expression, e.g. "ia.length".)
@@ -2674,6 +2766,23 @@
         public final Rvalue[] dimExprs;
         public final int      dims;
 
+        /**
+         * Create a new array with dimension dimExprs.length + dims
+         * <p>
+         * e.g. byte[12][][] is created with
+         *     new NewArray(
+         *         null,
+         *         Java.BasicType(NULL, Java.BasicType.BYTE),
+         *         new Rvalue[] {
+         *             new Java.Literal(null, Integer.valueOf(12)
+         *         },
+         *         2
+         *     )
+         * @param location  the location of this element
+         * @param type      the base type of the array
+         * @param dimExprs  sizes for dimensions being allocated with specific sizes
+         * @param dims      the number of dimensions that are not yet allocated
+         */
         public NewArray(
             Location location,
             Type     type,
=== src/org/codehaus/janino/SimpleCompiler.java
==================================================================
--- src/org/codehaus/janino/SimpleCompiler.java (revision 338)
+++ src/org/codehaus/janino/SimpleCompiler.java (patch statementlist level 4)
@@ -201,7 +201,7 @@
     public static final ClassLoader BOOT_CLASS_LOADER = new ClassLoader(null) {};
 
     /**
-     * Allowe references to the classes loaded through this parent class loader
+     * Allow references to the classes loaded through this parent class loader
      * (@see {@link #setParentClassLoader(ClassLoader)}), plus the extra
      * <code>auxiliaryClasses</code>.
      * <p>
@@ -233,7 +233,22 @@
             DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
         );
     }
+    
+    /**
+     * Cook this compilation unit directly.
+     *  See {@link Cookable.cook}
+     */
+    public void cook(Java.CompilationUnit compilationUnit)
+    throws CompileException, Parser.ParseException, Scanner.ScanException, IOException {
+        this.setUpClassLoaders();
 
+        // Compile the classes and load them.
+        this.compileToClassLoader(
+            compilationUnit,
+            DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
+        );
+    }
+
     /**
      * Initializes {@link #classLoader} and {@link #iClassLoader} from the configured
      * {@link #parentClassLoader} and {@link #optionalAuxiliaryClasses}. These are needed by
=== src/org/codehaus/janino/Scanner.java
==================================================================
--- src/org/codehaus/janino/Scanner.java (revision 338)
+++ src/org/codehaus/janino/Scanner.java (patch statementlist level 4)
@@ -457,6 +457,9 @@
         if (v instanceof Boolean) {
             return v.toString();
         }
+        if (v instanceof Byte) {
+            return v.toString();
+        }
         if (v == null) {
             return "null";
         }
=== src/org/codehaus/janino/Visitor.java
==================================================================
--- src/org/codehaus/janino/Visitor.java (revision 338)
+++ src/org/codehaus/janino/Visitor.java (patch statementlist level 4)
@@ -73,6 +73,7 @@
         void visitFieldDeclaration(Java.FieldDeclaration fd);
         void visitLabeledStatement(Java.LabeledStatement ls);
         void visitBlock(Java.Block b);
+        void visitStatementList(Java.StatementList sl);
         void visitExpressionStatement(Java.ExpressionStatement es);
         void visitIfStatement(Java.IfStatement is);
         void visitForStatement(Java.ForStatement fs);
@@ -131,6 +132,7 @@
         void visitAmbiguousName(Java.AmbiguousName an);
         void visitArrayAccessExpression(Java.ArrayAccessExpression aae);
         void visitFieldAccess(Java.FieldAccess fa);
+        void visitIndirectFieldAccess(Java.IndirectFieldAccess fa);
         void visitFieldAccessExpression(Java.FieldAccessExpression fae);
         void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae);
         void visitLocalVariableAccess(Java.LocalVariableAccess lva);
=== src/org/codehaus/janino/UnparseVisitor.java
==================================================================
--- src/org/codehaus/janino/UnparseVisitor.java (revision 338)
+++ src/org/codehaus/janino/UnparseVisitor.java (patch statementlist level 4)
@@ -45,8 +45,103 @@
  * {@link #main(String[])} for a usage example.
  */
 public class UnparseVisitor implements Visitor.ComprehensiveVisitor {
-    private final AutoIndentWriter aiw;
-    private final PrintWriter      pw;
+    protected final AutoIndentWriter aiw;
+    protected final PrintWriter      pw;
+    
+    private final Stack curPrecedence;
+    
+    /**
+     * Install op as the next op's precedence level
+     * @param op a string that it is the precedenceMap
+     */
+    private void pushPrecedence(String op) {
+        Object o = precedenceMap.get(op);
+        if(o == null) {
+            throw new RuntimeException("Illegal operator for precedence: " + op);
+        }
+        curPrecedence.push(o);
+    }
+    
+    /**
+     * Remove the current precedence setting
+     */
+    private void popPrecedence() {
+        curPrecedence.pop();
+    }
+    
+    /**
+     * Give the precedence value a slight nudge to account for associativity of operators
+     */
+    private void adjustPrecedenceForAssociativity(boolean higher) {
+        double cur = ((Double)curPrecedence.pop()).doubleValue();
+        cur += higher ? 0.5 : -0.5;
+        curPrecedence.push(Double.valueOf(cur));
+    }
+    
+    private boolean needsParens(String op) {
+        //return false;
+        if(curPrecedence.isEmpty()) { return false; }
+        double cur = ((Double)curPrecedence.peek()).doubleValue();
+        double nxt = ((Double)precedenceMap.get(op)).doubleValue();
+        return cur > nxt;
+    }
+    
+    // this provides a mapping from operators to precedence levels
+    // - higher numbers are more tightly binding
+    // - ambiguous cases are just given special tokens
+    // - "reset" is a special token for bracketing operations
+    private static final Map precedenceMap = new HashMap(); // Map<String, Double>
+    static {
+        precedenceMap.put("[]",         Double.valueOf(15));
+        precedenceMap.put(".",          Double.valueOf(15));
+        precedenceMap.put("()",         Double.valueOf(15));
+        precedenceMap.put("methodcall", Double.valueOf(15));
+        // ++ and -- are ambiguous as they are both post and prefix
+        // so instead we are just putting the names
+        precedenceMap.put("postfix",    Double.valueOf(15));
+        precedenceMap.put("prefix",     Double.valueOf(14));
+        //unary - is ambiguous just use prefix
+        //precedenceMap.put("-",          Double.valueOf(14));
+        precedenceMap.put("~",          Double.valueOf(14));
+        precedenceMap.put("!",          Double.valueOf(14));
+        precedenceMap.put("cast",       Double.valueOf(13));
+        precedenceMap.put("new",        Double.valueOf(13));
+        precedenceMap.put("*",          Double.valueOf(12));
+        precedenceMap.put("/",          Double.valueOf(12));
+        precedenceMap.put("%",          Double.valueOf(12));
+        precedenceMap.put("+",          Double.valueOf(11));
+        precedenceMap.put("-",          Double.valueOf(11));
+        precedenceMap.put("<<",         Double.valueOf(10));
+        precedenceMap.put(">>",         Double.valueOf(10));
+        precedenceMap.put(">>>",        Double.valueOf(10));
+        precedenceMap.put(">=",         Double.valueOf(9));
+        precedenceMap.put("<=",         Double.valueOf(9));
+        precedenceMap.put("<",          Double.valueOf(9));
+        precedenceMap.put(">",          Double.valueOf(9));
+        precedenceMap.put("instanceof", Double.valueOf(9));
+        precedenceMap.put("==",         Double.valueOf(8));
+        precedenceMap.put("!=",         Double.valueOf(8));
+        precedenceMap.put("&",          Double.valueOf(7));
+        precedenceMap.put("^",          Double.valueOf(6));
+        precedenceMap.put("|",          Double.valueOf(5));
+        precedenceMap.put("&&",         Double.valueOf(4));
+        precedenceMap.put("||",         Double.valueOf(4));
+        precedenceMap.put("?:",         Double.valueOf(3));
+        precedenceMap.put("=",          Double.valueOf(2));
+        precedenceMap.put("+=",         Double.valueOf(2));
+        precedenceMap.put("-=",         Double.valueOf(2));
+        precedenceMap.put("*=",         Double.valueOf(2));
+        precedenceMap.put("/=",         Double.valueOf(2));
+        precedenceMap.put("%=",         Double.valueOf(2));
+        precedenceMap.put("&=",         Double.valueOf(2));
+        precedenceMap.put("^=",         Double.valueOf(2));
+        precedenceMap.put("|=",         Double.valueOf(2));
+        precedenceMap.put("<<=",        Double.valueOf(2));
+        precedenceMap.put(">>=",        Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put("reset",      Double.valueOf(0));
+    }
 
     /**
      * Testing of parsing/unparsing.
@@ -84,6 +179,7 @@
     public UnparseVisitor(Writer w) {
         this.aiw = new AutoIndentWriter(w);
         this.pw = new PrintWriter(this.aiw, true);
+        this.curPrecedence = new Stack();
     }
 
     public void unparseCompilationUnit(Java.CompilationUnit cu) {
@@ -177,11 +273,18 @@
     }
     public void visitBlock(Java.Block b) {
         this.pw.println('{');
-        for (Iterator it = b.statements.iterator(); it.hasNext();) {
+        visitListStatements(b.statements);
+        this.pw.print('}');
+    }
+    public void visitStatementList(Java.StatementList sl) {
+        visitListStatements(sl.statements);
+    }
+    //takes a List<Java.BlockStatement>
+    private void visitListStatements(List l) {
+        for (Iterator it = l.iterator(); it.hasNext();) {
             ((Java.BlockStatement) it.next()).accept(this);
             this.pw.println();
         }
-        this.pw.print('}');
     }
     public void visitBreakStatement(Java.BreakStatement bs) {
         this.pw.print("break");
@@ -204,8 +307,10 @@
         this.pw.print(';');
     }
     public void visitExpressionStatement(Java.ExpressionStatement es) {
+        pushPrecedence("reset"); //this might be an expression in a nested class body or something
         ((Java.Atom) es.rvalue).accept(this);
         this.pw.print(';');
+        popPrecedence();
     }
     public void visitForStatement(Java.ForStatement fs) {
         this.pw.print("for (");
@@ -334,12 +439,16 @@
         this.pw.print(' ' + fp.name);
     }
     public void visitMethodInvocation(Java.MethodInvocation mi) {
+        if(needsParens("methodcall")) { this.pw.print('('); }
         if (mi.optionalTarget != null) {
+            pushPrecedence(".");
             mi.optionalTarget.accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print(mi.methodName);
         this.unparseFunctionInvocationArguments(mi.arguments);
+        if(needsParens("methodcall")) { this.pw.print(')'); }
     }
     public void visitAlternateConstructorInvocation(Java.AlternateConstructorInvocation aci) {
         this.pw.print("this");
@@ -354,28 +463,47 @@
         this.unparseFunctionInvocationArguments(sci.arguments);
     }
     public void visitNewClassInstance(Java.NewClassInstance nci) {
+        if(needsParens("new")) { this.pw.print('('); }
         if (nci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) nci.optionalQualification).accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print("new " + nci.type.toString());
         this.unparseFunctionInvocationArguments(nci.arguments);
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitAssignment(Java.Assignment a) {
+        if(needsParens(a.operator)) { this.pw.print('('); }
+        pushPrecedence(a.operator);
         ((Java.Atom) a.lhs).accept(this);
         this.pw.print(' ' + a.operator + ' ');
         ((Java.Atom) a.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(a.operator)) { this.pw.print(')'); }
     }
     public void visitAmbiguousName(Java.AmbiguousName an) { this.pw.print(an.toString()); }
     public void visitArrayAccessExpression(Java.ArrayAccessExpression aae) {
+        if(needsParens("[]")) { this.pw.print('('); }
+        pushPrecedence("[]");
         ((Java.Atom) aae.lhs).accept(this);
+        popPrecedence();
+        
         this.pw.print('[');
+        pushPrecedence("reset");
         ((Java.Atom) aae.index).accept(this);
+        popPrecedence();
         this.pw.print(']');
+        if(needsParens("[]")) { this.pw.print(')'); }
     }
     public void visitArrayLength(Java.ArrayLength al) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) al.lhs).accept(this);
         this.pw.print(".length");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitArrayType(Java.ArrayType at) {
         ((Java.Atom) at.componentType).accept(this);
@@ -385,44 +513,88 @@
         this.pw.print(bt.toString());
     }
     public void visitBinaryOperation(Java.BinaryOperation bo) {
+        if(needsParens(bo.op)) { this.pw.print('('); }
+        pushPrecedence(bo.op);
         ((Java.Atom) bo.lhs).accept(this);
         this.pw.print(' ' + bo.op + ' ');
+        adjustPrecedenceForAssociativity(true); //binary ops are all left -> right associative
         ((Java.Atom) bo.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(bo.op)) { this.pw.print(')'); }
     }
     public void visitCast(Java.Cast c) {
+        if(needsParens("cast")) { this.pw.print('('); }
+        pushPrecedence("cast");
         this.pw.print('(');
         ((Java.Atom) c.targetType).accept(this);
         this.pw.print(") ");
         ((Java.Atom) c.value).accept(this);
+        popPrecedence();
+        if(needsParens("cast")) { this.pw.print(')'); }
     }
     public void visitClassLiteral(Java.ClassLiteral cl) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) cl.type).accept(this);
         this.pw.print(".class");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitConditionalExpression(Java.ConditionalExpression ce) {
+        if(needsParens("?:")) { this.pw.print('('); }
+        pushPrecedence("?:");
+        adjustPrecedenceForAssociativity(true); //ternary is right -> left associative
         ((Java.Atom) ce.lhs).accept(this);
+        adjustPrecedenceForAssociativity(false); //back to normal
         this.pw.print(" ? ");
         ((Java.Atom) ce.mhs).accept(this);
         this.pw.print(" : ");
         ((Java.Atom) ce.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("?:")) { this.pw.print(')'); }
     }
     public void visitConstantValue(Java.ConstantValue cv) { this.pw.print(cv.toString()); }
     public void visitCrement(Java.Crement c) {
-        this.pw.print(
-            c.pre ?
-            c.operator + c.operand :
-            c.operand + c.operator
-        );
+        String prec = c.pre ? "prefix" : "postfix";
+        if(needsParens(prec)) { this.pw.print('('); }
+        pushPrecedence(prec);
+        if(c.pre) {
+            this.pw.print(c.operator);
+            this.pw.print(c.operand);
+        } else {
+            this.pw.print(c.operand);
+            this.pw.print(c.operator);
+        }
+        popPrecedence();
+        if(needsParens(prec)) { this.pw.print(')'); }
     }
     public void visitFieldAccess(Java.FieldAccess fa) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fa.lhs.accept(this);
         this.pw.print('.' + fa.field.getName());
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
+    public void visitIndirectFieldAccess(Java.IndirectFieldAccess ifa) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
+        ifa.lhs.accept(this);
+        this.pw.print('.' + ifa.field.getName());
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
+    }
     public void visitFieldAccessExpression(Java.FieldAccessExpression fae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fae.lhs.accept(this);
         this.pw.print('.' + fae.fieldName);
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         if (scfae.optionalQualification != null) {
             scfae.optionalQualification.accept((Visitor.TypeVisitor) this);
             this.pw.print(".super." + scfae.fieldName);
@@ -430,37 +602,57 @@
         {
             this.pw.print("super." + scfae.fieldName);
         }
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitInstanceof(Java.Instanceof io) {
+        if(needsParens("instanceof")) { this.pw.print('('); }
+        pushPrecedence("instanceof");
         ((Java.Atom) io.lhs).accept(this);
         this.pw.print(" instanceof ");
         ((Java.Atom) io.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("instanceof")) { this.pw.print(')'); }
     }
     public void visitLiteral(Java.Literal l) { this.pw.print(l.toString()); }
     public void visitLocalVariableAccess(Java.LocalVariableAccess lva) { this.pw.print(lva.toString()); }
     public void visitNewArray(Java.NewArray na) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         ((Java.Atom) na.type).accept(this);
         for (int i = 0; i < na.dimExprs.length; ++i) {
             this.pw.print('[');
+            pushPrecedence("reset");
             ((Java.Atom) na.dimExprs[i]).accept(this);
+            popPrecedence();
             this.pw.print(']');
         }
         for (int i = 0; i < na.dims; ++i) {
             this.pw.print("[]");
         }
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitNewInitializedArray(Java.NewInitializedArray nai) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         nai.arrayType.accept(this);
         this.pw.print(" ");
         this.unparseArrayInitializerOrRvalue(nai.arrayInitializer);
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitPackage(Java.Package p) { this.pw.print(p.toString()); }
     public void visitParameterAccess(Java.ParameterAccess pa) { this.pw.print(pa.toString()); }
     public void visitQualifiedThisReference(Java.QualifiedThisReference qtr) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) qtr.qualification).accept(this);
         this.pw.print(".this");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitReferenceType(Java.ReferenceType rt) { this.pw.print(rt.toString()); }
     public void visitRvalueMemberType(Java.RvalueMemberType rmt) { this.pw.print(rmt.toString()); }
@@ -473,12 +665,19 @@
         this.pw.print("this");
     }
     public void visitUnaryOperation(Java.UnaryOperation uo) {
+        if(needsParens("prefix")) { this.pw.print('('); }
+        pushPrecedence("prefix");
         this.pw.print(uo.operator);
+        this.adjustPrecedenceForAssociativity(true); //handle cases like "- - 3" as -(-3)
         ((Java.Atom) uo.operand).accept(this);
+        popPrecedence();
+        if(needsParens("prefix")) { this.pw.print(')'); }
     }
     public void visitParenthesizedExpression(Java.ParenthesizedExpression pe) {
         this.pw.print('(');
+        pushPrecedence("reset");
         ((Java.Atom) pe.value).accept(this);
+        popPrecedence();
         this.pw.print(')');
     }
 
@@ -508,11 +707,13 @@
             } else
             {
                 this.pw.print("{ ");
+                pushPrecedence("reset");
                 this.unparseArrayInitializerOrRvalue(ai.values[0]);
                 for (int i = 1; i < ai.values.length; ++i) {
                     this.pw.print(", ");
                     this.unparseArrayInitializerOrRvalue(ai.values[i]);
                 }
+                popPrecedence();
                 this.pw.print(" }");
             }
         } else
@@ -524,22 +725,32 @@
     public void visitAnonymousClassDeclaration(Java.AnonymousClassDeclaration acd) {
         ((Java.Atom) acd.baseType).accept(this);
         this.pw.println(" {");
+        pushPrecedence("reset");
         this.unparseClassDeclarationBody(acd);
+        popPrecedence();
         this.pw.print('}');
     }
     public void visitNewAnonymousClassInstance(Java.NewAnonymousClassInstance naci) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         if (naci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) naci.optionalQualification).accept(this);
+            popPrecedence();
             this.pw.print('.');
         }
         this.pw.print("new " + naci.anonymousClassDeclaration.baseType.toString() + '(');
+        pushPrecedence("reset");
         for (int i = 0; i < naci.arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) naci.arguments[i]).accept(this);
         }
+        popPrecedence(); //pop the reset
         this.pw.println(") {");
         this.unparseClassDeclarationBody(naci.anonymousClassDeclaration);
         this.pw.print('}');
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     // Multi-line!
     private void unparseClassDeclarationBody(Java.ClassDeclaration cd) {
@@ -607,10 +818,12 @@
     }
     private void unparseFunctionInvocationArguments(Java.Rvalue[] arguments) {
         this.pw.print('(');
+        pushPrecedence("reset");
         for (int i = 0; i < arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) arguments[i]).accept(this);
         }
+        popPrecedence();
         this.pw.print(')');
     }
 }
=== src/org/codehaus/janino/util/Traverser.java
==================================================================
--- src/org/codehaus/janino/util/Traverser.java (revision 338)
+++ src/org/codehaus/janino/util/Traverser.java (patch statementlist level 4)
@@ -70,6 +70,7 @@
         public final void visitFieldDeclaration(Java.FieldDeclaration fd)                                     { Traverser.this.traverseFieldDeclaration(fd); }
         public final void visitLabeledStatement(Java.LabeledStatement ls)                                     { Traverser.this.traverseLabeledStatement(ls); }
         public final void visitBlock(Java.Block b)                                                            { Traverser.this.traverseBlock(b); }
+        public final void visitStatementList(Java.StatementList sl)                                           { Traverser.this.traverseStatementList(sl); }
         public final void visitExpressionStatement(Java.ExpressionStatement es)                               { Traverser.this.traverseExpressionStatement(es); }
         public final void visitIfStatement(Java.IfStatement is)                                               { Traverser.this.traverseIfStatement(is); }
         public final void visitForStatement(Java.ForStatement fs)                                             { Traverser.this.traverseForStatement(fs); }
@@ -116,6 +117,7 @@
         public final void visitAmbiguousName(Java.AmbiguousName an)                                           { Traverser.this.traverseAmbiguousName(an); }
         public final void visitArrayAccessExpression(Java.ArrayAccessExpression aae)                          { Traverser.this.traverseArrayAccessExpression(aae); }
         public final void visitFieldAccess(Java.FieldAccess fa)                                               { Traverser.this.traverseFieldAccess(fa); }
+        public final void visitIndirectFieldAccess(Java.IndirectFieldAccess ifa)                              { Traverser.this.traverseIndirectFieldAccess(ifa); }
         public final void visitFieldAccessExpression(Java.FieldAccessExpression fae)                          { Traverser.this.traverseFieldAccessExpression(fae); }
         public final void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae)    { Traverser.this.traverseSuperclassFieldAccessExpression(scfae); }
         public final void visitLocalVariableAccess(Java.LocalVariableAccess lva)                              { Traverser.this.traverseLocalVariableAccess(lva); }
@@ -220,6 +222,12 @@
         }
         this.traverseStatement(b);
     }
+    public void traverseStatementList(Java.StatementList sl) {
+        for (Iterator it = sl.statements.iterator(); it.hasNext();) {
+            ((Java.Statement) it.next()).accept(this.cv);
+        }
+        this.traverseStatement(sl);
+    }
 
     public void traverseExpressionStatement(Java.ExpressionStatement es) {
         es.rvalue.accept((Visitor.RvalueVisitor) this.cv);
@@ -488,6 +496,11 @@
         fa.lhs.accept(this.cv);
         this.traverseLvalue(fa);
     }
+    
+    public void traverseIndirectFieldAccess(Java.IndirectFieldAccess ifa) {
+        ifa.lhs.accept(this.cv);
+        this.traverseLvalue(ifa);
+    }
 
     public void traverseFieldAccessExpression(Java.FieldAccessExpression fae) {
         fae.lhs.accept(this.cv);

==== BEGIN SVK PATCH BLOCK ====
Version: svk v2.0.2 (linux)

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
==== END SVK PATCH BLOCK ====


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email

Re: Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

Welcome to what appears to be the longest conversation I have ever had
with myself.  This latest version includes everything from before and
also allows Janino to compile methods with over 32K bytecode.

If people want I will spend the time to extract just the 32K
expansion, but my work absolutely requires the other portions, so I
would much prefer that everything were included.

Matt

On Tue, May 13, 2008 at 12:53 PM, Matt Fowles <matt.fowles@...> wrote:

> All~
>
> This version includes further updates to problems I found in
>
> http://jira.codehaus.org/browse/JANINO-113
>
> Matt
>
> On Fri, May 9, 2008 at 10:40 PM, Matt Fowles <matt.fowles@...> wrote:
>> All~
>>
>>  This patch includes fixes for
>>
>>  http://jira.codehaus.org/browse/JANINO-110
>>
>> http://jira.codehaus.org/browse/JANINO-111
>>  http://jira.codehaus.org/browse/JANINO-112
>>  http://jira.codehaus.org/browse/JANINO-113
>>
>>
>>  - added class Java.StatementList
>>  - this allows code to be generated dynamically into an existing AST
>>  (very necessary for my compiler)
>>   - update as necessary all the visitors for Java.StatementList
>>   - basic tests for Java.StatementList
>>
>>  - improved comments for Java.NewArray
>>
>>  It does not contain my earlier change which exposed byte[] in
>>  SimpleCompiler.  It turns out that I actually wanted to be using
>>  UnitCompiler and just didn't know it.  The change from SimpleCompiler
>>  to a custom Compiler (based on Compiler) that uses UnitCompiler
>>  internally has greatly cleaned up the code for my project.  We may
>>  wish to improve the documentation around this point, as the example
>>  did not provide an in depth enough example.  I will attempt to further
>>  write up my experiences from integrating Janino once the project is
>>  further advanced.
>>
>>
>>
>>  Once again, any pointers on things I can do to speed the inclusion of
>>  patches into Janino or to improve the patch in general are greatly appreciated.
>>
>

[janino-full.patch]

==== Patch <janino-full> level 1
Source: 737f8f2c-97f8-0310-ac9b-cfac8cc1a2f5:/eng/third-party/janino/hynes-bytecode:72229
        (svn+ssh://svn.streambase.com/repos/sb)
Target: 737f8f2c-97f8-0310-ac9b-cfac8cc1a2f5:/eng/third-party/janino/trunk:72158
        (svn+ssh://svn.streambase.com/repos/sb)
Log:
 r74026@spiceweasel (orig r72225):  fowles | 2008-05-18 21:53:29 -0400
 creating a branch for bytecode generation work done during hynes
 
 r74027@spiceweasel (orig r72226):  fowles | 2008-05-18 21:55:43 -0400
  r73994@spiceweasel (orig r72199):  fowles | 2008-05-17 13:51:45 -0400
  merge personal changes from local svk to svn
 
 
 r74028@spiceweasel (orig r72227):  fowles | 2008-05-18 21:56:13 -0400
  r74007@spiceweasel (orig r72211):  fowles | 2008-05-18 13:13:14 -0400
  fix test names after some merge games
 
 
 r74029@spiceweasel (orig r72228):  fowles | 2008-05-18 21:57:42 -0400
  r74008@spiceweasel (orig r72212):  fowles | 2008-05-18 13:13:46 -0400
  switch from recursion to having a single method that iterates to a fixed point
 
 
 r74030@spiceweasel (orig r72229):  fowles | 2008-05-18 21:58:21 -0400
  r74016@spiceweasel (orig r72220):  fowles | 2008-05-18 18:41:01 -0400
  fix things to work with wide jumps
  raises bytecode limit from 32K to 64K
 
 

=== tests/src/UnparseTests.java
==================================================================
--- tests/src/UnparseTests.java (revision 72158)
+++ tests/src/UnparseTests.java (patch janino-full level 1)
@@ -0,0 +1,337 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Parser;
+import org.codehaus.janino.Scanner;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Visitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayAccessExpression;
+import org.codehaus.janino.Java.ArrayLength;
+import org.codehaus.janino.Java.Assignment;
+import org.codehaus.janino.Java.Atom;
+import org.codehaus.janino.Java.BinaryOperation;
+import org.codehaus.janino.Java.Cast;
+import org.codehaus.janino.Java.ClassLiteral;
+import org.codehaus.janino.Java.ConditionalExpression;
+import org.codehaus.janino.Java.ConstantValue;
+import org.codehaus.janino.Java.Crement;
+import org.codehaus.janino.Java.FieldAccess;
+import org.codehaus.janino.Java.FieldAccessExpression;
+import org.codehaus.janino.Java.IndirectFieldAccess;
+import org.codehaus.janino.Java.Instanceof;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableAccess;
+import org.codehaus.janino.Java.MethodInvocation;
+import org.codehaus.janino.Java.NewAnonymousClassInstance;
+import org.codehaus.janino.Java.NewArray;
+import org.codehaus.janino.Java.NewClassInstance;
+import org.codehaus.janino.Java.NewInitializedArray;
+import org.codehaus.janino.Java.ParameterAccess;
+import org.codehaus.janino.Java.ParenthesizedExpression;
+import org.codehaus.janino.Java.QualifiedThisReference;
+import org.codehaus.janino.Java.SuperclassFieldAccessExpression;
+import org.codehaus.janino.Java.SuperclassMethodInvocation;
+import org.codehaus.janino.Java.ThisReference;
+import org.codehaus.janino.Java.UnaryOperation;
+
+public class UnparseTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(UnparseTests.class.getName());
+        s.addTest(new UnparseTests("testSimple"));
+        s.addTest(new UnparseTests("testParens"));
+        s.addTest(new UnparseTests("testMany"));
+        return s;
+    }
+
+    public UnparseTests(String name) { super(name); }
+    
+    private static void helpTestExpr(String input, String expect, boolean simplify) throws Exception {
+        Parser p = new Parser(new Scanner(
+                null, new ByteArrayInputStream(input.getBytes()
+        )));
+        Atom expr = p.parseExpression();
+        if(simplify) {
+            expr = stripUnnecessaryParenExprs(expr);
+        }
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        expr.accept(uv);
+        assertEquals(expect, sw.toString());    
+    }
+    
+    private static Java.Rvalue[] stripUnnecessaryParenExprs(Java.Rvalue[] rvalues) {
+        Java.Rvalue[] res = new Java.Rvalue[rvalues.length];
+        for(int i = 0; i < res.length; ++i) {
+            res[i] = stripUnnecessaryParenExprs(rvalues[i]);
+        }
+        return res;
+    }
+    
+    private static Java.Atom stripUnnecessaryParenExprs(Java.Atom atom) {
+        if(atom instanceof Java.Rvalue) {
+            return stripUnnecessaryParenExprs((Java.Rvalue)atom);
+        }
+        return atom;
+    }
+    
+    private static Java.Lvalue stripUnnecessaryParenExprs(Java.Lvalue lvalue) {
+        return (Java.Lvalue)stripUnnecessaryParenExprs((Java.Rvalue)lvalue);
+    }
+    
+    private static Java.Rvalue stripUnnecessaryParenExprs(Java.Rvalue rvalue) {
+        if(rvalue == null) { return null; }
+        final Java.Rvalue[] res = new Java.Rvalue[1];
+        Visitor.RvalueVisitor rv = new Visitor.RvalueVisitor() {
+            public void visitArrayLength(ArrayLength al) {
+                res[0] = new Java.ArrayLength(al.getLocation(),
+                        stripUnnecessaryParenExprs(al.lhs));
+            }
+
+            public void visitAssignment(Assignment a) {
+                res[0] = new Java.Assignment(a.getLocation(),
+                        stripUnnecessaryParenExprs(a.lhs),
+                        a.operator,
+                        stripUnnecessaryParenExprs(a.rhs)
+                );
+            }
+
+            public void visitBinaryOperation(BinaryOperation bo) {
+                res[0] = new Java.BinaryOperation(bo.getLocation(),
+                        stripUnnecessaryParenExprs(bo.lhs),
+                        bo.op,
+                        stripUnnecessaryParenExprs(bo.rhs)
+                );
+            }
+
+            public void visitCast(Cast c) {
+                res[0] = new Java.Cast(c.getLocation(),
+                        c.targetType,
+                        stripUnnecessaryParenExprs(c.value));
+            }
+
+            public void visitClassLiteral(ClassLiteral cl) {
+                res[0] = cl; //too much effort
+            }
+
+            public void visitConditionalExpression(ConditionalExpression ce) {
+                res[0] = new Java.ConditionalExpression(ce.getLocation(),
+                        stripUnnecessaryParenExprs(ce.lhs),
+                        stripUnnecessaryParenExprs(ce.mhs),
+                        stripUnnecessaryParenExprs(ce.rhs));
+            }
+
+            public void visitConstantValue(ConstantValue cv) {
+                res[0] = cv;
+            }
+
+            public void visitCrement(Crement c) {
+                if(c.pre) {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            c.operator,
+                            stripUnnecessaryParenExprs(c.operand));
+                } else {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            stripUnnecessaryParenExprs(c.operand),
+                            c.operator);
+                }
+            }
+
+            public void visitInstanceof(Instanceof io) {
+                res[0] = new Java.Instanceof(io.getLocation(),
+                        stripUnnecessaryParenExprs(io.lhs),
+                        io.rhs);
+            }
+
+            public void visitLiteral(Literal l) {
+                res[0] = l;
+            }
+
+            public void visitMethodInvocation(MethodInvocation mi) {
+                res[0] = new Java.MethodInvocation(mi.getLocation(),
+                        stripUnnecessaryParenExprs(mi.optionalTarget),
+                        mi.methodName,
+                        stripUnnecessaryParenExprs(mi.arguments));
+            }
+
+            public void visitNewAnonymousClassInstance(
+                    NewAnonymousClassInstance naci) {
+                res[0] = naci; //too much effort
+            }
+
+            public void visitNewArray(NewArray na) {
+                res[0] = new Java.NewArray(na.getLocation(),
+                        na.type,
+                        stripUnnecessaryParenExprs(na.dimExprs),
+                        na.dims);
+            }
+
+            public void visitNewClassInstance(NewClassInstance nci) {
+                res[0] = new Java.NewClassInstance(nci.getLocation(),
+                        stripUnnecessaryParenExprs(nci.optionalQualification),
+                        nci.type,
+                        stripUnnecessaryParenExprs(nci.arguments));
+            }
+
+            public void visitNewInitializedArray(NewInitializedArray nia) {
+                res[0] = nia; //too much effort
+            }
+
+            public void visitParameterAccess(ParameterAccess pa) {
+                res[0] = pa;
+            }
+
+            public void visitQualifiedThisReference(QualifiedThisReference qtr) {
+                res[0] = qtr;
+            }
+
+            public void visitSuperclassMethodInvocation(
+                    SuperclassMethodInvocation smi) {
+                res[0] = new Java.SuperclassMethodInvocation(smi.getLocation(),
+                        smi.methodName,
+                        stripUnnecessaryParenExprs(smi.arguments));
+            }
+
+            public void visitThisReference(ThisReference tr) {
+                res[0] = tr;
+            }
+
+            public void visitUnaryOperation(UnaryOperation uo) {
+                res[0] = new Java.UnaryOperation(uo.getLocation(),
+                        uo.operator,
+                        stripUnnecessaryParenExprs(uo.operand));
+            }
+
+            public void visitAmbiguousName(AmbiguousName an) {
+                res[0] = an;
+            }
+
+            public void visitArrayAccessExpression(ArrayAccessExpression aae) {
+                res[0] = new Java.ArrayAccessExpression(aae.getLocation(),
+                        stripUnnecessaryParenExprs(aae.lhs),
+                        stripUnnecessaryParenExprs(aae.index));
+            }
+
+            public void visitFieldAccess(FieldAccess fa) {
+                res[0] = new Java.FieldAccess(fa.getLocation(),
+                        stripUnnecessaryParenExprs(fa.lhs),
+                        fa.field);
+            }
+
+            public void visitFieldAccessExpression(FieldAccessExpression fae) {
+                res[0] = new Java.FieldAccessExpression(fae.getLocation(),
+                        stripUnnecessaryParenExprs(fae.lhs),
+                        fae.fieldName);
+            }
+
+            public void visitLocalVariableAccess(LocalVariableAccess lva) {
+                res[0] = lva;
+            }
+
+            public void visitParenthesizedExpression(ParenthesizedExpression pe) {
+                res[0] = stripUnnecessaryParenExprs(pe.value);
+            }
+
+            public void visitSuperclassFieldAccessExpression(
+                    SuperclassFieldAccessExpression scfae) {
+                res[0] = scfae;
+            }
+
+            public void visitIndirectFieldAccess(IndirectFieldAccess fa) {
+                res[0] = fa;
+            }
+            
+        };
+        rvalue.accept(rv);
+        return res[0];
+    }
+    
+    public void testSimple() throws Exception {
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", false);
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", true);
+    }
+    
+    public void testParens() throws Exception {
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", false);
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", true);
+    }
+    
+    public void testMany() throws Exception {
+        final String[][] exprs = new String[][] {
+              //input                                  expected simplified                    expect non-simplified
+            { "((1)+2)",                               "1 + 2",                               "((1) + 2)"           },
+            { "1 + 2 * 3",                             null,                                  null                  },
+            { "1 + (2 * 3)",                           "1 + 2 * 3",                           null                  },
+            { "3 - (2 - 1)",                           null,                                  null                  },
+            { "true ? 1 : 2",                          null,                                  null                  },
+            { "(true ? false : true) ? 1 : 2",         null,                                  null                  },
+            { "true ? false : (true ? false : true)",  "true ? false : true ? false : true",  null                  },
+            { "-(-(2))",                               "-(-2)",                               null                  },
+            { "- - 2",                                 "-(-2)",                               "-(-2)"               },
+            { "x && (y || z)",                         null,                                  null                  },
+            { "(x && y) || z",                         "x && y || z",                         null                  },
+            { "x = (y = z)",                           "x = y = z",                           null                  },
+            { "(--x) + 3",                             "--x + 3",                             null                  },
+            { "(baz.bar).foo(x, (3 + 4) * 5)",         "baz.bar.foo(x, (3 + 4) * 5)",         null                  },
+            { "!(bar instanceof Integer)",             null,                                  null                  },
+            { "(true ? foo : bar).baz()",              null,                                  null                  },
+        };
+        
+        for(int i = 0; i < exprs.length; ++i) {
+            String input = exprs[i][0];
+            String expectSimplify = exprs[i][1];
+            if(expectSimplify == null) {
+                expectSimplify = input;
+            }
+            
+            String expectNoSimplify = exprs[i][2];
+            if(expectNoSimplify == null) {
+                expectNoSimplify = input;
+            }
+            
+            helpTestExpr(input, expectSimplify, true);
+            helpTestExpr(input, expectNoSimplify, false);
+        }
+    }
+
+}
=== tests/src/EvaluatorTests.java
==================================================================
--- tests/src/EvaluatorTests.java (revision 72158)
+++ tests/src/EvaluatorTests.java (patch janino-full level 1)
@@ -53,7 +53,11 @@
         s.addTest(new EvaluatorTests("testGuessParameterNames"));
         s.addTest(new EvaluatorTests("testAssertNotCooked"));
         s.addTest(new EvaluatorTests("testAccessingCompilingClass"));
-        s.addTest(new EvaluatorTests("testProtectedAccessToParentSuperClassVar"));
+        s.addTest(new EvaluatorTests("testProtectedAccessAcrossPackage"));
+        s.addTest(new EvaluatorTests("testProtectedAccessWithinPackage"));
+        s.addTest(new EvaluatorTests("testComplicatedSyntheticAccess"));
+        s.addTest(new EvaluatorTests("testStaticInitAccessProtected"));
+        s.addTest(new EvaluatorTests("test32kBranchLimit"));
         return s;
     }
 
@@ -251,7 +255,7 @@
         assertEquals(8, numTests);
     }
     
-    public void testProtectedAccessToParentSuperClassVar() throws Exception {
+    public void testProtectedAccessAcrossPackage() throws Exception {
         SimpleCompiler sc = new SimpleCompiler();
         sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
         sc.cook("package test;\n" +
@@ -264,4 +268,177 @@
                 "}"
         );
     }
+    
+    public void testProtectedAccessWithinPackage() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
+        sc.cook("package for_sandbox_tests;\n" +
+                "public class Top extends for_sandbox_tests.ProtectedVariable {\n" +
+                "    public class Inner {\n" +
+                "        public int get() {\n" +
+                "            return var;\n" +
+                "        }\n" +
+                "        public void set() {\n" +
+                "            var += 10;\n" +
+                "        }\n" +
+                "        public int getS() {\n" +
+                "            return svar;\n" +
+                "        }\n" +
+                "        public void setS() {\n" +
+                "            svar += 10;\n" +
+                "        }\n" +
+                "    } \n" +
+                "    public Inner createInner() {\n" +
+                "        return new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("for_sandbox_tests.Top");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        Object i = createInner.invoke(t, null);
+        
+        Class innerClass = sc.getClassLoader().loadClass("for_sandbox_tests.Top$Inner");
+        Method get = innerClass.getDeclaredMethod("get", null);
+        Method getS = innerClass.getDeclaredMethod("getS", null);
+        Method set = innerClass.getDeclaredMethod("set", null);
+        Method setS = innerClass.getDeclaredMethod("setS", null);
+        
+        Object res;
+        {   // non-static
+            res = get.invoke(i, null);
+            assertEquals(Integer.valueOf(1), res);
+            set.invoke(i, null);
+            res = get.invoke(i, null);
+            assertEquals(Integer.valueOf(11), res);
+        }
+        {   //static
+            res = getS.invoke(i, null);
+            assertEquals(Integer.valueOf(2), res);
+            setS.invoke(i, null);
+            res = getS.invoke(i, null);
+            assertEquals(Integer.valueOf(12), res);
+        }
+    }
+    
+    public void testComplicatedSyntheticAccess() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
+        sc.cook("package for_sandbox_tests;\n" +
+                "public class L0 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "    public class L1 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "        public class L2 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "            public class Inner {\n" +
+                "                public int getL2() { return L0.L1.L2.this.var; }\n" +
+                "                public int getL1() { return L0.L1.this.var; }\n" +
+                "                public int getL0() { return L0.this.var; }\n" +
+                "                public int setL2() { return L2.this.var = 2; }\n" +
+                "                public int setL1() { return L1.this.var = 1; }\n" +
+                "                public int setL0() { return L0.this.var = 0; }\n" +
+                "            }\n" +
+                "        }\n" +
+                "    }\n" +
+                "    public L0.L1.L2.Inner createInner() {\n" +
+                "        return new L0().new L1().new L2().new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("for_sandbox_tests.L0");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        Object inner = createInner.invoke(t, null);
+        
+        Class innerClass = inner.getClass();
+        Method[] gets = new Method[] {
+                innerClass.getMethod("getL0", null),
+                innerClass.getMethod("getL1", null),
+                innerClass.getMethod("getL2", null),
+        };
+        Method[] sets = new Method[] {
+                innerClass.getMethod("setL0", null),
+                innerClass.getMethod("setL1", null),
+                innerClass.getMethod("setL2", null),
+        };
+        for(int i = 0; i < 3; ++i) {
+            Object g1 = gets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(1), g1);
+            Object s1 = sets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(i), s1);
+            Object g2 = gets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(i), g2);
+        }
+    }
+    
+    public void testStaticInitAccessProtected() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.cook("package test;\n" +
+                "public class Outer extends for_sandbox_tests.ProtectedVariable  {\n" +
+                "    public class Inner {\n" +
+                "        {\n" +
+                "            int t = var;\n" +
+                "            var = svar;\n" +
+                "            svar = t;\n" +
+                "        }\n" +
+                "        private final int i = var;\n" +
+                "        private final int j = svar;\n" +
+                "        {\n" +
+                "            int t = var;\n" +
+                "            var = svar;\n" +
+                "            svar = t;\n" +
+                "        }\n" +
+                "        private final int[] a = new int[] { i, j };\n" +
+                "    }\n" +
+                "    public Inner createInner() {\n" +
+                "        return new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("test.Outer");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        assertNotNull(t);
+        Object inner = createInner.invoke(t, null);
+        assertNotNull(inner);
+    }
+    
+    public void test32kBranchLimit() throws Exception {
+        String preamble =
+            "package test;\n" +
+            "public class Test {\n" +
+            "    public int run() {\n" +
+            "        int res = 0;\n" +
+            "        for(int i = 0; i < 2; ++i) {\n";
+        String middle =
+            "            ++res;\n";
+        String postamble =
+            "        }\n" +
+            "        return res;\n" +
+            "    }\n" +
+            "}";
+        
+        int[] tests = new int[] { 1, 10, 100, Short.MAX_VALUE/5, Short.MAX_VALUE/4, Short.MAX_VALUE/2 };
+        for(int i = 0; i < tests.length; ++i) {
+            int repititions = tests[i];
+            
+            StringBuilder sb = new StringBuilder();
+            sb.append(preamble);
+            for(int j = 0; j < repititions; ++j) {
+                sb.append(middle);
+            }
+            sb.append(postamble);
+            
+            SimpleCompiler sc = new SimpleCompiler();
+            sc.cook(sb.toString());
+            
+            Class c = sc.getClassLoader().loadClass("test.Test");
+            Method m = c.getDeclaredMethod("run", null);
+            Object o = c.newInstance();
+            Object res = m.invoke(o, null);
+            assertEquals(Integer.valueOf(2*repititions), res);
+        }
+        
+    }
 }
=== tests/src/for_sandbox_tests/ProtectedVariable.java
==================================================================
--- tests/src/for_sandbox_tests/ProtectedVariable.java (revision 72158)
+++ tests/src/for_sandbox_tests/ProtectedVariable.java (patch janino-full level 1)
@@ -1,5 +1,6 @@
-package for_sandbox_tests;
-
-public class ProtectedVariable {
-    protected int var = 1;
-}
+package for_sandbox_tests;
+
+public class ProtectedVariable {
+    protected int var = 1;
+    protected static int svar = 2;
+}
=== tests/src/AstTests.java
==================================================================
--- tests/src/AstTests.java (revision 72158)
+++ tests/src/AstTests.java (patch janino-full level 1)
@@ -0,0 +1,392 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.CompileException;
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Location;
+import org.codehaus.janino.Mod;
+import org.codehaus.janino.SimpleCompiler;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayType;
+import org.codehaus.janino.Java.BasicType;
+import org.codehaus.janino.Java.Block;
+import org.codehaus.janino.Java.CompilationUnit;
+import org.codehaus.janino.Java.ExpressionStatement;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableDeclarationStatement;
+import org.codehaus.janino.Java.MethodDeclarator;
+import org.codehaus.janino.Java.PackageMemberClassDeclaration;
+import org.codehaus.janino.Java.ReturnStatement;
+import org.codehaus.janino.Java.Rvalue;
+import org.codehaus.janino.Java.StatementList;
+import org.codehaus.janino.Java.Type;
+import org.codehaus.janino.Java.FunctionDeclarator.FormalParameter;
+import org.codehaus.janino.Parser.ParseException;
+import org.codehaus.janino.Scanner.ScanException;
+
+public class AstTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(AstTests.class.getName());
+        s.addTest(new AstTests("testSimpleAst"));
+        s.addTest(new AstTests("testLocalVariable"));
+        s.addTest(new AstTests("testBlock"));
+        s.addTest(new AstTests("testStatementList"));
+        s.addTest(new AstTests("testByteArrayLiteral"));
+        s.addTest(new AstTests("testClassRef"));
+        s.addTest(new AstTests("testPrecedence"));
+        return s;
+    }
+    
+    public AstTests(String name) { super(name); }
+
+    private static Object compileAndEval(CompilationUnit cu) throws CompileException,
+            ParseException, ScanException, IOException, ClassNotFoundException,
+            InstantiationException, IllegalAccessException,
+            NoSuchMethodException, InvocationTargetException {
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        
+        Class handMadeClass = loader.loadClass("HandMade");
+        
+        Object handMade = handMadeClass.newInstance();
+        Method calc = handMadeClass.getMethod("calculate", null);
+        Object res = calc.invoke(handMade, null);
+        return res;
+    }
+    
+    private static ArrayType createByteArrayType() {
+        return new Java.ArrayType(
+                new Java.BasicType(
+                        getLocation(),
+                        Java.BasicType.BYTE
+                )
+        );
+    };
+    
+    private static PackageMemberClassDeclaration createClass(CompilationUnit cu)
+            throws ParseException {
+        PackageMemberClassDeclaration clazz = new PackageMemberClassDeclaration(
+                getLocation(),
+                null,
+                Mod.PUBLIC,
+                "HandMade",
+                null,
+                new Type[]{}
+        );
+        cu.addPackageMemberTypeDeclaration(clazz);
+        return clazz;
+    }
+    
+    private static Type createDoubleType() {
+        return new BasicType(getLocation(), BasicType.DOUBLE);
+    }
+    
+    private static Java.BinaryOperation createOp(Rvalue l1, String op, Rvalue l2) {
+        return new Java.BinaryOperation(getLocation(), l1, op, l2);
+    }
+    
+    private static Literal createLiteral(double d) {
+        return createLiteral(Double.valueOf(d));
+    }
+    
+    
+    private static Literal createLiteral(Object o) {
+        return new Literal( getLocation(), o );
+    }
+    
+    private static void createMethod(PackageMemberClassDeclaration clazz, Block body, Type returnType) {
+        MethodDeclarator method = new MethodDeclarator(
+                getLocation(),
+                null,
+                (short)(Mod.PUBLIC),
+                returnType,
+                "calculate",
+                new FormalParameter[0],
+                new Type[0],
+                body
+        );
+        clazz.addDeclaredMethod(method);
+    }
+        
+
+    private static LocalVariableDeclarationStatement createVarDecl(String name, double value) {
+        return new Java.LocalVariableDeclarationStatement(
+                getLocation(),
+                (short)0,
+                createDoubleType(),
+                new Java.VariableDeclarator[] {
+                    new Java.VariableDeclarator(
+                            getLocation(),
+                            name,
+                            0,
+                            createLiteral(value)
+                    )
+                }
+        );
+    }
+    
+    private static AmbiguousName createVariableRef(String name) {
+        return new Java.AmbiguousName(
+                getLocation(),
+                new String[] { name }
+        );
+    }
+
+    /**
+     * "Clever" method to get a location from a stack trace
+     */
+    static private Location getLocation() {
+        Exception e = new Exception();
+        StackTraceElement ste = e.getStackTrace()[1];//we only care about our caller
+        return new Location(
+                ste.getFileName(),
+                (short)ste.getLineNumber(),
+                (short)0
+        );
+    }
+
+    
+    public void testBlock() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        Block sub = new Block(getLocation());
+        sub.addStatement( createVarDecl("x", 2.0) );                        
+        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        try {
+            compileAndEval(cu);
+            fail("Block must limit the scope of variables in it");
+        } catch(CompileException ex) {
+            assertTrue(ex.getMessage().endsWith("Expression \"x\" is not an rvalue"));
+        }
+    }
+
+    public void testByteArrayLiteral() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Byte exp = Byte.valueOf((byte)1);
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.NewInitializedArray(
+                                getLocation(),
+                                createByteArrayType(),
+                                new Java.ArrayInitializer(
+                                        getLocation(),
+                                        new Java.Rvalue[] {
+                                            createLiteral(exp)
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                createByteArrayType()
+        );
+        
+        Object res = compileAndEval(cu);
+        assertEquals(exp.byteValue(), ((byte[])res)[0]);
+    }
+
+    public void testLocalVariable() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement( createVarDecl("x", 2.0) );                        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(6.0), res);
+    }
+    
+    public void testSimpleAst() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        createLiteral(3.0)
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertEquals(Double.valueOf(3.0), res);
+    }
+
+    public void testStatementList() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        StatementList sub = new StatementList(getLocation());
+        sub.addStatement( createVarDecl("x", 3.0) );                        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(9.0), res);
+    }
+    
+    public void testClassRef() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.ClassLiteral(
+                                getLocation(),
+                                new Java.ReferenceType(
+                                        getLocation(),
+                                        new String[] {
+                                            "HandMade"
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                new Java.ReferenceType(
+                        getLocation(),
+                        new String[] { "java", "lang", "Class" }
+                )
+        );
+        
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        Class handMadeClass = loader.loadClass("HandMade");
+        Method calc = handMadeClass.getMethod("calculate", null);
+        
+        Object handMade = handMadeClass.newInstance();
+        Object res = calc.invoke(handMade, null);
+        assertEquals(handMadeClass, res);
+    }
+    
+    public void testPrecedence() throws Exception {
+        ExpressionStatement es = new Java.ExpressionStatement(
+                new Java.Assignment(
+                        getLocation(),
+                        new Java.AmbiguousName(
+                                getLocation(),
+                                new String[] { "x" }
+                        ),
+                        "=",
+                        createOp(
+                                createLiteral(1), "*",
+                                createOp(createLiteral(2), "+", createLiteral(3))
+                        )
+                )
+        );
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        uv.visitExpressionStatement(es);
+        assertEquals("x = 1.0D * (2.0D + 3.0D);", sw.toString());
+    }
+}
=== tests/src/AllTests.java
==================================================================
--- tests/src/AllTests.java (revision 72158)
+++ tests/src/AllTests.java (patch janino-full level 1)
@@ -82,5 +82,7 @@
         this.addTest(ReportedBugs.suite());
         this.addTest(SandboxTests.suite());
         this.addTest(EvaluatorTests.suite());
+        this.addTest(AstTests.suite());
+        this.addTest(UnparseTests.suite());
     }
 }
=== src/org/codehaus/janino/UnitCompiler.java
==================================================================
--- src/org/codehaus/janino/UnitCompiler.java (revision 72158)
+++ src/org/codehaus/janino/UnitCompiler.java (patch janino-full level 1)
@@ -346,10 +346,10 @@
                 if (tbd.isStatic()) b.addButDontEncloseStatement((Java.BlockStatement) tbd);
             }
 
-            maybeCreateInitMethod(cd, cf, b);
+            this.maybeCreateInitMethod(cd, cf, b);
         }
 
-        compileDeclaredMethods(cd, cf);
+        this.compileDeclaredMethods(cd, cf);
 
         
         // Compile declared constructors.
@@ -366,8 +366,12 @@
             }
         }
 
+        // A side effect of this call may create synthetic functions to access
+        // protected parent variables
+        this.compileDeclaredMemberTypes(cd, cf);
+
         // Compile the aforementioned extras.
-        compileDeclaredMethods(cd, cf, declaredMethodCount);
+        this.compileDeclaredMethods(cd, cf, declaredMethodCount);
 
         // Class and instance variables.
         for (Iterator it = cd.variableDeclaratorsAndInitializers.iterator(); it.hasNext();) {
@@ -387,7 +391,6 @@
             );
         }
 
-        compileDeclaredMemberTypes(cd, cf);
 
         // Add the generated class file to a thread-local store.
         this.generatedClassFiles.add(cf);
@@ -647,6 +650,7 @@
             public void visitLocalClassDeclarationStatement   (Java.LocalClassDeclarationStatement    lcds) { try { res[0] = UnitCompiler.this.compile2(lcds); } catch (CompileException e) { throw new UCE(e); } }
             public void visitAlternateConstructorInvocation   (Java.AlternateConstructorInvocation    aci ) { try { res[0] = UnitCompiler.this.compile2(aci ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperConstructorInvocation       (Java.SuperConstructorInvocation        sci ) { try { res[0] = UnitCompiler.this.compile2(sci ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (StatementList                          sl  ) { try { res[0] = UnitCompiler.this.compile2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
         };
         try {
             bs.accept(bsv);
@@ -658,13 +662,14 @@
     private boolean compile2(Java.Initializer i) throws CompileException {
         return this.compile(i.block);
     }
-    private boolean compile2(Java.Block b) throws CompileException {
+    //takes a List<BlockStatement>
+    private boolean compile2ListStatement(List l) throws CompileException {
         this.codeContext.saveLocalVariables();
         try {
             boolean previousStatementCanCompleteNormally = true;
-            for (int i = 0; i < b.statements.size(); ++i) {
-                Java.BlockStatement bs = (Java.BlockStatement) b.statements.get(i);
-                if (!previousStatementCanCompleteNormally) {
+            for (int i = 0; i < l.size(); ++i) {
+                Java.BlockStatement bs = (Java.BlockStatement) l.get(i);
+                if (!previousStatementCanCompleteNormally && this.generatesCode(bs)) {
                     this.compileError("Statement is unreachable", bs.getLocation());
                     break;
                 }
@@ -675,6 +680,12 @@
             this.codeContext.restoreLocalVariables();
         }
     }
+    private boolean compile2(Java.Block b) throws CompileException {
+        return compile2ListStatement(b.statements);
+    }
+    private boolean compile2(Java.StatementList sl) throws CompileException {
+        return compile2ListStatement(sl.statements);
+    }
     private boolean compile2(Java.DoStatement ds) throws CompileException {
         Object cvc = this.getConstantValue(ds.condition);
         if (cvc != null) {
@@ -1667,6 +1678,9 @@
 
             // Compile the function body.
             try {
+                if(fd.optionalBody == null) {
+                    this.compileError("Method must have a body", fd.getLocation());
+                }
                 boolean canCompleteNormally = this.compile(fd.optionalBody);
                 if (canCompleteNormally) {
                     if (this.getReturnType(fd) != IClass.VOID) this.compileError("Method must return a value", fd.getLocation());
@@ -1686,12 +1700,9 @@
         // Don't continue code attribute generation if we had compile errors.
         if (this.compileErrorCount > 0) return;
 
-        // Fix up.
-        codeContext.fixUp();
+        // fix up and reallocate as needed
+        codeContext.fixUpAndRelocate();
 
-        // Relocate.
-        codeContext.relocate();
-
         // Do flow analysis.
         if (UnitCompiler.DEBUG) {
             try {
@@ -1770,6 +1781,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName              an   ) { try { UnitCompiler.this.compile2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression      aae  ) { try { UnitCompiler.this.compile2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                fa   ) { try { UnitCompiler.this.compile2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess        ifa  ) { try { UnitCompiler.this.compile2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression      fae  ) { try { UnitCompiler.this.compile2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compile2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess        lva  ) { try { UnitCompiler.this.compile2(lva  ); } catch (CompileException e) { throw new UCE(e); } }
@@ -1953,6 +1965,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { UnitCompiler.this.compileBoolean2(an   , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { UnitCompiler.this.compileBoolean2(aae  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { UnitCompiler.this.compileBoolean2(fa   , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { UnitCompiler.this.compileBoolean2(ifa  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { UnitCompiler.this.compileBoolean2(fae  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compileBoolean2(scfae, dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) { try { UnitCompiler.this.compileBoolean2(lva  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
@@ -2194,6 +2207,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.compileContext2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.compileContext2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.compileContext2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.compileContext2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.compileContext2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.compileContext2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.compileContext2(lva  );                                                                }
@@ -2221,6 +2235,15 @@
             return 1;
         }
     }
+    private int compileContext2(Java.IndirectFieldAccess fa) throws CompileException {
+        if (fa.field.isStatic()) {
+            this.getType(this.toTypeOrCE(fa.lhs));
+            return 0;
+        } else {
+            this.compileGetValue(this.toRvalueOrCE(fa.lhs));
+            return 1;
+        }
+    }
     private int compileContext2(Java.ArrayLength al) throws CompileException {
         if (!this.compileGetValue(al.lhs).isArray()) this.compileError("Cannot determine length of non-array type", al.getLocation());
         return 1;
@@ -2289,6 +2312,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.compileGet2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.compileGet2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.compileGet2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.compileGet2(ifa   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.compileGet2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.compileGet2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.compileGet2(lva  );                                                                }
@@ -2319,6 +2343,30 @@
     private IClass compileGet2(Java.LocalVariableAccess lva) {
         return this.load((Locatable) lva, lva.localVariable);
     }
+    private IClass compileGet2(Java.IndirectFieldAccess fa) throws CompileException {
+        String syntheticMethodName = "get$" + fa.field.getName();
+        AbstractTypeDeclaration type = fa.typeDeclaration;
+        
+        //create the accessor if its here yet
+        Java.MethodDeclarator method = type.getMethodDeclaration(syntheticMethodName);
+        if(method == null) {
+            this.declareIndirectGetMethod(fa);
+            method = type.getMethodDeclaration(syntheticMethodName);
+        }
+        
+
+        // the class or instance will already be on the stack as appropriate
+        // so just invoke the method we generated
+        IClass.IMethod iMethod = this.toIMethod(method);
+        
+        this.writeOpcode(fa, Opcode.INVOKESTATIC);
+        this.writeConstantMethodrefInfo(
+            iMethod.getDeclaringIClass().getDescriptor(), // classFD
+            iMethod.getName(),                            // methodName
+            iMethod.getDescriptor()                       // methodMD
+        );
+        return fa.field.getType();
+    }
     private IClass compileGet2(Java.FieldAccess fa) throws CompileException {
         this.checkAccessible(fa.field, fa.getEnclosingBlockStatement());
         if (fa.field.isStatic()) {
@@ -2393,17 +2441,7 @@
 
         // Check if synthetic method "static Class class$(String className)" is already
         // declared.
-        boolean classDollarMethodDeclared = false;
-        {
-            for (Iterator it = declaringType.declaredMethods.iterator(); it.hasNext();) {
-                Java.MethodDeclarator md = (Java.MethodDeclarator) it.next();
-                if (md.name.equals("class$")) {
-                    classDollarMethodDeclared = true;
-                    break;
-                }
-            }
-        }
-        if (!classDollarMethodDeclared) this.declareClassDollarMethod(cl);
+        if (declaringType.getMethodDeclaration("class$") == null) this.declareClassDollarMethod(cl);
 
         // Determine the statics of the declaring class (this is where static fields
         // declarations are found).
@@ -3288,6 +3326,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.getConstantValue2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.getConstantValue2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.getConstantValue2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.getConstantValue2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.getConstantValue2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.getConstantValue2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getConstantValue2(lva  );                                                    }
@@ -3310,6 +3349,9 @@
     private Object getConstantValue2(Java.FieldAccess fa) throws CompileException {
         return fa.field.getConstantValue();
     }
+    private Object getConstantValue2(Java.IndirectFieldAccess ifa) throws CompileException {
+        return ifa.field.getConstantValue();
+    }
     private Object getConstantValue2(Java.UnaryOperation uo) throws CompileException {
         if (uo.operator.equals("+")) return this.getConstantValue(uo.operand);
         if (uo.operator.equals("-")) return this.getNegatedConstantValue(uo.operand);
@@ -3506,6 +3548,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(an   );                                                    }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(fa   );                                                    }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(ifa  );                                                    }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(lva  );                                                    }
@@ -3555,6 +3598,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { try { res[0] = UnitCompiler.this.generatesCode2(fd  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) {       res[0] = UnitCompiler.this.generatesCode2(ls  );                                                                }
             public void visitBlock                            (Java.Block                             b   ) { try { res[0] = UnitCompiler.this.generatesCode2(b   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { try { res[0] = UnitCompiler.this.generatesCode2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) {       res[0] = UnitCompiler.this.generatesCode2(es  );                                                                }
             public void visitIfStatement                      (Java.IfStatement                       is  ) {       res[0] = UnitCompiler.this.generatesCode2(is  );                                                                }
             public void visitForStatement                     (Java.ForStatement                      fs  ) {       res[0] = UnitCompiler.this.generatesCode2(fs  );                                                                }
@@ -3584,12 +3628,19 @@
     public boolean generatesCode2(Java.EmptyStatement es) { return false; }
     public boolean generatesCode2(Java.LocalClassDeclarationStatement lcds) { return false; }
     public boolean generatesCode2(Java.Initializer i) throws CompileException { return this.generatesCode(i.block); }
-    public boolean generatesCode2(Java.Block b) throws CompileException {
-        for (int i = 0; i < b.statements.size(); ++i) {
-            if (this.generatesCode(((Java.BlockStatement) b.statements.get(i)))) return true;
+    //takes a List<Java.BlockStatement>
+    public boolean generatesCode2ListStatements(List l) throws CompileException {
+        for (int i = 0; i < l.size(); ++i) {
+            if (this.generatesCode(((Java.BlockStatement) l.get(i)))) return true;
         }
         return false;
     }
+    public boolean generatesCode2(Java.Block b) throws CompileException {
+        return generatesCode2ListStatements(b.statements);
+        }
+    public boolean generatesCode2(Java.StatementList sl) throws CompileException {
+        return generatesCode2ListStatements(sl.statements);
+    }
     public boolean generatesCode2(Java.FieldDeclaration fd) throws CompileException {
         // Code is only generated if at least one of the declared variables has a
         // non-constant-final initializer.
@@ -3622,6 +3673,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { UnitCompiler.this.leave2(fd,   optionalStackValueType); }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) { UnitCompiler.this.leave2(ls,   optionalStackValueType); }
             public void visitBlock                            (Java.Block                             b   ) { UnitCompiler.this.leave2(b,    optionalStackValueType); }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { UnitCompiler.this.leave2(sl,   optionalStackValueType); }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) { UnitCompiler.this.leave2(es,   optionalStackValueType); }
             public void visitIfStatement                      (Java.IfStatement                       is  ) { UnitCompiler.this.leave2(is,   optionalStackValueType); }
             public void visitForStatement                     (Java.ForStatement                      fs  ) { UnitCompiler.this.leave2(fs,   optionalStackValueType); }
@@ -3687,6 +3739,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { UnitCompiler.this.compileSet2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { UnitCompiler.this.compileSet2(aae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { UnitCompiler.this.compileSet2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { UnitCompiler.this.compileSet2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { UnitCompiler.this.compileSet2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compileSet2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       UnitCompiler.this.compileSet2(lva  );                                                    }
@@ -3721,6 +3774,32 @@
             fa.field.getDescriptor()                       // fieldFD
         );
     }
+    private void compileSet2(Java.IndirectFieldAccess fa) throws CompileException {
+        String syntheticMethodName = "set$" + fa.field.getName();
+        AbstractTypeDeclaration type = fa.typeDeclaration;
+        
+        //create the accessor if its here yet
+        Java.MethodDeclarator method = type.getMethodDeclaration(syntheticMethodName);
+        if(method == null) {
+            this.declareIndirectSetMethod(fa);
+            method = type.getMethodDeclaration(syntheticMethodName);
+        }
+        
+        // the value we wish to assign is already on the stack,
+        // so we just need to put our new value in place
+        // and then invoke the method we generated
+        IClass.IMethod iMethod = this.toIMethod(method);
+        if(!fa.field.isStatic()) {
+            compileGet(this.toRvalueOrCE(fa.lhs));
+        }
+        
+        this.writeOpcode(fa, Opcode.INVOKESTATIC);
+        this.writeConstantMethodrefInfo(
+            iMethod.getDeclaringIClass().getDescriptor(), // classFD
+            iMethod.getName(),                            // methodName
+            iMethod.getDescriptor()                       // methodMD
+        );
+    }
     private void compileSet2(Java.ArrayAccessExpression aae) throws CompileException {
         this.writeOpcode(aae, Opcode.IASTORE + UnitCompiler.ilfdabcs(this.getType(aae)));
     }
@@ -3775,6 +3854,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.getType2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.getType2(aae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.getType2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.getType2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.getType2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.getType2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getType2(lva  );                                                                }
@@ -3951,6 +4031,9 @@
     private IClass getType2(Java.FieldAccess fa) throws CompileException {
         return fa.field.getType();
     }
+    private IClass getType2(Java.IndirectFieldAccess ifa) throws CompileException {
+        return ifa.field.getType();
+    }
     private IClass getType2(Java.ArrayLength al) {
         return IClass.INT;
     }
@@ -4147,6 +4230,7 @@
         return this.getType(nia.arrayType);
     }
     private IClass getType2(Java.Literal l) {
+        if (l.value instanceof Byte     ) return IClass.BYTE;
         if (l.value instanceof Integer  ) return IClass.INT;
         if (l.value instanceof Long     ) return IClass.LONG;
         if (l.value instanceof Float    ) return IClass.FLOAT;
@@ -4159,6 +4243,7 @@
     }
     private IClass getType2(Java.ConstantValue cv) {
         IClass res = (
+            cv.constantValue instanceof Byte               ? IClass.BYTE    :
             cv.constantValue instanceof Integer            ? IClass.INT     :
             cv.constantValue instanceof Long               ? IClass.LONG    :
             cv.constantValue instanceof Float              ? IClass.FLOAT   :
@@ -4212,6 +4297,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.isType2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.isType2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) {       res[0] = UnitCompiler.this.isType2(fa   );                                                    }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) {       res[0] = UnitCompiler.this.isType2(ifa  );                                                    }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.isType2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.isType2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.isType2(lva  );                                                    }
@@ -4351,10 +4437,10 @@
             }
             parentClass = parentClass.getOuterIClass();
         } while(parentClass != null);
+        
+            return "Protected member cannot be accessed from type \"" + iClassDeclaringContextBlockStatement + "\", which is neither declared in the same package as nor is a subclass of \"" + iClassDeclaringMember + "\".";
+        }
 
-        return "Protected member cannot be accessed from type \"" + iClassDeclaringContextBlockStatement + "\", which is neither declared in the same package as nor is a subclass of \"" + iClassDeclaringMember + "\".";
-    }
-
     /**
      * Determine whether the given {@link IClass} is accessible in the given context,
      * according to JLS2 6.6.1.2 and 6.6.1.4.
@@ -5316,7 +5402,8 @@
         for (Java.Scope s = scope; !(s instanceof Java.CompilationUnit); s = s.getEnclosingScope()) {
             if (s instanceof Java.BlockStatement && enclosingBlockStatement == null) enclosingBlockStatement = (Java.BlockStatement) s;
             if (s instanceof Java.TypeDeclaration) {
-                final IClass etd = UnitCompiler.this.resolve((Java.AbstractTypeDeclaration) s);
+                final Java.AbstractTypeDeclaration enclosingTypeDecl = (Java.AbstractTypeDeclaration)s;
+                final IClass etd = UnitCompiler.this.resolve(enclosingTypeDecl);
                 final IClass.IField f = this.findIField(etd, identifier, location);
                 if (f != null) {
                     if (f.isStatic()) {
@@ -5328,7 +5415,7 @@
                         this.warning("IANSFEI", "Implicit access to non-static field \"" + identifier + "\" of enclosing instance (better write \"" + f.getDeclaringIClass() + ".this." + f.getName() + "\")", location);
                     }
 
-                    Java.Type ct = new Java.SimpleType(scopeTypeDeclaration.getLocation(), (IClass) etd);
+                    Java.SimpleType ct = new Java.SimpleType(scopeTypeDeclaration.getLocation(), (IClass) etd);
                     Java.Atom lhs;
                     if (scopeTBD.isStatic()) {
 
@@ -5348,13 +5435,29 @@
                             lhs = new Java.QualifiedThisReference(location, ct);
                         }
                     }
-                    Java.FieldAccess fa = new Java.FieldAccess(
+                    Java.Rvalue res;
+                    
+                    // accessing an enclosing class's parent's protected variable
+                    // requires an indirect access, seems simple enough ;-)
+                    if(f.getAccess() == Access.PROTECTED
+                            && f.getDeclaringIClass() != etd
+                            && scopeTypeDeclaration != enclosingTypeDecl
+                    ) {
+                        res = new Java.IndirectFieldAccess(
+                                location,
+                                lhs,
+                                f,
+                                enclosingTypeDecl
+                        );
+                    } else {
+                        res = new Java.FieldAccess(
                         location,
                         lhs,
                         f
                     );
-                    fa.setEnclosingBlockStatement(enclosingBlockStatement);
-                    return fa;
+                    }
+                    res.setEnclosingBlockStatement(enclosingBlockStatement);
+                    return res;
                 }
             }
         }
@@ -5470,6 +5573,27 @@
     }
 
     /**
+     * Check to see if a local variable was declared in this statement.  Possibly recursing into a StatementList, as needed.
+     * @param varName  The name of the variable to find
+     * @param stmt     The statement in question
+     * @return         A local variable definition if found.  null if not found
+     * @throws CompileException
+     */
+    private Java.LocalVariable findLocalVariableInStatement(Java.BlockStatement stmt, String varName) throws CompileException {
+        if (stmt instanceof Java.LocalVariableDeclarationStatement) {
+            return this.findLocalVariable((Java.LocalVariableDeclarationStatement)stmt, varName);
+        }
+        if (stmt instanceof Java.StatementList) {
+            Java.StatementList sl = (Java.StatementList)stmt;
+            for (Iterator it = sl.statements.iterator(); it.hasNext();) {
+                Java.LocalVariable lv = findLocalVariableInStatement((Java.BlockStatement)it.next(), varName);
+                if (lv != null) return lv;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Find a local variable declared by the given <code>blockStatement</code> or any enclosing
      * scope up to the {@link Java.FunctionDeclarator}.
      */
@@ -5482,19 +5606,15 @@
             {
                 if (s instanceof Java.ForStatement) {
                     Java.BlockStatement optionalForInit = ((Java.ForStatement) s).optionalInit;
-                    if (optionalForInit instanceof Java.LocalVariableDeclarationStatement) {
-                        Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) optionalForInit, name);
+                    Java.LocalVariable lv = this.findLocalVariableInStatement(optionalForInit, name);
                         if (lv != null) return lv;
                     }
-                }
                 if (es instanceof Java.Block) {
                     Java.Block b = (Java.Block) es;
-                    for (Iterator it = b.statements.iterator();;) {
+                    for (Iterator it = b.statements.iterator(); it.hasNext();) {
                         Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                        if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                            Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                        Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                             if (lv != null) return lv;
-                        }
                         if (bs2 == s) break;
                     }
                 }
@@ -5504,10 +5624,8 @@
                         Java.SwitchStatement.SwitchBlockStatementGroup sbgs = (Java.SwitchStatement.SwitchBlockStatementGroup) it2.next();
                         for (Iterator it = sbgs.blockStatements.iterator(); it.hasNext();) {
                             Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                            if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                                Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                            Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                                 if (lv != null) return lv;
-                            }
                             if (bs2 == s) break SBSGS;
                         }
                     }
@@ -5545,6 +5663,30 @@
         return null;
     }
 
+    private Java.AbstractTypeDeclaration findTypeDeclaration(Java.Rvalue rvalue) {
+        Java.Scope s = rvalue.getEnclosingBlockStatement().getEnclosingScope();
+        for(; !(s instanceof Java.CompilationUnit); s = s.getEnclosingScope()) {
+            if(s instanceof Java.AbstractTypeDeclaration) {
+                return (Java.AbstractTypeDeclaration)s;
+            }
+        }
+        return null;
+    }
+    
+    private Java.AbstractTypeDeclaration findTypeDeclaration(IClass iclass) {
+        List types = new ArrayList(); // Java.AbstractTypeDeclaration
+        Java.CompilationUnit cu = this.compilationUnit;
+        types.addAll(cu.packageMemberTypeDeclarations);
+        
+        for(int i = 0; i < types.size(); ++i) {
+            Java.AbstractTypeDeclaration td = (Java.AbstractTypeDeclaration) types.get(i);
+            IClass option = this.resolve(td);
+            if(option == iclass) { return td; }
+            types.addAll(td.getMemberTypeDeclarations());
+        }
+        return null;
+    }
+
     private void determineValue(Java.FieldAccessExpression fae) throws CompileException {
         if (fae.value != null) return;
 
@@ -5567,12 +5709,29 @@
                 };
                 return;
             }
+
+            // accessing an enclosing class's parent's protected variable
+            // requires an indirect access, seems simple enough ;-)
+            Java.AbstractTypeDeclaration scopeClass = this.findTypeDeclaration(fae);
+            Java.AbstractTypeDeclaration enclosingTypeDecl = this.findTypeDeclaration(lhsType);
+            if(iField.getAccess() == Access.PROTECTED
+                    && iField.getDeclaringIClass() != lhsType
+                    && scopeClass != enclosingTypeDecl
+            ) {
+                fae.value = new Java.IndirectFieldAccess(
+                        fae.getLocation(),
+                        fae.lhs,
+                        iField,
+                        enclosingTypeDecl
+                );
+            } else {
             fae.value = new Java.FieldAccess(
                 fae.getLocation(),
                 fae.lhs,
                 iField
             );
         }
+        }
         fae.value.setEnclosingBlockStatement(fae.getEnclosingBlockStatement());
     }
 
@@ -6665,7 +6824,148 @@
         return importedClass;
     }
     private final Map onDemandImportableTypes = new HashMap();   // String simpleTypeName => IClass
+    private void declareIndirectSetMethod(Java.IndirectFieldAccess ifa) throws CompileException {
+        // Method "set$XXX" is not yet declared; declare it like
+        //
+        // static field access looks like:
+        //   static FIELD_TYPE set$var(FIELD_TYPE value) {
+        //       return Class.var = value;
+        //   }
+        //
+        // non-static field access looks like:
+        //   static FIELD_TYPE set$var(FIELD_TYPE value, Class arg) {
+        //       return arg.var = value;
+        //   }
+        Location loc = ifa.getLocation();
+        
+        Java.Lvalue accessExpr;
+        Java.FunctionDeclarator.FormalParameter[] params;
+        Java.FunctionDeclarator.FormalParameter newValueParam = new Java.FunctionDeclarator.FormalParameter(
+                loc,
+                false,
+                new Java.SimpleType(loc, ifa.field.getType()),
+                "value"
+        );
+        Java.SimpleType classType = new Java.SimpleType(loc, ifa.typeDeclaration.resolvedType);
+        if(ifa.field.isStatic()) {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    newValueParam
+            };
+            
+            accessExpr = new Java.FieldAccessExpression(
+                    loc,
+                    classType,
+                    ifa.field.getName()
+            );
+        } else {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    newValueParam,
+                    new Java.FunctionDeclarator.FormalParameter(
+                            loc,
+                            false,
+                            classType,
+                            "parentClass"
+                    ),
+            };
+            accessExpr = new Java.AmbiguousName(
+                    loc,
+                    new String[] { "parentClass", ifa.field.getName() }
+            );
+        }
+        
+        Java.ReturnStatement ret = new Java.ReturnStatement(
+                loc,
+                new Java.Assignment(
+                        loc,
+                        accessExpr,
+                        "=",
+                        new Java.AmbiguousName(
+                                loc,
+                                new String[] { "value" }
+                        )
+                )
+        );
+        Java.Block body = new Java.Block(loc);
+        body.addStatement(ret);
+        
+        Java.MethodDeclarator method = new Java.MethodDeclarator(
+                loc,                                                  // location
+                null,                                                 // optionalDocComment
+                Mod.STATIC,                                           // modifiers
+                new Java.SimpleType(loc, ifa.field.getType()),        // type
+                "set$"+ifa.field.getName(),                           // name
+                params,                                               // formalPameters
+                new Java.Type[0],                                     // thrownExceptions
+                body                                                  // optionalBody
+        );
 
+        ifa.typeDeclaration.addDeclaredMethod(method);
+        ifa.typeDeclaration.invalidateMethodCaches();
+    }
+    
+    private void declareIndirectGetMethod(Java.IndirectFieldAccess ifa) throws CompileException {
+        // Method "get$XXX" is not yet declared; declare it like
+        //
+        // static field access looks like:
+        //   static FIELD_TYPE get$var() {
+        //       return Class.var;
+        //   }
+        //
+        // non-static field access looks like:
+        //   static FIELD_TYPE get$var(Class arg) {
+        //       return arg.var;
+        //   }
+        Location loc = ifa.getLocation();
+        
+        Java.Rvalue getExpr;
+        Java.FunctionDeclarator.FormalParameter[] params;
+        if(ifa.field.isStatic()) {
+            params = new Java.FunctionDeclarator.FormalParameter[0];
+            getExpr = new Java.FieldAccessExpression(
+                    loc,
+                    new Java.SimpleType(
+                            loc,
+                            ifa.typeDeclaration.resolvedType
+                    ),
+                    ifa.field.getName()
+            );
+        } else {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    new Java.FunctionDeclarator.FormalParameter(
+                            loc,
+                            false,
+                            new Java.SimpleType(
+                                    loc,
+                                    ifa.typeDeclaration.resolvedType
+                            ),
+                            "parentClass"
+                    )
+            };
+            getExpr = new Java.AmbiguousName(
+                    loc,
+                    new String[] { "parentClass", ifa.field.getName() }
+            );
+        }
+        
+        Java.ReturnStatement ret = new Java.ReturnStatement(loc, getExpr);
+        Java.Block body = new Java.Block(loc);
+        body.addStatement(ret);
+        
+        Java.MethodDeclarator method = new Java.MethodDeclarator(
+                loc,                                                  // location
+                null,                                                 // optionalDocComment
+                Mod.STATIC,                                           // modifiers
+                new Java.SimpleType(loc, ifa.field.getType()),        // type
+                "get$"+ifa.field.getName(),                           // name
+                params,                                               // formalPameters
+                new Java.Type[0],                                     // thrownExceptions
+                body                                                  // optionalBody
+        );
+        
+        ifa.typeDeclaration.addDeclaredMethod(method);
+        ifa.typeDeclaration.invalidateMethodCaches();
+    }
+
     private void declareClassDollarMethod(Java.ClassLiteral cl) {
 
         // Method "class$" is not yet declared; declare it like
@@ -6771,12 +7071,7 @@
         );
 
         declaringType.addDeclaredMethod(cdmd);
-
-        // Invalidate several caches.
-        if (declaringType.resolvedType != null) {
-            declaringType.resolvedType.declaredIMethods = null;
-            declaringType.resolvedType.declaredIMethodCache = null;
-        }
+        declaringType.invalidateMethodCaches();
     }
 
     private IClass pushConstant(Locatable l, Object value) {
=== src/org/codehaus/janino/CodeContext.java
==================================================================
--- src/org/codehaus/janino/CodeContext.java (revision 72158)
+++ src/org/codehaus/janino/CodeContext.java (patch janino-full level 1)
@@ -34,8 +34,15 @@
 
 package org.codehaus.janino;
 
-import java.io.*;
-import java.util.*;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
 
 import org.codehaus.janino.util.ClassFile;
 
@@ -135,10 +142,10 @@
         DataOutputStream dos,
         short            lineNumberTableAttributeNameIndex
     ) throws IOException {
-        dos.writeShort(this.maxStack);                     // max_stack
-        dos.writeShort(this.maxLocals);                    // max_locals
-        dos.writeInt(0xffff & this.end.offset);            // code_length
-        dos.write(this.code, 0, 0xffff & this.end.offset); // code
+        dos.writeShort(this.maxStack);            // max_stack
+        dos.writeShort(this.maxLocals);           // max_locals
+        dos.writeInt(this.end.offset);            // code_length
+        dos.write(this.code, 0, this.end.offset); // code
         dos.writeShort(this.exceptionTableEntries.size());        // exception_table_length
         for (int i = 0; i < this.exceptionTableEntries.size(); ++i) { // exception_table
             ExceptionTableEntry exceptionTableEntry = (ExceptionTableEntry) this.exceptionTableEntries.get(i);
@@ -178,17 +185,21 @@
      * Notice: On inconsistencies, a "RuntimeException" is thrown (KLUDGE).
      */
     public void flowAnalysis(String functionName) {
-        byte[] stackSizes = new byte[0xffff & this.end.offset];
+        if(CodeContext.DEBUG) {
+            System.err.println("flowAnalysis(" + functionName + ")");
+        }
+        
+        byte[] stackSizes = new byte[this.end.offset];
         Arrays.fill(stackSizes, CodeContext.UNEXAMINED);
 
         // Analyze flow from offset zero.
         this.flowAnalysis(
             functionName,
-            this.code,                // code
-            0xffff & this.end.offset, // codeSize
-            0,                        // offset
-            0,                        // stackSize
-            stackSizes                // stackSizes
+            this.code,       // code
+            this.end.offset, // codeSize
+            0,               // offset
+            0,               // stackSize
+            stackSizes       // stackSizes
         );
 
         // Analyze flow from exception handler entry points.
@@ -196,14 +207,14 @@
         while (analyzedExceptionHandlers != this.exceptionTableEntries.size()) {
             for (int i = 0; i < this.exceptionTableEntries.size(); ++i) {
                 ExceptionTableEntry exceptionTableEntry = (ExceptionTableEntry) this.exceptionTableEntries.get(i);
-                if (stackSizes[0xffff & exceptionTableEntry.startPC.offset] != CodeContext.UNEXAMINED) {
+                if (stackSizes[exceptionTableEntry.startPC.offset] != CodeContext.UNEXAMINED) {
                     this.flowAnalysis(
                         functionName,        
-                        this.code,                                                   // code
-                        0xffff & this.end.offset,                                    // codeSize
-                        0xffff & exceptionTableEntry.handlerPC.offset,               // offset
-                        stackSizes[0xffff & exceptionTableEntry.startPC.offset] + 1, // stackSize
-                        stackSizes                                                   // stackSizes
+                        this.code,                                          // code
+                        this.end.offset,                                    // codeSize
+                        exceptionTableEntry.handlerPC.offset,               // offset
+                        stackSizes[exceptionTableEntry.startPC.offset] + 1, // stackSize
+                        stackSizes                                          // stackSizes
                     );
                     ++analyzedExceptionHandlers;
                 }
@@ -287,7 +298,7 @@
                 /* FALL THROUGH */
             case Opcode.SD_GETSTATIC:
                 stackSize += this.determineFieldSize((short) (
-                    (code[operandOffset] << 8) + (0xff & code[operandOffset + 1])
+                    extract16BitValue(0, operandOffset, code)
                 ));
                 break;
 
@@ -296,7 +307,7 @@
                 /* FALL THROUGH */
             case Opcode.SD_PUTSTATIC:
                 stackSize -= this.determineFieldSize((short) (
-                    (code[operandOffset] << 8) + (0xff & code[operandOffset + 1])
+                    extract16BitValue(0, operandOffset, code)
                 ));
                 break;
 
@@ -307,7 +318,7 @@
                 /* FALL THROUGH */
             case Opcode.SD_INVOKESTATIC:
                 stackSize -= this.determineArgumentsSize((short) (
-                    (code[operandOffset] << 8) + (0xff & code[operandOffset + 1])
+                    extract16BitValue(0, operandOffset, code)
                 ));
                 break;
 
@@ -368,13 +379,11 @@
                 this.flowAnalysis(
                     functionName,
                     code, codeSize,
-                    offset + (
-                        ((       code[operandOffset++]) << 8) +
-                        ((0xff & code[operandOffset++])     )
-                    ),
+                    extract16BitValue(offset, operandOffset, code),
                     stackSize,
                     stackSizes
                 );
+                operandOffset += 2;
                 break;
 
             case Opcode.OP1_JSR:
@@ -384,10 +393,8 @@
                     System.out.println(code[operandOffset]);
                     System.out.println(code[operandOffset + 1]);
                 }
-                int targetOffset = offset + (
-                    ((       code[operandOffset++]) << 8) +
-                    ((0xff & code[operandOffset++])     )
-                );
+                int targetOffset = extract16BitValue(offset, operandOffset, code);
+                operandOffset += 2;
                 if (stackSizes[targetOffset] == CodeContext.UNEXAMINED) {
                     this.flowAnalysis(
                         functionName,
@@ -403,14 +410,10 @@
                 this.flowAnalysis(
                     functionName,
                     code, codeSize,
-                    offset + (
-                        ((       code[operandOffset++]) << 24) +
-                        ((0xff & code[operandOffset++]) << 16) +
-                        ((0xff & code[operandOffset++]) <<  8) +
-                        ((0xff & code[operandOffset++])      )
-                    ),
+                    extract32BitValue(offset, operandOffset, code),
                     stackSize, stackSizes
                 );
+                operandOffset += 4;
                 break;
 
             case Opcode.OP1_LOOKUPSWITCH:
@@ -418,33 +421,23 @@
                 this.flowAnalysis(
                     functionName,
                     code, codeSize,
-                    offset + (
-                        ((       code[operandOffset++]) << 24) +
-                        ((0xff & code[operandOffset++]) << 16) +
-                        ((0xff & code[operandOffset++]) <<  8) +
-                        ((0xff & code[operandOffset++])      )
-                    ),
+                    extract32BitValue(offset, operandOffset, code),
                     stackSize, stackSizes
                 );
-                int npairs = (
-                    ((       code[operandOffset++]) << 24) +
-                    ((0xff & code[operandOffset++]) << 16) +
-                    ((0xff & code[operandOffset++]) <<  8) +
-                    ((0xff & code[operandOffset++])      )
-                );
+                operandOffset += 4;
+                
+                int npairs = extract32BitValue(0, operandOffset, code);
+                operandOffset += 4;
+                
                 for (int i = 0; i < npairs; ++i) {
-                    operandOffset += 4;
+                    operandOffset += 4; //skip match value
                     this.flowAnalysis(
                         functionName,
                         code, codeSize,
-                        offset + (
-                            ((       code[operandOffset++]) << 24) +
-                            ((0xff & code[operandOffset++]) << 16) +
-                            ((0xff & code[operandOffset++]) <<  8) +
-                            ((0xff & code[operandOffset++])      )
-                        ),
+                        extract32BitValue(offset, operandOffset, code),
                         stackSize, stackSizes
                     );
+                    operandOffset += 4; //advance over offset
                 }
                 break;
 
@@ -453,38 +446,22 @@
                 this.flowAnalysis(
                     functionName,
                     code, codeSize,
-                    offset + (
-                        ((       code[operandOffset++]) << 24) +
-                        ((0xff & code[operandOffset++]) << 16) +
-                        ((0xff & code[operandOffset++]) <<  8) +
-                        ((0xff & code[operandOffset++])      )
-                    ),
+                    extract32BitValue(offset, operandOffset, code),
                     stackSize, stackSizes
                 );
-                int low = (
-                    ((       code[operandOffset++]) << 24) +
-                    ((0xff & code[operandOffset++]) << 16) +
-                    ((0xff & code[operandOffset++]) <<  8) +
-                    ((0xff & code[operandOffset++])      )
-                );
-                int hi = (
-                    ((       code[operandOffset++]) << 24) +
-                    ((0xff & code[operandOffset++]) << 16) +
-                    ((0xff & code[operandOffset++]) <<  8) +
-                    ((0xff & code[operandOffset++])      )
-                );
+                operandOffset += 4;
+                int low = extract32BitValue(offset, operandOffset, code);
+                operandOffset += 4;
+                int hi = extract32BitValue(offset, operandOffset, code);
+                operandOffset += 4;
                 for (int i = low; i <= hi; ++i) {
                     this.flowAnalysis(
                         functionName,
                         code, codeSize,
-                        offset + (
-                            ((       code[operandOffset++]) << 24) +
-                            ((0xff & code[operandOffset++]) << 16) +
-                            ((0xff & code[operandOffset++]) <<  8) +
-                            ((0xff & code[operandOffset++])      )
-                        ),
+                        extract32BitValue(offset, operandOffset, code),
                         stackSize, stackSizes
                     );
+                    operandOffset += 4;
                 }
                 break;
 
@@ -530,20 +507,93 @@
             offset = operandOffset;
         }
     }
+    
+    /**
+     * Extract a 16 bit value at offset in code and add bias to it
+     * @param bias    an int to skew the final result by (useful for calculating relative offsets)
+     * @param offset  the position in the code array to extract the bytes from
+     * @param code    the array of bytes
+     * @return an integer that treats the two bytes at position offset as an UNSIGNED SHORT
+     */
+    private int extract16BitValue(int bias, int offset, byte[] code) {
+        int res = bias + (
+                ((       code[offset  ]) << 8) +
+                ((0xff & code[offset+1])     )
+        );
+        if (CodeContext.DEBUG) {
+            System.out.println("extract16BitValue(bias, offset) = (" + bias +", " + offset + ")");
+            System.out.println("bytes = {" + code[offset] + ", " + code[offset+1] + "}");
+            System.out.println("result = " + res);
+        }
+        return res;
+    }
+    
+    /**
+     * Extract a 32 bit value at offset in code and add bias to it
+     * @param bias    an int to skew the final result by (useful for calculating relative offsets)
+     * @param offset  the position in the code array to extract the bytes from
+     * @param code    the array of bytes
+     * @return the 4 bytes at position offset + bias
+     */
+    private int extract32BitValue(int bias, int offset, byte[] code) {
+        int res = bias + (
+                ((       code[offset  ]) << 24) +
+                ((0xff & code[offset+1]) << 16) +
+                ((0xff & code[offset+2]) <<  8) +
+                ((0xff & code[offset+3])      )
+        );
+        if (CodeContext.DEBUG) {
+            System.out.println("extract32BitValue(bias, offset) = (" + bias +", " + offset + ")");
+            System.out.println(
+                    "bytes = {" +
+                    code[offset  ] + ", " +
+                    code[offset+1] + ", " +
+                    code[offset+2] + ", " +
+                    code[offset+3] + "}"
+            );
+            System.out.println("result = " + res);
+        }
+        return res;
+    }
+    
+    /**
+     * fixUp() all of the offsets and relocate() all relocatables
+     */
+    public void fixUpAndRelocate() {
+        //we do this in a loop to allow relocatables to adjust the size
+        //of things in the byte stream.  It is extremely unlikely, but possible
+        //that a late relocatable will grow the size of the bytecode, and require
+        //an earlier relocatable to switch from 32K mode to 64K mode branching
+        boolean finished = false;
+        while(!finished) {
+            fixUp();
+            finished = relocate();
+        }
+    }
 
     /**
      * Fix up all offsets.
      */
-    public void fixUp() {
+    private void fixUp() {
         for (Offset o = this.beginning; o != this.end; o = o.next) {
             if (o instanceof FixUp) ((FixUp) o).fixUp();
         }
     }
 
-    public void relocate() {
+    /**
+     * Relocate all relocatables and aggregate their response into a single one
+     * @return true if all of them relocated successfully
+     *         false if any of them needed to change size
+     */
+    private boolean relocate() {
+        boolean finished = true;
         for (int i = 0; i < this.relocatables.size(); ++i) {
-            ((Relocatable) this.relocatables.get(i)).relocate();
+            //do not terminate earlier so that everything gets a chance to grow in the first pass
+            //changes the common case for this to be O(n) instead of O(n**2)
+            boolean part =  ((Relocatable) this.relocatables.get(i)).relocate();
+            finished = finished && part;
         }
+        return finished;
     }
 
     /**
@@ -627,15 +677,15 @@
             this.currentInserter.prev = lno;
         }
 
-        int ico = 0xffff & this.currentInserter.offset;
-        if ((0xffff & this.end.offset) + b.length <= this.code.length) {
-            System.arraycopy(this.code, ico, this.code, ico + b.length, (0xffff & this.end.offset) - ico);
+        int ico = this.currentInserter.offset;
+        if (this.end.offset + b.length <= this.code.length) {
+            System.arraycopy(this.code, ico, this.code, ico + b.length, this.end.offset - ico);
         } else {
             byte[] oldCode = this.code;
             this.code = new byte[this.code.length + CodeContext.SIZE_INCREMENT];
             if (this.code.length >= 0xffff) throw new RuntimeException("Code attribute in class \"" + this.classFile.getThisClassName() + "\" grows beyond 64 KB");
             System.arraycopy(oldCode, 0, this.code, 0, ico);
-            System.arraycopy(oldCode, ico, this.code, ico + b.length, (0xffff & this.end.offset) - ico);
+            System.arraycopy(oldCode, ico, this.code, ico + b.length, this.end.offset - ico);
         }
         System.arraycopy(b, 0, this.code, ico, b.length);
         for (Offset o = this.currentInserter; o != null; o = o.next) o.offset += b.length;
@@ -652,23 +702,123 @@
      * @param lineNumber The line number that corresponds to the byte code, or -1
      */
     public void writeBranch(short lineNumber, int opcode, final Offset dst) {
-        this.relocatables.add(new Branch(this.newOffset(), dst));
+        this.relocatables.add(new Branch(opcode, dst));
         this.write(lineNumber, new byte[] { (byte) opcode, -1, -1 });
     }
+    
+    private static final Map EXPANDED_BRANCH_OPS = new HashMap(); // Map<Byte, Byte>
+    static {
+        //comparisons expand by doing a negated jump as follows:
+        //  [if cond offset]
+        //expands to
+        //  [if !cond skip_goto]
+        //  [GOTO_W offset]
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ACMPEQ), Byte.valueOf(Opcode.IF_ACMPNE));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ACMPNE), Byte.valueOf(Opcode.IF_ACMPEQ));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ICMPEQ), Byte.valueOf(Opcode.IF_ICMPNE));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ICMPNE), Byte.valueOf(Opcode.IF_ICMPEQ));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ICMPGE), Byte.valueOf(Opcode.IF_ICMPLT));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ICMPLT), Byte.valueOf(Opcode.IF_ICMPGE));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ICMPGT), Byte.valueOf(Opcode.IF_ICMPLE));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IF_ICMPLE), Byte.valueOf(Opcode.IF_ICMPGT));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFEQ), Byte.valueOf(Opcode.IFNE));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFNE), Byte.valueOf(Opcode.IFEQ));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFGE), Byte.valueOf(Opcode.IFLT));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFLT), Byte.valueOf(Opcode.IFGE));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFGT), Byte.valueOf(Opcode.IFLE));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFLE), Byte.valueOf(Opcode.IFGT));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFNULL), Byte.valueOf(Opcode.IFNONNULL));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.IFNONNULL), Byte.valueOf(Opcode.IFNULL));
+        
+        // these merely expand to their wide version
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.GOTO), Byte.valueOf(Opcode.GOTO_W));
+        EXPANDED_BRANCH_OPS.put(Byte.valueOf(Opcode.JSR), Byte.valueOf(Opcode.JSR_W));
+    }
 
     private class Branch extends Relocatable {
-        public Branch(Offset source, Offset destination) {
-            this.source = source;
+        public Branch(int opcode, Offset destination) {
+            this.opcode = opcode;
+            this.source = CodeContext.this.newInserter();
             this.destination = destination;
+            if(opcode == Opcode.JSR_W || opcode == Opcode.GOTO_W) {
+                //no need to expand wide opcodes
+                this.expanded = true;
+            } else {
+                this.expanded = false;
+            }
         }
-        public void relocate() {
+        
+        public boolean relocate() {
             if (this.destination.offset == Offset.UNSET) throw new RuntimeException("Cannot relocate branch to unset destination offset");
             int offset = this.destination.offset - this.source.offset;
-            if (offset > Short.MAX_VALUE || offset < Short.MIN_VALUE) throw new RuntimeException("Branch offset out of range");
-            byte[] ba = new byte[] { (byte) (offset >> 8), (byte) offset };
-            System.arraycopy(ba, 0, CodeContext.this.code, (0xffff & this.source.offset) + 1, 2);
+            
+            if (!expanded && (offset > Short.MAX_VALUE || offset < Short.MIN_VALUE)) {
+                //we want to insert the data without skewing our source position,
+                //so we will cache it and then restore it later.
+                int pos = this.source.offset;
+                CodeContext.this.pushInserter(source); {
+                    // promotion to a wide instruction only requires 2 extra bytes
+                    // everything else requires a new GOTO_W instruction after a negated if
+                    CodeContext.this.write(
+                            (short) -1,
+                            new byte[opcode == Opcode.GOTO ? 2 :
+                                     opcode == Opcode.JSR ? 2 :
+                                     5]
+                    );
+                } CodeContext.this.popInserter();
+                this.source.offset = pos;
+                expanded = true;
+                return false;
+            }
+            
+            final byte[] ba;
+            if(!expanded) {
+                //we fit in a 16-bit jump
+                ba = new byte[] { (byte)opcode, (byte) (offset >> 8), (byte) offset };
+            } else {
+                byte inverted = ((Byte)CodeContext.EXPANDED_BRANCH_OPS.get(
+                        Byte.valueOf((byte)opcode))
+                ).byteValue();
+                if(opcode == Opcode.GOTO || opcode == Opcode.JSR) {
+                    //  [GOTO offset]
+                    //expands to
+                    //  [GOTO_W offset]
+                    ba = new byte[] {
+                            (byte) inverted,
+                            (byte) (offset >> 24),
+                            (byte) (offset >> 16),
+                            (byte) (offset >> 8),
+                            (byte) offset
+                    };
+                } else {
+                    //exclude the if-statement from jump target
+                    //if jumping backwards this will increase the jump to go over it
+                    //if jumping forwards this will decrease the jump by it
+                    offset -= 3;
+                            
+                    //  [if cond offset]
+                    //expands to
+                    //  [if !cond skip_goto]
+                    //  [GOTO_W offset]
+                    ba = new byte[] {
+                            (byte) inverted,
+                            (byte) 0,
+                            (byte) 8, //jump from this instruction past the GOTO_W
+                            (byte) Opcode.GOTO_W,
+                            (byte) (offset >> 24),
+                            (byte) (offset >> 16),
+                            (byte) (offset >> 8),
+                            (byte) offset
+                    };
+                }
+            }
+            System.arraycopy(ba, 0, CodeContext.this.code, this.source.offset, ba.length);
+            return true;
         }
-        private final Offset source;
+        
+        private boolean expanded; //marks whether this has been expanded to account for a wide branch
+        private final int opcode;
+        private final Inserter source;
         private final Offset destination;
     }
 
@@ -683,7 +833,7 @@
             this.source      = source;
             this.destination = destination;
         }
-        public void relocate() {
+        public boolean relocate() {
             if (
                 this.source.offset == Offset.UNSET ||
                 this.destination.offset == Offset.UNSET
@@ -695,7 +845,8 @@
                 (byte) (offset >> 8),
                 (byte) offset
             };
-            System.arraycopy(ba, 0, CodeContext.this.code, 0xffff & this.where.offset, 4);
+            System.arraycopy(ba, 0, CodeContext.this.code, this.where.offset, 4);
+            return true;
         }
         private final Offset where, source, destination;
     }
@@ -755,9 +906,9 @@
      * automatically shifted.
      */
     public class Offset {
-        short              offset = Offset.UNSET;
-        Offset             prev = null, next = null;
-        final static short UNSET = -1;
+        int              offset = Offset.UNSET;
+        Offset           prev = null, next = null;
+        final static int UNSET = -1;
 
         /**
          * Set this "Offset" to the offset of the current inserter; insert
@@ -776,7 +927,7 @@
         public final CodeContext getCodeContext() { return CodeContext.this; }
 
         public String toString() {
-            return CodeContext.this.classFile.getThisClassName() + ": " + (0xffff & this.offset);
+            return CodeContext.this.classFile.getThisClassName() + ": " + this.offset;
         }
     }
 
@@ -830,15 +981,20 @@
     }
 
     public class LineNumberOffset extends Offset {
-        private final short lineNumber;
-        public LineNumberOffset(short offset, short lineNumber) {
+        private final int lineNumber;
+        public LineNumberOffset(int offset, int lineNumber) {
             this.lineNumber = lineNumber;
             this.offset = offset;
         }
     }
 
     private abstract class Relocatable {
-        public abstract void relocate();
+        /**
+         * Relocate this object.
+         * @return true if the relocation succeeded in place
+         *         false if the relocation grew the number of bytes required
+         */
+        public abstract boolean relocate();
     }
 
     private short       localVariableArrayLength = 0;
=== src/org/codehaus/janino/Java.java
==================================================================
--- src/org/codehaus/janino/Java.java (revision 72158)
+++ src/org/codehaus/janino/Java.java (patch janino-full level 1)
@@ -36,6 +36,9 @@
 
 import java.util.*;
 
+import org.codehaus.janino.Visitor.AtomVisitor;
+import org.codehaus.janino.Visitor.LvalueVisitor;
+import org.codehaus.janino.Visitor.RvalueVisitor;
 import org.codehaus.janino.util.*;
 import org.codehaus.janino.util.iterator.*;
 
@@ -77,6 +80,7 @@
         private final Location location;
 
         protected Located(Location location) {
+            //assert location != null;
             this.location = location;
         }
 
@@ -353,6 +357,13 @@
             this.declaredMethods.add(method);
             method.setDeclaringType(this);
         }
+        
+        public void invalidateMethodCaches() {
+            if (this.resolvedType != null) {
+                this.resolvedType.declaredIMethods = null;
+                this.resolvedType.declaredIMethodCache = null;
+            }
+        }
 
         // Implement TypeDeclaration.
         public void addMemberTypeDeclaration(MemberTypeDeclaration mcoid) {
@@ -369,6 +380,13 @@
             }
             return null;
         }
+        public MethodDeclarator getMethodDeclaration(String name) {
+            for (Iterator it = this.declaredMethods.iterator(); it.hasNext();) {
+                MethodDeclarator md = (MethodDeclarator) it.next();
+                if (md.name.equals(name)) return md;
+            }
+            return null;
+        }
         public String createLocalTypeName(String localTypeName) {
             return (
                 this.getClassName()
@@ -1227,10 +1245,52 @@
         }
 
         // Compile time members.
-
         public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitBlock(this); }
     }
+    
+    /**
+     * This is similar to a {@link Java.Block} except that it does not create a scope around it statements.
+     * It is useful for programmatically manipulating an AST
+     */
+    public final static class StatementList extends Statement {
+        public final List statements = new ArrayList(); // BlockStatement
 
+        public StatementList(Location location) {
+            super(location);
+        }
+
+        public void addStatement(BlockStatement statement) {
+            this.statements.add(statement);
+            statement.setEnclosingScope(this.getEnclosingScope());
+        }
+        
+        public void addStatements(
+            List statements // BlockStatement
+        ) {
+            Iterator stmtIter = statements.iterator();
+            while(stmtIter.hasNext()) {
+                addStatement((BlockStatement)stmtIter.next());
+            }
+        }
+        
+        public BlockStatement[] getStatements() {
+            return (BlockStatement[]) this.statements.toArray(new BlockStatement[this.statements.size()]);
+        }
+        
+        // set children to share this object's enclosing scope
+        public void setEnclosingScope(Scope enclosingScope) {
+            super.setEnclosingScope(enclosingScope);
+            Iterator stmtIter = this.statements.iterator();
+            while(stmtIter.hasNext()) {
+                BlockStatement stmt = (BlockStatement)stmtIter.next();
+                stmt.setEnclosingScope(enclosingScope);
+            }
+        }
+
+        // Compile time members.
+        public final void accept(Visitor.BlockStatementVisitor visitor) { visitor.visitStatementList(this); }
+    }
+
     /**
      * Base class for statements that can be terminated abnormally with a
      * "break" statement.
@@ -1978,7 +2038,39 @@
         public final void accept(Visitor.RvalueVisitor visitor) { visitor.visitLocalVariableAccess(this); }
         public final void accept(Visitor.AtomVisitor visitor) { visitor.visitLocalVariableAccess(this); }
     }
+    
+    /**
+     * Representation of an access to a field that requires an indirection through a generated method.  
+     * <p>
+     * Typically accessing a protected member of the enclosing class's parent class.
+     */
+    public static final class IndirectFieldAccess extends Lvalue {
+        public final Atom                    lhs;
+        public final IClass.IField           field;
+        public final AbstractTypeDeclaration typeDeclaration;
 
+        public IndirectFieldAccess(
+            Location                location,
+            Atom                    lhs,
+            IClass.IField           field,
+            AbstractTypeDeclaration typeDeclaration
+        ) {
+            super(location);
+            this.lhs   = lhs;
+            this.field = field;
+            this.typeDeclaration = typeDeclaration;
+        }
+
+        // Compile time members.
+
+        // Implement "Atom".
+        public String toString() { return this.lhs.toString() + '.' + this.field.toString(); }
+
+        public void accept(LvalueVisitor lvv) { lvv.visitIndirectFieldAccess(this); }
+        public void accept(RvalueVisitor rvv) { rvv.visitIndirectFieldAccess(this); }
+        public void accept(AtomVisitor   vis) { vis.visitIndirectFieldAccess(this); }
+    }
+
     /**
      * Representation of an access to a field of a class or an interface. (Does not implement the
      * "array length" expression, e.g. "ia.length".)
@@ -2674,6 +2766,23 @@
         public final Rvalue[] dimExprs;
         public final int      dims;
 
+        /**
+         * Create a new array with dimension dimExprs.length + dims
+         * <p>
+         * e.g. byte[12][][] is created with
+         *     new NewArray(
+         *         null,
+         *         Java.BasicType(NULL, Java.BasicType.BYTE),
+         *         new Rvalue[] {
+         *             new Java.Literal(null, Integer.valueOf(12)
+         *         },
+         *         2
+         *     )
+         * @param location  the location of this element
+         * @param type      the base type of the array
+         * @param dimExprs  sizes for dimensions being allocated with specific sizes
+         * @param dims      the number of dimensions that are not yet allocated
+         */
         public NewArray(
             Location location,
             Type     type,
=== src/org/codehaus/janino/SimpleCompiler.java
==================================================================
--- src/org/codehaus/janino/SimpleCompiler.java (revision 72158)
+++ src/org/codehaus/janino/SimpleCompiler.java (patch janino-full level 1)
@@ -201,7 +201,7 @@
     public static final ClassLoader BOOT_CLASS_LOADER = new ClassLoader(null) {};
 
     /**
-     * Allowe references to the classes loaded through this parent class loader
+     * Allow references to the classes loaded through this parent class loader
      * (@see {@link #setParentClassLoader(ClassLoader)}), plus the extra
      * <code>auxiliaryClasses</code>.
      * <p>
@@ -233,7 +233,22 @@
             DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
         );
     }
+    
+    /**
+     * Cook this compilation unit directly.
+     *  See {@link Cookable.cook}
+     */
+    public void cook(Java.CompilationUnit compilationUnit)
+    throws CompileException, Parser.ParseException, Scanner.ScanException, IOException {
+        this.setUpClassLoaders();
 
+        // Compile the classes and load them.
+        this.compileToClassLoader(
+            compilationUnit,
+            DebuggingInformation.DEFAULT_DEBUGGING_INFORMATION
+        );
+    }
+
     /**
      * Initializes {@link #classLoader} and {@link #iClassLoader} from the configured
      * {@link #parentClassLoader} and {@link #optionalAuxiliaryClasses}. These are needed by
=== src/org/codehaus/janino/Scanner.java
==================================================================
--- src/org/codehaus/janino/Scanner.java (revision 72158)
+++ src/org/codehaus/janino/Scanner.java (patch janino-full level 1)
@@ -457,6 +457,9 @@
         if (v instanceof Boolean) {
             return v.toString();
         }
+        if (v instanceof Byte) {
+            return v.toString();
+        }
         if (v == null) {
             return "null";
         }
=== src/org/codehaus/janino/Visitor.java
==================================================================
--- src/org/codehaus/janino/Visitor.java (revision 72158)
+++ src/org/codehaus/janino/Visitor.java (patch janino-full level 1)
@@ -73,6 +73,7 @@
         void visitFieldDeclaration(Java.FieldDeclaration fd);
         void visitLabeledStatement(Java.LabeledStatement ls);
         void visitBlock(Java.Block b);
+        void visitStatementList(Java.StatementList sl);
         void visitExpressionStatement(Java.ExpressionStatement es);
         void visitIfStatement(Java.IfStatement is);
         void visitForStatement(Java.ForStatement fs);
@@ -131,6 +132,7 @@
         void visitAmbiguousName(Java.AmbiguousName an);
         void visitArrayAccessExpression(Java.ArrayAccessExpression aae);
         void visitFieldAccess(Java.FieldAccess fa);
+        void visitIndirectFieldAccess(Java.IndirectFieldAccess fa);
         void visitFieldAccessExpression(Java.FieldAccessExpression fae);
         void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae);
         void visitLocalVariableAccess(Java.LocalVariableAccess lva);
=== src/org/codehaus/janino/UnparseVisitor.java
==================================================================
--- src/org/codehaus/janino/UnparseVisitor.java (revision 72158)
+++ src/org/codehaus/janino/UnparseVisitor.java (patch janino-full level 1)
@@ -45,8 +45,103 @@
  * {@link #main(String[])} for a usage example.
  */
 public class UnparseVisitor implements Visitor.ComprehensiveVisitor {
-    private final AutoIndentWriter aiw;
-    private final PrintWriter      pw;
+    protected final AutoIndentWriter aiw;
+    protected final PrintWriter      pw;
+    
+    private final Stack curPrecedence;
+    
+    /**
+     * Install op as the next op's precedence level
+     * @param op a string that it is the precedenceMap
+     */
+    private void pushPrecedence(String op) {
+        Object o = precedenceMap.get(op);
+        if(o == null) {
+            throw new RuntimeException("Illegal operator for precedence: " + op);
+        }
+        curPrecedence.push(o);
+    }
+    
+    /**
+     * Remove the current precedence setting
+     */
+    private void popPrecedence() {
+        curPrecedence.pop();
+    }
+    
+    /**
+     * Give the precedence value a slight nudge to account for associativity of operators
+     */
+    private void adjustPrecedenceForAssociativity(boolean higher) {
+        double cur = ((Double)curPrecedence.pop()).doubleValue();
+        cur += higher ? 0.5 : -0.5;
+        curPrecedence.push(Double.valueOf(cur));
+    }
+    
+    private boolean needsParens(String op) {
+        //return false;
+        if(curPrecedence.isEmpty()) { return false; }
+        double cur = ((Double)curPrecedence.peek()).doubleValue();
+        double nxt = ((Double)precedenceMap.get(op)).doubleValue();
+        return cur > nxt;
+    }
+    
+    // this provides a mapping from operators to precedence levels
+    // - higher numbers are more tightly binding
+    // - ambiguous cases are just given special tokens
+    // - "reset" is a special token for bracketing operations
+    private static final Map precedenceMap = new HashMap(); // Map<String, Double>
+    static {
+        precedenceMap.put("[]",         Double.valueOf(15));
+        precedenceMap.put(".",          Double.valueOf(15));
+        precedenceMap.put("()",         Double.valueOf(15));
+        precedenceMap.put("methodcall", Double.valueOf(15));
+        // ++ and -- are ambiguous as they are both post and prefix
+        // so instead we are just putting the names
+        precedenceMap.put("postfix",    Double.valueOf(15));
+        precedenceMap.put("prefix",     Double.valueOf(14));
+        //unary - is ambiguous just use prefix
+        //precedenceMap.put("-",          Double.valueOf(14));
+        precedenceMap.put("~",          Double.valueOf(14));
+        precedenceMap.put("!",          Double.valueOf(14));
+        precedenceMap.put("cast",       Double.valueOf(13));
+        precedenceMap.put("new",        Double.valueOf(13));
+        precedenceMap.put("*",          Double.valueOf(12));
+        precedenceMap.put("/",          Double.valueOf(12));
+        precedenceMap.put("%",          Double.valueOf(12));
+        precedenceMap.put("+",          Double.valueOf(11));
+        precedenceMap.put("-",          Double.valueOf(11));
+        precedenceMap.put("<<",         Double.valueOf(10));
+        precedenceMap.put(">>",         Double.valueOf(10));
+        precedenceMap.put(">>>",        Double.valueOf(10));
+        precedenceMap.put(">=",         Double.valueOf(9));
+        precedenceMap.put("<=",         Double.valueOf(9));
+        precedenceMap.put("<",          Double.valueOf(9));
+        precedenceMap.put(">",          Double.valueOf(9));
+        precedenceMap.put("instanceof", Double.valueOf(9));
+        precedenceMap.put("==",         Double.valueOf(8));
+        precedenceMap.put("!=",         Double.valueOf(8));
+        precedenceMap.put("&",          Double.valueOf(7));
+        precedenceMap.put("^",          Double.valueOf(6));
+        precedenceMap.put("|",          Double.valueOf(5));
+        precedenceMap.put("&&",         Double.valueOf(4));
+        precedenceMap.put("||",         Double.valueOf(4));
+        precedenceMap.put("?:",         Double.valueOf(3));
+        precedenceMap.put("=",          Double.valueOf(2));
+        precedenceMap.put("+=",         Double.valueOf(2));
+        precedenceMap.put("-=",         Double.valueOf(2));
+        precedenceMap.put("*=",         Double.valueOf(2));
+        precedenceMap.put("/=",         Double.valueOf(2));
+        precedenceMap.put("%=",         Double.valueOf(2));
+        precedenceMap.put("&=",         Double.valueOf(2));
+        precedenceMap.put("^=",         Double.valueOf(2));
+        precedenceMap.put("|=",         Double.valueOf(2));
+        precedenceMap.put("<<=",        Double.valueOf(2));
+        precedenceMap.put(">>=",        Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put(">>>=",       Double.valueOf(2));
+        precedenceMap.put("reset",      Double.valueOf(0));
+    }
 
     /**
      * Testing of parsing/unparsing.
@@ -84,6 +179,7 @@
     public UnparseVisitor(Writer w) {
         this.aiw = new AutoIndentWriter(w);
         this.pw = new PrintWriter(this.aiw, true);
+        this.curPrecedence = new Stack();
     }
 
     public void unparseCompilationUnit(Java.CompilationUnit cu) {
@@ -177,11 +273,18 @@
     }
     public void visitBlock(Java.Block b) {
         this.pw.println('{');
-        for (Iterator it = b.statements.iterator(); it.hasNext();) {
+        visitListStatements(b.statements);
+        this.pw.print('}');
+    }
+    public void visitStatementList(Java.StatementList sl) {
+        visitListStatements(sl.statements);
+    }
+    //takes a List<Java.BlockStatement>
+    private void visitListStatements(List l) {
+        for (Iterator it = l.iterator(); it.hasNext();) {
             ((Java.BlockStatement) it.next()).accept(this);
             this.pw.println();
         }
-        this.pw.print('}');
     }
     public void visitBreakStatement(Java.BreakStatement bs) {
         this.pw.print("break");
@@ -204,8 +307,10 @@
         this.pw.print(';');
     }
     public void visitExpressionStatement(Java.ExpressionStatement es) {
+        pushPrecedence("reset"); //this might be an expression in a nested class body or something
         ((Java.Atom) es.rvalue).accept(this);
         this.pw.print(';');
+        popPrecedence();
     }
     public void visitForStatement(Java.ForStatement fs) {
         this.pw.print("for (");
@@ -334,12 +439,16 @@
         this.pw.print(' ' + fp.name);
     }
     public void visitMethodInvocation(Java.MethodInvocation mi) {
+        if(needsParens("methodcall")) { this.pw.print('('); }
         if (mi.optionalTarget != null) {
+            pushPrecedence(".");
             mi.optionalTarget.accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print(mi.methodName);
         this.unparseFunctionInvocationArguments(mi.arguments);
+        if(needsParens("methodcall")) { this.pw.print(')'); }
     }
     public void visitAlternateConstructorInvocation(Java.AlternateConstructorInvocation aci) {
         this.pw.print("this");
@@ -354,28 +463,47 @@
         this.unparseFunctionInvocationArguments(sci.arguments);
     }
     public void visitNewClassInstance(Java.NewClassInstance nci) {
+        if(needsParens("new")) { this.pw.print('('); }
         if (nci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) nci.optionalQualification).accept(this);
             this.pw.print('.');
+            popPrecedence();
         }
         this.pw.print("new " + nci.type.toString());
         this.unparseFunctionInvocationArguments(nci.arguments);
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitAssignment(Java.Assignment a) {
+        if(needsParens(a.operator)) { this.pw.print('('); }
+        pushPrecedence(a.operator);
         ((Java.Atom) a.lhs).accept(this);
         this.pw.print(' ' + a.operator + ' ');
         ((Java.Atom) a.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(a.operator)) { this.pw.print(')'); }
     }
     public void visitAmbiguousName(Java.AmbiguousName an) { this.pw.print(an.toString()); }
     public void visitArrayAccessExpression(Java.ArrayAccessExpression aae) {
+        if(needsParens("[]")) { this.pw.print('('); }
+        pushPrecedence("[]");
         ((Java.Atom) aae.lhs).accept(this);
+        popPrecedence();
+        
         this.pw.print('[');
+        pushPrecedence("reset");
         ((Java.Atom) aae.index).accept(this);
+        popPrecedence();
         this.pw.print(']');
+        if(needsParens("[]")) { this.pw.print(')'); }
     }
     public void visitArrayLength(Java.ArrayLength al) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) al.lhs).accept(this);
         this.pw.print(".length");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitArrayType(Java.ArrayType at) {
         ((Java.Atom) at.componentType).accept(this);
@@ -385,44 +513,88 @@
         this.pw.print(bt.toString());
     }
     public void visitBinaryOperation(Java.BinaryOperation bo) {
+        if(needsParens(bo.op)) { this.pw.print('('); }
+        pushPrecedence(bo.op);
         ((Java.Atom) bo.lhs).accept(this);
         this.pw.print(' ' + bo.op + ' ');
+        adjustPrecedenceForAssociativity(true); //binary ops are all left -> right associative
         ((Java.Atom) bo.rhs).accept(this);
+        popPrecedence();
+        if(needsParens(bo.op)) { this.pw.print(')'); }
     }
     public void visitCast(Java.Cast c) {
+        if(needsParens("cast")) { this.pw.print('('); }
+        pushPrecedence("cast");
         this.pw.print('(');
         ((Java.Atom) c.targetType).accept(this);
         this.pw.print(") ");
         ((Java.Atom) c.value).accept(this);
+        popPrecedence();
+        if(needsParens("cast")) { this.pw.print(')'); }
     }
     public void visitClassLiteral(Java.ClassLiteral cl) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) cl.type).accept(this);
         this.pw.print(".class");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitConditionalExpression(Java.ConditionalExpression ce) {
+        if(needsParens("?:")) { this.pw.print('('); }
+        pushPrecedence("?:");
+        adjustPrecedenceForAssociativity(true); //ternary is right -> left associative
         ((Java.Atom) ce.lhs).accept(this);
+        adjustPrecedenceForAssociativity(false); //back to normal
         this.pw.print(" ? ");
         ((Java.Atom) ce.mhs).accept(this);
         this.pw.print(" : ");
         ((Java.Atom) ce.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("?:")) { this.pw.print(')'); }
     }
     public void visitConstantValue(Java.ConstantValue cv) { this.pw.print(cv.toString()); }
     public void visitCrement(Java.Crement c) {
-        this.pw.print(
-            c.pre ?
-            c.operator + c.operand :
-            c.operand + c.operator
-        );
+        String prec = c.pre ? "prefix" : "postfix";
+        if(needsParens(prec)) { this.pw.print('('); }
+        pushPrecedence(prec);
+        if(c.pre) {
+            this.pw.print(c.operator);
+            this.pw.print(c.operand);
+        } else {
+            this.pw.print(c.operand);
+            this.pw.print(c.operator);
+        }
+        popPrecedence();
+        if(needsParens(prec)) { this.pw.print(')'); }
     }
     public void visitFieldAccess(Java.FieldAccess fa) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fa.lhs.accept(this);
         this.pw.print('.' + fa.field.getName());
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
+    public void visitIndirectFieldAccess(Java.IndirectFieldAccess ifa) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
+        ifa.lhs.accept(this);
+        this.pw.print('.' + ifa.field.getName());
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
+    }
     public void visitFieldAccessExpression(Java.FieldAccessExpression fae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         fae.lhs.accept(this);
         this.pw.print('.' + fae.fieldName);
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         if (scfae.optionalQualification != null) {
             scfae.optionalQualification.accept((Visitor.TypeVisitor) this);
             this.pw.print(".super." + scfae.fieldName);
@@ -430,37 +602,57 @@
         {
             this.pw.print("super." + scfae.fieldName);
         }
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitInstanceof(Java.Instanceof io) {
+        if(needsParens("instanceof")) { this.pw.print('('); }
+        pushPrecedence("instanceof");
         ((Java.Atom) io.lhs).accept(this);
         this.pw.print(" instanceof ");
         ((Java.Atom) io.rhs).accept(this);
+        popPrecedence();
+        if(needsParens("instanceof")) { this.pw.print(')'); }
     }
     public void visitLiteral(Java.Literal l) { this.pw.print(l.toString()); }
     public void visitLocalVariableAccess(Java.LocalVariableAccess lva) { this.pw.print(lva.toString()); }
     public void visitNewArray(Java.NewArray na) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         ((Java.Atom) na.type).accept(this);
         for (int i = 0; i < na.dimExprs.length; ++i) {
             this.pw.print('[');
+            pushPrecedence("reset");
             ((Java.Atom) na.dimExprs[i]).accept(this);
+            popPrecedence();
             this.pw.print(']');
         }
         for (int i = 0; i < na.dims; ++i) {
             this.pw.print("[]");
         }
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitNewInitializedArray(Java.NewInitializedArray nai) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         this.pw.print("new ");
         nai.arrayType.accept(this);
         this.pw.print(" ");
         this.unparseArrayInitializerOrRvalue(nai.arrayInitializer);
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     public void visitPackage(Java.Package p) { this.pw.print(p.toString()); }
     public void visitParameterAccess(Java.ParameterAccess pa) { this.pw.print(pa.toString()); }
     public void visitQualifiedThisReference(Java.QualifiedThisReference qtr) {
+        if(needsParens(".")) { this.pw.print('('); }
+        pushPrecedence(".");
         ((Java.Atom) qtr.qualification).accept(this);
         this.pw.print(".this");
+        popPrecedence();
+        if(needsParens(".")) { this.pw.print(')'); }
     }
     public void visitReferenceType(Java.ReferenceType rt) { this.pw.print(rt.toString()); }
     public void visitRvalueMemberType(Java.RvalueMemberType rmt) { this.pw.print(rmt.toString()); }
@@ -473,12 +665,19 @@
         this.pw.print("this");
     }
     public void visitUnaryOperation(Java.UnaryOperation uo) {
+        if(needsParens("prefix")) { this.pw.print('('); }
+        pushPrecedence("prefix");
         this.pw.print(uo.operator);
+        this.adjustPrecedenceForAssociativity(true); //handle cases like "- - 3" as -(-3)
         ((Java.Atom) uo.operand).accept(this);
+        popPrecedence();
+        if(needsParens("prefix")) { this.pw.print(')'); }
     }
     public void visitParenthesizedExpression(Java.ParenthesizedExpression pe) {
         this.pw.print('(');
+        pushPrecedence("reset");
         ((Java.Atom) pe.value).accept(this);
+        popPrecedence();
         this.pw.print(')');
     }
 
@@ -508,11 +707,13 @@
             } else
             {
                 this.pw.print("{ ");
+                pushPrecedence("reset");
                 this.unparseArrayInitializerOrRvalue(ai.values[0]);
                 for (int i = 1; i < ai.values.length; ++i) {
                     this.pw.print(", ");
                     this.unparseArrayInitializerOrRvalue(ai.values[i]);
                 }
+                popPrecedence();
                 this.pw.print(" }");
             }
         } else
@@ -524,22 +725,32 @@
     public void visitAnonymousClassDeclaration(Java.AnonymousClassDeclaration acd) {
         ((Java.Atom) acd.baseType).accept(this);
         this.pw.println(" {");
+        pushPrecedence("reset");
         this.unparseClassDeclarationBody(acd);
+        popPrecedence();
         this.pw.print('}');
     }
     public void visitNewAnonymousClassInstance(Java.NewAnonymousClassInstance naci) {
+        if(needsParens("new")) { this.pw.print('('); }
+        pushPrecedence("new");
         if (naci.optionalQualification != null) {
+            pushPrecedence(".");
             ((Java.Atom) naci.optionalQualification).accept(this);
+            popPrecedence();
             this.pw.print('.');
         }
         this.pw.print("new " + naci.anonymousClassDeclaration.baseType.toString() + '(');
+        pushPrecedence("reset");
         for (int i = 0; i < naci.arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) naci.arguments[i]).accept(this);
         }
+        popPrecedence(); //pop the reset
         this.pw.println(") {");
         this.unparseClassDeclarationBody(naci.anonymousClassDeclaration);
         this.pw.print('}');
+        popPrecedence();
+        if(needsParens("new")) { this.pw.print(')'); }
     }
     // Multi-line!
     private void unparseClassDeclarationBody(Java.ClassDeclaration cd) {
@@ -607,10 +818,12 @@
     }
     private void unparseFunctionInvocationArguments(Java.Rvalue[] arguments) {
         this.pw.print('(');
+        pushPrecedence("reset");
         for (int i = 0; i < arguments.length; ++i) {
             if (i > 0) this.pw.print(", ");
             ((Java.Atom) arguments[i]).accept(this);
         }
+        popPrecedence();
         this.pw.print(')');
     }
 }
=== src/org/codehaus/janino/util/ClassFile.java
==================================================================
--- src/org/codehaus/janino/util/ClassFile.java (revision 72158)
+++ src/org/codehaus/janino/util/ClassFile.java (patch janino-full level 1)
@@ -1334,8 +1334,8 @@
         }
 
         public static class Entry {
-            public final short startPC, lineNumber;
-            public Entry(short startPC, short lineNumber) {
+            public final int startPC, lineNumber;
+            public Entry(int startPC, int lineNumber) {
                 this.startPC    = startPC;
                 this.lineNumber = lineNumber;
             }
=== src/org/codehaus/janino/util/Traverser.java
==================================================================
--- src/org/codehaus/janino/util/Traverser.java (revision 72158)
+++ src/org/codehaus/janino/util/Traverser.java (patch janino-full level 1)
@@ -70,6 +70,7 @@
         public final void visitFieldDeclaration(Java.FieldDeclaration fd)                                     { Traverser.this.traverseFieldDeclaration(fd); }
         public final void visitLabeledStatement(Java.LabeledStatement ls)                                     { Traverser.this.traverseLabeledStatement(ls); }
         public final void visitBlock(Java.Block b)                                                            { Traverser.this.traverseBlock(b); }
+        public final void visitStatementList(Java.StatementList sl)                                           { Traverser.this.traverseStatementList(sl); }
         public final void visitExpressionStatement(Java.ExpressionStatement es)                               { Traverser.this.traverseExpressionStatement(es); }
         public final void visitIfStatement(Java.IfStatement is)                                               { Traverser.this.traverseIfStatement(is); }
         public final void visitForStatement(Java.ForStatement fs)                                             { Traverser.this.traverseForStatement(fs); }
@@ -116,6 +117,7 @@
         public final void visitAmbiguousName(Java.AmbiguousName an)                                           { Traverser.this.traverseAmbiguousName(an); }
         public final void visitArrayAccessExpression(Java.ArrayAccessExpression aae)                          { Traverser.this.traverseArrayAccessExpression(aae); }
         public final void visitFieldAccess(Java.FieldAccess fa)                                               { Traverser.this.traverseFieldAccess(fa); }
+        public final void visitIndirectFieldAccess(Java.IndirectFieldAccess ifa)                              { Traverser.this.traverseIndirectFieldAccess(ifa); }
         public final void visitFieldAccessExpression(Java.FieldAccessExpression fae)                          { Traverser.this.traverseFieldAccessExpression(fae); }
         public final void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae)    { Traverser.this.traverseSuperclassFieldAccessExpression(scfae); }
         public final void visitLocalVariableAccess(Java.LocalVariableAccess lva)                              { Traverser.this.traverseLocalVariableAccess(lva); }
@@ -220,6 +222,12 @@
         }
         this.traverseStatement(b);
     }
+    public void traverseStatementList(Java.StatementList sl) {
+        for (Iterator it = sl.statements.iterator(); it.hasNext();) {
+            ((Java.Statement) it.next()).accept(this.cv);
+        }
+        this.traverseStatement(sl);
+    }
 
     public void traverseExpressionStatement(Java.ExpressionStatement es) {
         es.rvalue.accept((Visitor.RvalueVisitor) this.cv);
@@ -488,6 +496,11 @@
         fa.lhs.accept(this.cv);
         this.traverseLvalue(fa);
     }
+    
+    public void traverseIndirectFieldAccess(Java.IndirectFieldAccess ifa) {
+        ifa.lhs.accept(this.cv);
+        this.traverseLvalue(ifa);
+    }
 
     public void traverseFieldAccessExpression(Java.FieldAccessExpression fae) {
         fae.lhs.accept(this.cv);

==== BEGIN SVK PATCH BLOCK ====
Version: svk v2.0.2 (linux)

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
9TTrTW+Lym5d3TVXdrrW0TLxwKN9VEd95cpAny1b8MGQhgY8iKWBJYvWHH4aE8GKyIgSOUMCLSaE
I3oYkqgdO5/lS8wAyOTIGsbjcBWJroQ1gg9wSb/X2mWCk0Izc2W/scv5NJudA25EXKh1FyOcl/jy
3lm01lBpaP1VUH1oPZXvWU9lmKs5rb65sq9KZZhZpbbXWnlzJbS9VSo3rqfyxvVU3lKpsqdK5YpM
Uq3ytm0V5n9Tlcrbt6+r8nb7ca5audO+5fZqXV5P3Qq0rlZ3+zrqluzhcklbrW5nhf62VRMD66h7
X4X+tlape6BC3ZYqdecr1K0m3u+7z76/1aTe/Py1193RYV+3mszrrNDfqoKnwvhWq9uwjrqb11G3
cR11N66j7n3rqHtgHXXn11F3m17YrbHu9u3rqaur/A7WJXp/vWVdbU3B+VHVfEAlS0vR6rHjxYkD
ggwRs5IzpgvhNCf7ycySZG3OTQvqZt5CmWsWA63BXag3OK0QkZLlmCwo1k+OPYTtM1RlWylOWau9
vdy6t2oCY2JAxCLmO2ly/yjkx/YQthdT2A0RQzks0TmZLIq0JIkj0RaThDxjZNsvJsbnaBQzKyKb
QolAMrpKnHrbXG96YAvbNAhoR1VnZ+OwcEHbSR/Fh//sNoTNnXKb90zLILmV6DWy1WlyWxnwcpt3
eo19JLiuqacu3FNjFaRC10iVNCfYBBisgTq1dcQOK9cmUyR+2ZTpBraZSuPYcMzRpd80a7ymytAi
61b9EjUQw9QvXV2L7hl9lrW3SbpqJgxY/2tHENcqoeFUKJMTUy6aZXkUj+qy4UvbvtiJtHE919rN
eXtUhHScP7JmZEw4TGyyJ7odBWvgLHxNBd7KVrAuPaDZZAXmQoy/9kFz2xMqWWnIjK04lN13Ry10
rA1vS86MiW7kNlxrL0k1m37Cy5r7uYlGQc8YHPwLv3Qlq7qk0UlAvBLFBOyHETPEfYj2SpJ8Ikc3
bKezeH0qecR5W5yz1zCdaqVmdTbtYVVVAX2juUp8iZ0v18CapN6ahJ19WzV0CV/NpQRRlG7PVJ6A
WvAOzj0uiaPLap18WGV5u+ceMgmvoZOo1rVMEqA6niWgtJFJAbMDz5Kqk4OruNZUxQDviZB5ijY8
cyjHaTYFHLAWutrQihBW2cdB1gK6Sg5e8vQOWvXk0h205hG2bQDVXftg4FrGLSLUuGUmLg0mp1M8
qhdLx/WbmlZhidWr1YjDwprZ3Y5q1YVDWZyE/sxUopLOdx1EQ4JFDF3bAoVP5UAFciYHTAF0HMEQ
K3ldZEMlWlWNLdHTTni7iSesmXrCr5Z8OuoENAu1jOlKr2C0+beb//g1MyBPSIjId72Jd5269w7N
BdVNrk4BLYxMECuNms6/fg3909deW0ertGutGdRo3puxxNW2VjXf0MewwKdZtxoMOi5M2C305d1e
EyEqOQeua/+vG1bl43KdtVJ06/phvUumVv0U/X7b1VNFfbqGHqs17TqQF63UD3I0oWZ9Ft1BiyJr
cNQIugeCdjTQDbTPgeINGpwNPpc10dXGQUNap7lXiUKEpGvzpGQUT3ZteFnCNqNeFQGMdj6dYbMS
bz7wsFOMzzlZLm6LwnWctEaXJXs9fJaV5J0d/NplHwJhe1TEjYLwR43HmZB/w3AMyHpszCOIwkbQ
ZSo4X5GSu+k6SbfaELAMXCZRxx5ve3tbswfFHPPNXq454Y0lPO1+Xzvv8cc8rbEuHwpcRsW9de3z
MOvSpL7fNy9luUYxO9WoJvVpJEl9GtEdro2YnEEo6Ub3uqL6nrrm+Vav19uuwPOtCd5olkUJpPms
BTwfCawmvSPqtJidw2Uqx117EQX8bCsbb0lwrb72GO9rTjS3NXvb0EXEcb/H3xoDNJUY+MUfJhZ/
+MAHuqnF8zRV6Ka+/vDi+SkBZ/Vgs7nhnnrLTFqETbAKFEjnsnNOQw3BOtMUwazN29rCtcU8XGus
1cexbfEWH+dtbvIk2tm2GOfjKnfOR85ZsLFEm98HQ+tNtDQ1tXjb/V6Ph2tp4lo8fq65zt+qHpL4
b6Pyv9Duxbuol9qoF6jAnzz8OPXGnS99uLBAvXDLzzYU7qfk98vyQpH6o3vkxQ3ocuRLfvnf0TVs
lBmL1BBnX2YLVfwcpUu8Qc4gKj+t2hF0w4E1UrXwGjf8LPbZpKSE/SFlSm8lfGq3AcssNjc3Y14E
1JZIMgmnYM1bmDGa2xMt7cDtwFDtLW0Jb3PCz7d4fbG2Jh8LDMbXeeY9de2tLfN0ttXf5G3pkjIC
x8+i7JdJ2ilmhSl4AbOv2dWBqDGLrgyap71NTW0NTc0Nnjba6+lo9nV42+mGJn9T0wZymoxkcCHZ
9kiQp3JXk5oRBq0Ps2L2EB0X0zwdz2On0PRcGh1U2kBQabVDpaUSKs0dfp+KChT3tbf7reF42tut
4bTSHl9Hs6fD36zBSfHZKR7W/Kwk4sORysU8ONoW3zxNSzPYWybNoKwlWhfa7LrQWqkLLR0efRfg
XztSeDx2cKAL6H+/BgeUHzrHA3+TkEyS9Rpt8Cqdm0KP9bi32+HeVgn31g6/14i7HQ083sq4+1s0
OPrrumD+5PH9Aojc0+wM4TTlgiqyO6uensCZh7SsRUd4FO4v4FN5aid9TXadtOEN0sm2Dq/H0EmP
7bRpsu1kW4ff09HkMQ4QuVANMMZzAx/jw3krUeJTNDpZVkAqtDabkkJKyJXuMSNXmJEO1nk882Rh
bUD3b23f7oXV2AtrwEBHB9ko7+gYSwtIjrDJjS3zdf75DJubrvPNZ/kZ+JHPC/E6X9t8I5+eQvED
2XgDut1pTl2t8WRtUDFR1+g6X8t8q681AZKGa2iHfxuafJ6mBpZrjzVwCZZr4zgP6000b3d55+0a
9bbbNgoWRfoQbsvT3FZrW3VtnprKddTW1Q7c0f8P9BDxGw==
==== END SVK PATCH BLOCK ====


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email

Re: Manipulable AST

by Matt Fowles :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

All~

This email thread appears to be utterly silent... Should I move this
patch to a Jira ticket?  Anyone?

Here goes anyway...  This new version of the patch includes everything
previously mentioned, as well as:

- a fix for http://jira.codehaus.org/browse/JANINO-114
- a fix to a bug where StatementLists (the thing that started all of
this) were erroneously creating their own local variable scope

Comments greatly appreciated.

Matt

[janino-full.patch]

==== Patch <janino-full> level 2
Source: 737f8f2c-97f8-0310-ac9b-cfac8cc1a2f5:/eng/third-party/janino/hynes-bytecode:72367
        (svn+ssh://svn.streambase.com/repos/sb)
Target: 737f8f2c-97f8-0310-ac9b-cfac8cc1a2f5:/eng/third-party/janino/trunk:72158
        (svn+ssh://svn.streambase.com/repos/sb)
Log:
 r74026@spiceweasel (orig r72225):  fowles | 2008-05-18 21:53:29 -0400
 creating a branch for bytecode generation work done during hynes
 
 r74027@spiceweasel (orig r72226):  fowles | 2008-05-18 21:55:43 -0400
  r73994@spiceweasel (orig r72199):  fowles | 2008-05-17 13:51:45 -0400
  merge personal changes from local svk to svn
 
 
 r74028@spiceweasel (orig r72227):  fowles | 2008-05-18 21:56:13 -0400
  r74007@spiceweasel (orig r72211):  fowles | 2008-05-18 13:13:14 -0400
  fix test names after some merge games
 
 
 r74029@spiceweasel (orig r72228):  fowles | 2008-05-18 21:57:42 -0400
  r74008@spiceweasel (orig r72212):  fowles | 2008-05-18 13:13:46 -0400
  switch from recursion to having a single method that iterates to a fixed point
 
 
 r74030@spiceweasel (orig r72229):  fowles | 2008-05-18 21:58:21 -0400
  r74016@spiceweasel (orig r72220):  fowles | 2008-05-18 18:41:01 -0400
  fix things to work with wide jumps
  raises bytecode limit from 32K to 64K
 
 
 r74167@spiceweasel (orig r72365):  fowles | 2008-05-19 21:00:58 -0400
 Fix a div by zero in constant evaluation
 
 r74169@spiceweasel (orig r72367):  fowles | 2008-05-19 22:33:06 -0400
 Fix a problem with StatementList erroneously getting their own local variables
 

=== tests/src/UnparseTests.java
==================================================================
--- tests/src/UnparseTests.java (revision 72158)
+++ tests/src/UnparseTests.java (patch janino-full level 2)
@@ -0,0 +1,337 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Parser;
+import org.codehaus.janino.Scanner;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Visitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayAccessExpression;
+import org.codehaus.janino.Java.ArrayLength;
+import org.codehaus.janino.Java.Assignment;
+import org.codehaus.janino.Java.Atom;
+import org.codehaus.janino.Java.BinaryOperation;
+import org.codehaus.janino.Java.Cast;
+import org.codehaus.janino.Java.ClassLiteral;
+import org.codehaus.janino.Java.ConditionalExpression;
+import org.codehaus.janino.Java.ConstantValue;
+import org.codehaus.janino.Java.Crement;
+import org.codehaus.janino.Java.FieldAccess;
+import org.codehaus.janino.Java.FieldAccessExpression;
+import org.codehaus.janino.Java.IndirectFieldAccess;
+import org.codehaus.janino.Java.Instanceof;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableAccess;
+import org.codehaus.janino.Java.MethodInvocation;
+import org.codehaus.janino.Java.NewAnonymousClassInstance;
+import org.codehaus.janino.Java.NewArray;
+import org.codehaus.janino.Java.NewClassInstance;
+import org.codehaus.janino.Java.NewInitializedArray;
+import org.codehaus.janino.Java.ParameterAccess;
+import org.codehaus.janino.Java.ParenthesizedExpression;
+import org.codehaus.janino.Java.QualifiedThisReference;
+import org.codehaus.janino.Java.SuperclassFieldAccessExpression;
+import org.codehaus.janino.Java.SuperclassMethodInvocation;
+import org.codehaus.janino.Java.ThisReference;
+import org.codehaus.janino.Java.UnaryOperation;
+
+public class UnparseTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(UnparseTests.class.getName());
+        s.addTest(new UnparseTests("testSimple"));
+        s.addTest(new UnparseTests("testParens"));
+        s.addTest(new UnparseTests("testMany"));
+        return s;
+    }
+
+    public UnparseTests(String name) { super(name); }
+    
+    private static void helpTestExpr(String input, String expect, boolean simplify) throws Exception {
+        Parser p = new Parser(new Scanner(
+                null, new ByteArrayInputStream(input.getBytes()
+        )));
+        Atom expr = p.parseExpression();
+        if(simplify) {
+            expr = stripUnnecessaryParenExprs(expr);
+        }
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        expr.accept(uv);
+        assertEquals(expect, sw.toString());    
+    }
+    
+    private static Java.Rvalue[] stripUnnecessaryParenExprs(Java.Rvalue[] rvalues) {
+        Java.Rvalue[] res = new Java.Rvalue[rvalues.length];
+        for(int i = 0; i < res.length; ++i) {
+            res[i] = stripUnnecessaryParenExprs(rvalues[i]);
+        }
+        return res;
+    }
+    
+    private static Java.Atom stripUnnecessaryParenExprs(Java.Atom atom) {
+        if(atom instanceof Java.Rvalue) {
+            return stripUnnecessaryParenExprs((Java.Rvalue)atom);
+        }
+        return atom;
+    }
+    
+    private static Java.Lvalue stripUnnecessaryParenExprs(Java.Lvalue lvalue) {
+        return (Java.Lvalue)stripUnnecessaryParenExprs((Java.Rvalue)lvalue);
+    }
+    
+    private static Java.Rvalue stripUnnecessaryParenExprs(Java.Rvalue rvalue) {
+        if(rvalue == null) { return null; }
+        final Java.Rvalue[] res = new Java.Rvalue[1];
+        Visitor.RvalueVisitor rv = new Visitor.RvalueVisitor() {
+            public void visitArrayLength(ArrayLength al) {
+                res[0] = new Java.ArrayLength(al.getLocation(),
+                        stripUnnecessaryParenExprs(al.lhs));
+            }
+
+            public void visitAssignment(Assignment a) {
+                res[0] = new Java.Assignment(a.getLocation(),
+                        stripUnnecessaryParenExprs(a.lhs),
+                        a.operator,
+                        stripUnnecessaryParenExprs(a.rhs)
+                );
+            }
+
+            public void visitBinaryOperation(BinaryOperation bo) {
+                res[0] = new Java.BinaryOperation(bo.getLocation(),
+                        stripUnnecessaryParenExprs(bo.lhs),
+                        bo.op,
+                        stripUnnecessaryParenExprs(bo.rhs)
+                );
+            }
+
+            public void visitCast(Cast c) {
+                res[0] = new Java.Cast(c.getLocation(),
+                        c.targetType,
+                        stripUnnecessaryParenExprs(c.value));
+            }
+
+            public void visitClassLiteral(ClassLiteral cl) {
+                res[0] = cl; //too much effort
+            }
+
+            public void visitConditionalExpression(ConditionalExpression ce) {
+                res[0] = new Java.ConditionalExpression(ce.getLocation(),
+                        stripUnnecessaryParenExprs(ce.lhs),
+                        stripUnnecessaryParenExprs(ce.mhs),
+                        stripUnnecessaryParenExprs(ce.rhs));
+            }
+
+            public void visitConstantValue(ConstantValue cv) {
+                res[0] = cv;
+            }
+
+            public void visitCrement(Crement c) {
+                if(c.pre) {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            c.operator,
+                            stripUnnecessaryParenExprs(c.operand));
+                } else {
+                    res[0] = new Java.Crement(c.getLocation(),
+                            stripUnnecessaryParenExprs(c.operand),
+                            c.operator);
+                }
+            }
+
+            public void visitInstanceof(Instanceof io) {
+                res[0] = new Java.Instanceof(io.getLocation(),
+                        stripUnnecessaryParenExprs(io.lhs),
+                        io.rhs);
+            }
+
+            public void visitLiteral(Literal l) {
+                res[0] = l;
+            }
+
+            public void visitMethodInvocation(MethodInvocation mi) {
+                res[0] = new Java.MethodInvocation(mi.getLocation(),
+                        stripUnnecessaryParenExprs(mi.optionalTarget),
+                        mi.methodName,
+                        stripUnnecessaryParenExprs(mi.arguments));
+            }
+
+            public void visitNewAnonymousClassInstance(
+                    NewAnonymousClassInstance naci) {
+                res[0] = naci; //too much effort
+            }
+
+            public void visitNewArray(NewArray na) {
+                res[0] = new Java.NewArray(na.getLocation(),
+                        na.type,
+                        stripUnnecessaryParenExprs(na.dimExprs),
+                        na.dims);
+            }
+
+            public void visitNewClassInstance(NewClassInstance nci) {
+                res[0] = new Java.NewClassInstance(nci.getLocation(),
+                        stripUnnecessaryParenExprs(nci.optionalQualification),
+                        nci.type,
+                        stripUnnecessaryParenExprs(nci.arguments));
+            }
+
+            public void visitNewInitializedArray(NewInitializedArray nia) {
+                res[0] = nia; //too much effort
+            }
+
+            public void visitParameterAccess(ParameterAccess pa) {
+                res[0] = pa;
+            }
+
+            public void visitQualifiedThisReference(QualifiedThisReference qtr) {
+                res[0] = qtr;
+            }
+
+            public void visitSuperclassMethodInvocation(
+                    SuperclassMethodInvocation smi) {
+                res[0] = new Java.SuperclassMethodInvocation(smi.getLocation(),
+                        smi.methodName,
+                        stripUnnecessaryParenExprs(smi.arguments));
+            }
+
+            public void visitThisReference(ThisReference tr) {
+                res[0] = tr;
+            }
+
+            public void visitUnaryOperation(UnaryOperation uo) {
+                res[0] = new Java.UnaryOperation(uo.getLocation(),
+                        uo.operator,
+                        stripUnnecessaryParenExprs(uo.operand));
+            }
+
+            public void visitAmbiguousName(AmbiguousName an) {
+                res[0] = an;
+            }
+
+            public void visitArrayAccessExpression(ArrayAccessExpression aae) {
+                res[0] = new Java.ArrayAccessExpression(aae.getLocation(),
+                        stripUnnecessaryParenExprs(aae.lhs),
+                        stripUnnecessaryParenExprs(aae.index));
+            }
+
+            public void visitFieldAccess(FieldAccess fa) {
+                res[0] = new Java.FieldAccess(fa.getLocation(),
+                        stripUnnecessaryParenExprs(fa.lhs),
+                        fa.field);
+            }
+
+            public void visitFieldAccessExpression(FieldAccessExpression fae) {
+                res[0] = new Java.FieldAccessExpression(fae.getLocation(),
+                        stripUnnecessaryParenExprs(fae.lhs),
+                        fae.fieldName);
+            }
+
+            public void visitLocalVariableAccess(LocalVariableAccess lva) {
+                res[0] = lva;
+            }
+
+            public void visitParenthesizedExpression(ParenthesizedExpression pe) {
+                res[0] = stripUnnecessaryParenExprs(pe.value);
+            }
+
+            public void visitSuperclassFieldAccessExpression(
+                    SuperclassFieldAccessExpression scfae) {
+                res[0] = scfae;
+            }
+
+            public void visitIndirectFieldAccess(IndirectFieldAccess fa) {
+                res[0] = fa;
+            }
+            
+        };
+        rvalue.accept(rv);
+        return res[0];
+    }
+    
+    public void testSimple() throws Exception {
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", false);
+        helpTestExpr("1 + 2*3", "1 + 2 * 3", true);
+    }
+    
+    public void testParens() throws Exception {
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", false);
+        helpTestExpr("(1 + 2)*3", "(1 + 2) * 3", true);
+    }
+    
+    public void testMany() throws Exception {
+        final String[][] exprs = new String[][] {
+              //input                                  expected simplified                    expect non-simplified
+            { "((1)+2)",                               "1 + 2",                               "((1) + 2)"           },
+            { "1 + 2 * 3",                             null,                                  null                  },
+            { "1 + (2 * 3)",                           "1 + 2 * 3",                           null                  },
+            { "3 - (2 - 1)",                           null,                                  null                  },
+            { "true ? 1 : 2",                          null,                                  null                  },
+            { "(true ? false : true) ? 1 : 2",         null,                                  null                  },
+            { "true ? false : (true ? false : true)",  "true ? false : true ? false : true",  null                  },
+            { "-(-(2))",                               "-(-2)",                               null                  },
+            { "- - 2",                                 "-(-2)",                               "-(-2)"               },
+            { "x && (y || z)",                         null,                                  null                  },
+            { "(x && y) || z",                         "x && y || z",                         null                  },
+            { "x = (y = z)",                           "x = y = z",                           null                  },
+            { "(--x) + 3",                             "--x + 3",                             null                  },
+            { "(baz.bar).foo(x, (3 + 4) * 5)",         "baz.bar.foo(x, (3 + 4) * 5)",         null                  },
+            { "!(bar instanceof Integer)",             null,                                  null                  },
+            { "(true ? foo : bar).baz()",              null,                                  null                  },
+        };
+        
+        for(int i = 0; i < exprs.length; ++i) {
+            String input = exprs[i][0];
+            String expectSimplify = exprs[i][1];
+            if(expectSimplify == null) {
+                expectSimplify = input;
+            }
+            
+            String expectNoSimplify = exprs[i][2];
+            if(expectNoSimplify == null) {
+                expectNoSimplify = input;
+            }
+            
+            helpTestExpr(input, expectSimplify, true);
+            helpTestExpr(input, expectNoSimplify, false);
+        }
+    }
+
+}
=== tests/src/EvaluatorTests.java
==================================================================
--- tests/src/EvaluatorTests.java (revision 72158)
+++ tests/src/EvaluatorTests.java (patch janino-full level 2)
@@ -32,14 +32,27 @@
  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-import java.io.*;
-import java.lang.reflect.*;
-import java.util.*;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
 
-import junit.framework.*;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
 
-import org.codehaus.janino.*;
+import org.codehaus.janino.ClassBodyEvaluator;
+import org.codehaus.janino.CompileException;
+import org.codehaus.janino.ExpressionEvaluator;
 import org.codehaus.janino.Scanner;
+import org.codehaus.janino.ScriptEvaluator;
+import org.codehaus.janino.SimpleCompiler;
 
 public class EvaluatorTests extends TestCase {
     public static Test suite() {
@@ -53,7 +66,12 @@
         s.addTest(new EvaluatorTests("testGuessParameterNames"));
         s.addTest(new EvaluatorTests("testAssertNotCooked"));
         s.addTest(new EvaluatorTests("testAccessingCompilingClass"));
-        s.addTest(new EvaluatorTests("testProtectedAccessToParentSuperClassVar"));
+        s.addTest(new EvaluatorTests("testProtectedAccessAcrossPackage"));
+        s.addTest(new EvaluatorTests("testProtectedAccessWithinPackage"));
+        s.addTest(new EvaluatorTests("testComplicatedSyntheticAccess"));
+        s.addTest(new EvaluatorTests("testStaticInitAccessProtected"));
+        s.addTest(new EvaluatorTests("test32kBranchLimit"));
+        s.addTest(new EvaluatorTests("testDivByZero"));
         return s;
     }
 
@@ -251,7 +269,7 @@
         assertEquals(8, numTests);
     }
     
-    public void testProtectedAccessToParentSuperClassVar() throws Exception {
+    public void testProtectedAccessAcrossPackage() throws Exception {
         SimpleCompiler sc = new SimpleCompiler();
         sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
         sc.cook("package test;\n" +
@@ -264,4 +282,214 @@
                 "}"
         );
     }
+    
+    public void testProtectedAccessWithinPackage() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
+        sc.cook("package for_sandbox_tests;\n" +
+                "public class Top extends for_sandbox_tests.ProtectedVariable {\n" +
+                "    public class Inner {\n" +
+                "        public int get() {\n" +
+                "            return var;\n" +
+                "        }\n" +
+                "        public void set() {\n" +
+                "            var += 10;\n" +
+                "        }\n" +
+                "        public int getS() {\n" +
+                "            return svar;\n" +
+                "        }\n" +
+                "        public void setS() {\n" +
+                "            svar += 10;\n" +
+                "        }\n" +
+                "    } \n" +
+                "    public Inner createInner() {\n" +
+                "        return new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("for_sandbox_tests.Top");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        Object i = createInner.invoke(t, null);
+        
+        Class innerClass = sc.getClassLoader().loadClass("for_sandbox_tests.Top$Inner");
+        Method get = innerClass.getDeclaredMethod("get", null);
+        Method getS = innerClass.getDeclaredMethod("getS", null);
+        Method set = innerClass.getDeclaredMethod("set", null);
+        Method setS = innerClass.getDeclaredMethod("setS", null);
+        
+        Object res;
+        {   // non-static
+            res = get.invoke(i, null);
+            assertEquals(Integer.valueOf(1), res);
+            set.invoke(i, null);
+            res = get.invoke(i, null);
+            assertEquals(Integer.valueOf(11), res);
+        }
+        {   //static
+            res = getS.invoke(i, null);
+            assertEquals(Integer.valueOf(2), res);
+            setS.invoke(i, null);
+            res = getS.invoke(i, null);
+            assertEquals(Integer.valueOf(12), res);
+        }
+    }
+    
+    public void testComplicatedSyntheticAccess() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.setParentClassLoader(SimpleCompiler.BOOT_CLASS_LOADER, new Class[] { for_sandbox_tests.ProtectedVariable.class });
+        sc.cook("package for_sandbox_tests;\n" +
+                "public class L0 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "    public class L1 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "        public class L2 extends for_sandbox_tests.ProtectedVariable {\n" +
+                "            public class Inner {\n" +
+                "                public int getL2() { return L0.L1.L2.this.var; }\n" +
+                "                public int getL1() { return L0.L1.this.var; }\n" +
+                "                public int getL0() { return L0.this.var; }\n" +
+                "                public int setL2() { return L2.this.var = 2; }\n" +
+                "                public int setL1() { return L1.this.var = 1; }\n" +
+                "                public int setL0() { return L0.this.var = 0; }\n" +
+                "            }\n" +
+                "        }\n" +
+                "    }\n" +
+                "    public L0.L1.L2.Inner createInner() {\n" +
+                "        return new L0().new L1().new L2().new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("for_sandbox_tests.L0");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        Object inner = createInner.invoke(t, null);
+        
+        Class innerClass = inner.getClass();
+        Method[] gets = new Method[] {
+                innerClass.getMethod("getL0", null),
+                innerClass.getMethod("getL1", null),
+                innerClass.getMethod("getL2", null),
+        };
+        Method[] sets = new Method[] {
+                innerClass.getMethod("setL0", null),
+                innerClass.getMethod("setL1", null),
+                innerClass.getMethod("setL2", null),
+        };
+        for(int i = 0; i < 3; ++i) {
+            Object g1 = gets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(1), g1);
+            Object s1 = sets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(i), s1);
+            Object g2 = gets[i].invoke(inner, null);
+            assertEquals(Integer.valueOf(i), g2);
+        }
+    }
+    
+    public void testStaticInitAccessProtected() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.cook("package test;\n" +
+                "public class Outer extends for_sandbox_tests.ProtectedVariable  {\n" +
+                "    public class Inner {\n" +
+                "        {\n" +
+                "            int t = var;\n" +
+                "            var = svar;\n" +
+                "            svar = t;\n" +
+                "        }\n" +
+                "        private final int i = var;\n" +
+                "        private final int j = svar;\n" +
+                "        {\n" +
+                "            int t = var;\n" +
+                "            var = svar;\n" +
+                "            svar = t;\n" +
+                "        }\n" +
+                "        private final int[] a = new int[] { i, j };\n" +
+                "    }\n" +
+                "    public Inner createInner() {\n" +
+                "        return new Inner();\n" +
+                "    }\n" +
+                "}"
+        );
+        
+        Class topClass = sc.getClassLoader().loadClass("test.Outer");
+        Method createInner = topClass.getDeclaredMethod("createInner", null);
+        Object t = topClass.newInstance();
+        assertNotNull(t);
+        Object inner = createInner.invoke(t, null);
+        assertNotNull(inner);
+    }
+    
+    public void test32kBranchLimit() throws Exception {
+        String preamble =
+            "package test;\n" +
+            "public class Test {\n" +
+            "    public int run() {\n" +
+            "        int res = 0;\n" +
+            "        for(int i = 0; i < 2; ++i) {\n";
+        String middle =
+            "            ++res;\n";
+        String postamble =
+            "        }\n" +
+            "        return res;\n" +
+            "    }\n" +
+            "}";
+        
+        int[] tests = new int[] { 1, 10, 100, Short.MAX_VALUE/5, Short.MAX_VALUE/4, Short.MAX_VALUE/2 };
+        for(int i = 0; i < tests.length; ++i) {
+            int repititions = tests[i];
+            
+            StringBuilder sb = new StringBuilder();
+            sb.append(preamble);
+            for(int j = 0; j < repititions; ++j) {
+                sb.append(middle);
+            }
+            sb.append(postamble);
+            
+            SimpleCompiler sc = new SimpleCompiler();
+            sc.cook(sb.toString());
+            
+            Class c = sc.getClassLoader().loadClass("test.Test");
+            Method m = c.getDeclaredMethod("run", null);
+            Object o = c.newInstance();
+            Object res = m.invoke(o, null);
+            assertEquals(Integer.valueOf(2*repititions), res);
+        }
+        
+    }
+    
+    public void testDivByZero() throws Exception {
+        SimpleCompiler sc = new SimpleCompiler();
+        sc.cook(
+            "package test;\n" +
+            "public class Test {\n" +
+            "    public int runIntDiv() {\n" +
+            "        return 1 / 0;\n" +
+            "    }\n" +
+            "    public int runIntMod() {\n" +
+            "        return 1 % 0;\n" +
+            "    }\n" +
+            "    public long runLongDiv() {\n" +
+            "        return 1L / 0;\n" +
+            "    }\n" +
+            "    public long runLongMod() {\n" +
+            "        return 1L % 0;\n" +
+            "    }\n" +
+            "}"
+        );
+        
+        
+        Class c = sc.getClassLoader().loadClass("test.Test");
+        Object o = c.newInstance();
+        
+        Method[] m = c.getMethods();
+        for(int i = 0; i < m.length; ++i) {
+            if(m[i].getName().startsWith("run")) {
+                try {
+                    Object res = m[i].invoke(o, null);
+                    fail("Method " + m[i] + " should have failed, but got " + res);
+                } catch(InvocationTargetException ae) {
+                    assertTrue(ae.getTargetException() instanceof ArithmeticException);
+                }
+            }
+        }
+    }
 }
=== tests/src/for_sandbox_tests/ProtectedVariable.java
==================================================================
--- tests/src/for_sandbox_tests/ProtectedVariable.java (revision 72158)
+++ tests/src/for_sandbox_tests/ProtectedVariable.java (patch janino-full level 2)
@@ -1,5 +1,6 @@
-package for_sandbox_tests;
-
-public class ProtectedVariable {
-    protected int var = 1;
-}
+package for_sandbox_tests;
+
+public class ProtectedVariable {
+    protected int var = 1;
+    protected static int svar = 2;
+}
=== tests/src/AstTests.java
==================================================================
--- tests/src/AstTests.java (revision 72158)
+++ tests/src/AstTests.java (patch janino-full level 2)
@@ -0,0 +1,425 @@
+
+/*
+ * Janino - An embedded Java[TM] compiler
+ *
+ * Copyright (c) 2001-2007, Arno Unkrig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *    2. Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials
+ *       provided with the distribution.
+ *    3. The name of the author may not be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.CompileException;
+import org.codehaus.janino.Java;
+import org.codehaus.janino.Location;
+import org.codehaus.janino.Mod;
+import org.codehaus.janino.SimpleCompiler;
+import org.codehaus.janino.UnparseVisitor;
+import org.codehaus.janino.Java.AmbiguousName;
+import org.codehaus.janino.Java.ArrayType;
+import org.codehaus.janino.Java.BasicType;
+import org.codehaus.janino.Java.Block;
+import org.codehaus.janino.Java.CompilationUnit;
+import org.codehaus.janino.Java.ExpressionStatement;
+import org.codehaus.janino.Java.Literal;
+import org.codehaus.janino.Java.LocalVariableDeclarationStatement;
+import org.codehaus.janino.Java.MethodDeclarator;
+import org.codehaus.janino.Java.PackageMemberClassDeclaration;
+import org.codehaus.janino.Java.ReturnStatement;
+import org.codehaus.janino.Java.Rvalue;
+import org.codehaus.janino.Java.StatementList;
+import org.codehaus.janino.Java.Type;
+import org.codehaus.janino.Java.FunctionDeclarator.FormalParameter;
+import org.codehaus.janino.Parser.ParseException;
+import org.codehaus.janino.Scanner.ScanException;
+
+public class AstTests extends TestCase {
+    public static Test suite() {
+        TestSuite s = new TestSuite(AstTests.class.getName());
+        s.addTest(new AstTests("testSimpleAst"));
+        s.addTest(new AstTests("testLocalVariable"));
+        s.addTest(new AstTests("testBlock"));
+        s.addTest(new AstTests("testStatementList"));
+        s.addTest(new AstTests("testStatementList2"));
+        s.addTest(new AstTests("testByteArrayLiteral"));
+        s.addTest(new AstTests("testClassRef"));
+        s.addTest(new AstTests("testPrecedence"));
+        return s;
+    }
+    
+    public AstTests(String name) { super(name); }
+
+    private static Object compileAndEval(CompilationUnit cu) throws CompileException,
+            ParseException, ScanException, IOException, ClassNotFoundException,
+            InstantiationException, IllegalAccessException,
+            NoSuchMethodException, InvocationTargetException {
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        
+        Class handMadeClass = loader.loadClass("HandMade");
+        
+        Object handMade = handMadeClass.newInstance();
+        Method calc = handMadeClass.getMethod("calculate", null);
+        Object res = calc.invoke(handMade, null);
+        return res;
+    }
+    
+    private static ArrayType createByteArrayType() {
+        return new Java.ArrayType(
+                new Java.BasicType(
+                        getLocation(),
+                        Java.BasicType.BYTE
+                )
+        );
+    };
+    
+    private static PackageMemberClassDeclaration createClass(CompilationUnit cu)
+            throws ParseException {
+        PackageMemberClassDeclaration clazz = new PackageMemberClassDeclaration(
+                getLocation(),
+                null,
+                Mod.PUBLIC,
+                "HandMade",
+                null,
+                new Type[]{}
+        );
+        cu.addPackageMemberTypeDeclaration(clazz);
+        return clazz;
+    }
+    
+    private static Type createDoubleType() {
+        return new BasicType(getLocation(), BasicType.DOUBLE);
+    }
+    
+    private static Java.BinaryOperation createOp(Rvalue l1, String op, Rvalue l2) {
+        return new Java.BinaryOperation(getLocation(), l1, op, l2);
+    }
+    
+    private static Literal createLiteral(double d) {
+        return createLiteral(Double.valueOf(d));
+    }
+    
+    
+    private static Literal createLiteral(Object o) {
+        return new Literal( getLocation(), o );
+    }
+    
+    private static void createMethod(PackageMemberClassDeclaration clazz, Block body, Type returnType) {
+        MethodDeclarator method = new MethodDeclarator(
+                getLocation(),
+                null,
+                (short)(Mod.PUBLIC),
+                returnType,
+                "calculate",
+                new FormalParameter[0],
+                new Type[0],
+                body
+        );
+        clazz.addDeclaredMethod(method);
+    }
+        
+
+    private static LocalVariableDeclarationStatement createVarDecl(String name, double value) {
+        return new Java.LocalVariableDeclarationStatement(
+                getLocation(),
+                (short)0,
+                createDoubleType(),
+                new Java.VariableDeclarator[] {
+                    new Java.VariableDeclarator(
+                            getLocation(),
+                            name,
+                            0,
+                            createLiteral(value)
+                    )
+                }
+        );
+    }
+    
+    private static AmbiguousName createVariableRef(String name) {
+        return new Java.AmbiguousName(
+                getLocation(),
+                new String[] { name }
+        );
+    }
+
+    /**
+     * "Clever" method to get a location from a stack trace
+     */
+    static private Location getLocation() {
+        Exception e = new Exception();
+        StackTraceElement ste = e.getStackTrace()[1];//we only care about our caller
+        return new Location(
+                ste.getFileName(),
+                (short)ste.getLineNumber(),
+                (short)0
+        );
+    }
+
+    
+    public void testBlock() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        Block sub = new Block(getLocation());
+        sub.addStatement( createVarDecl("x", 2.0) );                        
+        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        try {
+            compileAndEval(cu);
+            fail("Block must limit the scope of variables in it");
+        } catch(CompileException ex) {
+            assertTrue(ex.getMessage().endsWith("Expression \"x\" is not an rvalue"));
+        }
+    }
+
+    public void testByteArrayLiteral() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Byte exp = Byte.valueOf((byte)1);
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.NewInitializedArray(
+                                getLocation(),
+                                createByteArrayType(),
+                                new Java.ArrayInitializer(
+                                        getLocation(),
+                                        new Java.Rvalue[] {
+                                            createLiteral(exp)
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                createByteArrayType()
+        );
+        
+        Object res = compileAndEval(cu);
+        assertEquals(exp.byteValue(), ((byte[])res)[0]);
+    }
+
+    public void testLocalVariable() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement( createVarDecl("x", 2.0) );                        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(6.0), res);
+    }
+    
+    public void testSimpleAst() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        createLiteral(3.0)
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertEquals(Double.valueOf(3.0), res);
+    }
+
+    public void testStatementList() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        StatementList sub = new StatementList(getLocation());
+        sub.addStatement( createVarDecl("x", 3.0) );                        
+        body.addStatement(sub);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createLiteral(3)
+                        )
+                )
+        );
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(9.0), res);
+    }
+    
+    public void testStatementList2() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        StatementList subX = new StatementList(getLocation());
+        StatementList subY = new StatementList(getLocation());
+        body.addStatement(subX);
+        body.addStatement(subY);
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.BinaryOperation(
+                                getLocation(),
+                                createVariableRef("x"),
+                                "*",
+                                createVariableRef("y")
+                        )
+                )
+        );
+        subX.addStatement( createVarDecl("x", 3.0) );                        
+        subY.addStatement( createVarDecl("y", 4.0) );                        
+        
+        createMethod(clazz, body, createDoubleType());
+        
+        Object res = compileAndEval(cu);
+        assertTrue(res instanceof Double);
+        assertEquals(Double.valueOf(12.0), res);
+    }
+    
+    public void testClassRef() throws Exception {
+        CompilationUnit cu = new CompilationUnit("AstTests.java");
+        
+        PackageMemberClassDeclaration clazz = createClass(cu);
+        
+        Block body = new Block(getLocation());
+        
+        body.addStatement(
+                new ReturnStatement(
+                        getLocation(),
+                        new Java.ClassLiteral(
+                                getLocation(),
+                                new Java.ReferenceType(
+                                        getLocation(),
+                                        new String[] {
+                                            "HandMade"
+                                        }
+                                )
+                        )
+                )
+        );
+        
+        createMethod(clazz, body,
+                new Java.ReferenceType(
+                        getLocation(),
+                        new String[] { "java", "lang", "Class" }
+                )
+        );
+        
+        SimpleCompiler compiler = new SimpleCompiler();
+        compiler.cook(cu);
+        
+        ClassLoader loader = compiler.getClassLoader();
+        Class handMadeClass = loader.loadClass("HandMade");
+        Method calc = handMadeClass.getMethod("calculate", null);
+        
+        Object handMade = handMadeClass.newInstance();
+        Object res = calc.invoke(handMade, null);
+        assertEquals(handMadeClass, res);
+    }
+    
+    public void testPrecedence() throws Exception {
+        ExpressionStatement es = new Java.ExpressionStatement(
+                new Java.Assignment(
+                        getLocation(),
+                        new Java.AmbiguousName(
+                                getLocation(),
+                                new String[] { "x" }
+                        ),
+                        "=",
+                        createOp(
+                                createLiteral(1), "*",
+                                createOp(createLiteral(2), "+", createLiteral(3))
+                        )
+                )
+        );
+        
+        StringWriter sw = new StringWriter();
+        UnparseVisitor uv = new UnparseVisitor(sw);
+        uv.visitExpressionStatement(es);
+        assertEquals("x = 1.0D * (2.0D + 3.0D);", sw.toString());
+    }
+}
=== tests/src/AllTests.java
==================================================================
--- tests/src/AllTests.java (revision 72158)
+++ tests/src/AllTests.java (patch janino-full level 2)
@@ -82,5 +82,7 @@
         this.addTest(ReportedBugs.suite());
         this.addTest(SandboxTests.suite());
         this.addTest(EvaluatorTests.suite());
+        this.addTest(AstTests.suite());
+        this.addTest(UnparseTests.suite());
     }
 }
=== src/org/codehaus/janino/UnitCompiler.java
==================================================================
--- src/org/codehaus/janino/UnitCompiler.java (revision 72158)
+++ src/org/codehaus/janino/UnitCompiler.java (patch janino-full level 2)
@@ -346,10 +346,10 @@
                 if (tbd.isStatic()) b.addButDontEncloseStatement((Java.BlockStatement) tbd);
             }
 
-            maybeCreateInitMethod(cd, cf, b);
+            this.maybeCreateInitMethod(cd, cf, b);
         }
 
-        compileDeclaredMethods(cd, cf);
+        this.compileDeclaredMethods(cd, cf);
 
         
         // Compile declared constructors.
@@ -366,8 +366,12 @@
             }
         }
 
+        // A side effect of this call may create synthetic functions to access
+        // protected parent variables
+        this.compileDeclaredMemberTypes(cd, cf);
+
         // Compile the aforementioned extras.
-        compileDeclaredMethods(cd, cf, declaredMethodCount);
+        this.compileDeclaredMethods(cd, cf, declaredMethodCount);
 
         // Class and instance variables.
         for (Iterator it = cd.variableDeclaratorsAndInitializers.iterator(); it.hasNext();) {
@@ -387,7 +391,6 @@
             );
         }
 
-        compileDeclaredMemberTypes(cd, cf);
 
         // Add the generated class file to a thread-local store.
         this.generatedClassFiles.add(cf);
@@ -647,6 +650,7 @@
             public void visitLocalClassDeclarationStatement   (Java.LocalClassDeclarationStatement    lcds) { try { res[0] = UnitCompiler.this.compile2(lcds); } catch (CompileException e) { throw new UCE(e); } }
             public void visitAlternateConstructorInvocation   (Java.AlternateConstructorInvocation    aci ) { try { res[0] = UnitCompiler.this.compile2(aci ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperConstructorInvocation       (Java.SuperConstructorInvocation        sci ) { try { res[0] = UnitCompiler.this.compile2(sci ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (StatementList                          sl  ) { try { res[0] = UnitCompiler.this.compile2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
         };
         try {
             bs.accept(bsv);
@@ -658,13 +662,14 @@
     private boolean compile2(Java.Initializer i) throws CompileException {
         return this.compile(i.block);
     }
-    private boolean compile2(Java.Block b) throws CompileException {
-        this.codeContext.saveLocalVariables();
+    //takes a List<BlockStatement>
+    private boolean compile2ListStatement(List l, boolean saveVariables) throws CompileException {
+        if (saveVariables) this.codeContext.saveLocalVariables();
         try {
             boolean previousStatementCanCompleteNormally = true;
-            for (int i = 0; i < b.statements.size(); ++i) {
-                Java.BlockStatement bs = (Java.BlockStatement) b.statements.get(i);
-                if (!previousStatementCanCompleteNormally) {
+            for (int i = 0; i < l.size(); ++i) {
+                Java.BlockStatement bs = (Java.BlockStatement) l.get(i);
+                if (!previousStatementCanCompleteNormally && this.generatesCode(bs)) {
                     this.compileError("Statement is unreachable", bs.getLocation());
                     break;
                 }
@@ -672,9 +677,15 @@
             }
             return previousStatementCanCompleteNormally;
         } finally {
-            this.codeContext.restoreLocalVariables();
+            if (saveVariables) this.codeContext.restoreLocalVariables();
         }
     }
+    private boolean compile2(Java.Block b) throws CompileException {
+        return compile2ListStatement(b.statements, true);
+    }
+    private boolean compile2(Java.StatementList sl) throws CompileException {
+        return compile2ListStatement(sl.statements, false);
+    }
     private boolean compile2(Java.DoStatement ds) throws CompileException {
         Object cvc = this.getConstantValue(ds.condition);
         if (cvc != null) {
@@ -1667,6 +1678,9 @@
 
             // Compile the function body.
             try {
+                if(fd.optionalBody == null) {
+                    this.compileError("Method must have a body", fd.getLocation());
+                }
                 boolean canCompleteNormally = this.compile(fd.optionalBody);
                 if (canCompleteNormally) {
                     if (this.getReturnType(fd) != IClass.VOID) this.compileError("Method must return a value", fd.getLocation());
@@ -1686,12 +1700,9 @@
         // Don't continue code attribute generation if we had compile errors.
         if (this.compileErrorCount > 0) return;
 
-        // Fix up.
-        codeContext.fixUp();
+        // fix up and reallocate as needed
+        codeContext.fixUpAndRelocate();
 
-        // Relocate.
-        codeContext.relocate();
-
         // Do flow analysis.
         if (UnitCompiler.DEBUG) {
             try {
@@ -1770,6 +1781,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName              an   ) { try { UnitCompiler.this.compile2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression      aae  ) { try { UnitCompiler.this.compile2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                fa   ) { try { UnitCompiler.this.compile2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess        ifa  ) { try { UnitCompiler.this.compile2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression      fae  ) { try { UnitCompiler.this.compile2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compile2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess        lva  ) { try { UnitCompiler.this.compile2(lva  ); } catch (CompileException e) { throw new UCE(e); } }
@@ -1953,6 +1965,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { UnitCompiler.this.compileBoolean2(an   , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { UnitCompiler.this.compileBoolean2(aae  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { UnitCompiler.this.compileBoolean2(fa   , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { UnitCompiler.this.compileBoolean2(ifa  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { UnitCompiler.this.compileBoolean2(fae  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compileBoolean2(scfae, dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) { try { UnitCompiler.this.compileBoolean2(lva  , dst, orientation); } catch (CompileException e) { throw new UCE(e); } }
@@ -2194,6 +2207,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.compileContext2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.compileContext2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.compileContext2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.compileContext2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.compileContext2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.compileContext2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.compileContext2(lva  );                                                                }
@@ -2221,6 +2235,15 @@
             return 1;
         }
     }
+    private int compileContext2(Java.IndirectFieldAccess fa) throws CompileException {
+        if (fa.field.isStatic()) {
+            this.getType(this.toTypeOrCE(fa.lhs));
+            return 0;
+        } else {
+            this.compileGetValue(this.toRvalueOrCE(fa.lhs));
+            return 1;
+        }
+    }
     private int compileContext2(Java.ArrayLength al) throws CompileException {
         if (!this.compileGetValue(al.lhs).isArray()) this.compileError("Cannot determine length of non-array type", al.getLocation());
         return 1;
@@ -2289,6 +2312,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.compileGet2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.compileGet2(aae  ); } catch (CompileException e) { throw new UCE(e); } };
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.compileGet2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.compileGet2(ifa   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.compileGet2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.compileGet2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.compileGet2(lva  );                                                                }
@@ -2319,6 +2343,30 @@
     private IClass compileGet2(Java.LocalVariableAccess lva) {
         return this.load((Locatable) lva, lva.localVariable);
     }
+    private IClass compileGet2(Java.IndirectFieldAccess fa) throws CompileException {
+        String syntheticMethodName = "get$" + fa.field.getName();
+        AbstractTypeDeclaration type = fa.typeDeclaration;
+        
+        //create the accessor if its here yet
+        Java.MethodDeclarator method = type.getMethodDeclaration(syntheticMethodName);
+        if(method == null) {
+            this.declareIndirectGetMethod(fa);
+            method = type.getMethodDeclaration(syntheticMethodName);
+        }
+        
+
+        // the class or instance will already be on the stack as appropriate
+        // so just invoke the method we generated
+        IClass.IMethod iMethod = this.toIMethod(method);
+        
+        this.writeOpcode(fa, Opcode.INVOKESTATIC);
+        this.writeConstantMethodrefInfo(
+            iMethod.getDeclaringIClass().getDescriptor(), // classFD
+            iMethod.getName(),                            // methodName
+            iMethod.getDescriptor()                       // methodMD
+        );
+        return fa.field.getType();
+    }
     private IClass compileGet2(Java.FieldAccess fa) throws CompileException {
         this.checkAccessible(fa.field, fa.getEnclosingBlockStatement());
         if (fa.field.isStatic()) {
@@ -2393,17 +2441,7 @@
 
         // Check if synthetic method "static Class class$(String className)" is already
         // declared.
-        boolean classDollarMethodDeclared = false;
-        {
-            for (Iterator it = declaringType.declaredMethods.iterator(); it.hasNext();) {
-                Java.MethodDeclarator md = (Java.MethodDeclarator) it.next();
-                if (md.name.equals("class$")) {
-                    classDollarMethodDeclared = true;
-                    break;
-                }
-            }
-        }
-        if (!classDollarMethodDeclared) this.declareClassDollarMethod(cl);
+        if (declaringType.getMethodDeclaration("class$") == null) this.declareClassDollarMethod(cl);
 
         // Determine the statics of the declaring class (this is where static fields
         // declarations are found).
@@ -3288,6 +3326,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.getConstantValue2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.getConstantValue2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.getConstantValue2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.getConstantValue2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.getConstantValue2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.getConstantValue2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getConstantValue2(lva  );                                                    }
@@ -3310,6 +3349,9 @@
     private Object getConstantValue2(Java.FieldAccess fa) throws CompileException {
         return fa.field.getConstantValue();
     }
+    private Object getConstantValue2(Java.IndirectFieldAccess ifa) throws CompileException {
+        return ifa.field.getConstantValue();
+    }
     private Object getConstantValue2(Java.UnaryOperation uo) throws CompileException {
         if (uo.operator.equals("+")) return this.getConstantValue(uo.operand);
         if (uo.operator.equals("-")) return this.getNegatedConstantValue(uo.operand);
@@ -3367,56 +3409,62 @@
 
                 if (!(lhs instanceof Number) || !(rhs instanceof Number)) return null;
 
-                // Numeric binary operation.
-                if (lhs instanceof Double || rhs instanceof Double) {
-                    double lhsD = ((Number) lhs).doubleValue();
-                    double rhsD = ((Number) rhs).doubleValue();
-                    double cvD;
-                    if (bo.op == "*") cvD = lhsD * rhsD; else
-                    if (bo.op == "/") cvD = lhsD / rhsD; else
-                    if (bo.op == "%") cvD = lhsD % rhsD; else
-                    if (bo.op == "+") cvD = lhsD + rhsD; else
-                    if (bo.op == "-") cvD = lhsD - rhsD; else return null;
-                    lhs = new Double(cvD);
-                } else
-                if (lhs instanceof Float || rhs instanceof Float) {
-                    float lhsF = ((Number) lhs).floatValue();
-                    float rhsF = ((Number) rhs).floatValue();
-                    float cvF;
-                    if (bo.op == "*") cvF = lhsF * rhsF; else
-                    if (bo.op == "/") cvF = lhsF / rhsF; else
-                    if (bo.op == "%") cvF = lhsF % rhsF; else
-                    if (bo.op == "+") cvF = lhsF + rhsF; else
-                    if (bo.op == "-") cvF = lhsF - rhsF; else return null;
-                    lhs = new Float(cvF);
-                } else
-                if (lhs instanceof Long || rhs instanceof Long) {
-                    long lhsL = ((Number) lhs).longValue();
-                    long rhsL = ((Number) rhs).longValue();
-                    long cvL;
-                    if (bo.op == "|") cvL = lhsL | rhsL; else
-                    if (bo.op == "^") cvL = lhsL ^ rhsL; else
-                    if (bo.op == "&") cvL = lhsL & rhsL; else
-                    if (bo.op == "*") cvL = lhsL * rhsL; else
-                    if (bo.op == "/") cvL = lhsL / rhsL; else
-                    if (bo.op == "%") cvL = lhsL % rhsL; else
-                    if (bo.op == "+") cvL = lhsL + rhsL; else
-                    if (bo.op == "-") cvL = lhsL - rhsL; else return null;
-                    lhs = new Long(cvL);
-                } else
-                {
-                    int lhsI = ((Number) lhs).intValue();
-                    int rhsI = ((Number) rhs).intValue();
-                    int cvI;
-                    if (bo.op == "|") cvI = lhsI | rhsI; else
-                    if (bo.op == "^") cvI = lhsI ^ rhsI; else
-                    if (bo.op == "&") cvI = lhsI & rhsI; else
-                    if (bo.op == "*") cvI = lhsI * rhsI; else
-                    if (bo.op == "/") cvI = lhsI / rhsI; else
-                    if (bo.op == "%") cvI = lhsI % rhsI; else
-                    if (bo.op == "+") cvI = lhsI + rhsI; else
-                    if (bo.op == "-") cvI = lhsI - rhsI; else return null;
-                    lhs = new Integer(cvI);
+                try {
+                    // Numeric binary operation.
+                    if (lhs instanceof Double || rhs instanceof Double) {
+                        double lhsD = ((Number) lhs).doubleValue();
+                        double rhsD = ((Number) rhs).doubleValue();
+                        double cvD;
+                        if (bo.op == "*") cvD = lhsD * rhsD; else
+                        if (bo.op == "/") cvD = lhsD / rhsD; else
+                        if (bo.op == "%") cvD = lhsD % rhsD; else
+                        if (bo.op == "+") cvD = lhsD + rhsD; else
+                        if (bo.op == "-") cvD = lhsD - rhsD; else return null;
+                        lhs = new Double(cvD);
+                    } else
+                    if (lhs instanceof Float || rhs instanceof Float) {
+                        float lhsF = ((Number) lhs).floatValue();
+                        float rhsF = ((Number) rhs).floatValue();
+                        float cvF;
+                        if (bo.op == "*") cvF = lhsF * rhsF; else
+                        if (bo.op == "/") cvF = lhsF / rhsF; else
+                        if (bo.op == "%") cvF = lhsF % rhsF; else
+                        if (bo.op == "+") cvF = lhsF + rhsF; else
+                        if (bo.op == "-") cvF = lhsF - rhsF; else return null;
+                        lhs = new Float(cvF);
+                    } else
+                    if (lhs instanceof Long || rhs instanceof Long) {
+                        long lhsL = ((Number) lhs).longValue();
+                        long rhsL = ((Number) rhs).longValue();
+                        long cvL;
+                        if (bo.op == "|") cvL = lhsL | rhsL; else
+                        if (bo.op == "^") cvL = lhsL ^ rhsL; else
+                        if (bo.op == "&") cvL = lhsL & rhsL; else
+                        if (bo.op == "*") cvL = lhsL * rhsL; else
+                        if (bo.op == "/") cvL = lhsL / rhsL; else
+                        if (bo.op == "%") cvL = lhsL % rhsL; else
+                        if (bo.op == "+") cvL = lhsL + rhsL; else
+                        if (bo.op == "-") cvL = lhsL - rhsL; else return null;
+                        lhs = new Long(cvL);
+                    } else
+                    {
+                        int lhsI = ((Number) lhs).intValue();
+                        int rhsI = ((Number) rhs).intValue();
+                        int cvI;
+                        if (bo.op == "|") cvI = lhsI | rhsI; else
+                        if (bo.op == "^") cvI = lhsI ^ rhsI; else
+                        if (bo.op == "&") cvI = lhsI & rhsI; else
+                        if (bo.op == "*") cvI = lhsI * rhsI; else
+                        if (bo.op == "/") cvI = lhsI / rhsI; else
+                        if (bo.op == "%") cvI = lhsI % rhsI; else
+                        if (bo.op == "+") cvI = lhsI + rhsI; else
+                        if (bo.op == "-") cvI = lhsI - rhsI; else return null;
+                        lhs = new Integer(cvI);
+                    }
+                } catch(ArithmeticException ae) {
+                    // most likely a div by zero or mod by zero
+                    // guess we can't make this expression into a constant
+                    return null;
                 }
             }
             return lhs;
@@ -3506,6 +3554,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(an   );                                                    }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(fa   );                                                    }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(ifa  );                                                    }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getNegatedConstantValue2(lva  );                                                    }
@@ -3555,6 +3604,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { try { res[0] = UnitCompiler.this.generatesCode2(fd  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) {       res[0] = UnitCompiler.this.generatesCode2(ls  );                                                                }
             public void visitBlock                            (Java.Block                             b   ) { try { res[0] = UnitCompiler.this.generatesCode2(b   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { try { res[0] = UnitCompiler.this.generatesCode2(sl  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) {       res[0] = UnitCompiler.this.generatesCode2(es  );                                                                }
             public void visitIfStatement                      (Java.IfStatement                       is  ) {       res[0] = UnitCompiler.this.generatesCode2(is  );                                                                }
             public void visitForStatement                     (Java.ForStatement                      fs  ) {       res[0] = UnitCompiler.this.generatesCode2(fs  );                                                                }
@@ -3584,12 +3634,19 @@
     public boolean generatesCode2(Java.EmptyStatement es) { return false; }
     public boolean generatesCode2(Java.LocalClassDeclarationStatement lcds) { return false; }
     public boolean generatesCode2(Java.Initializer i) throws CompileException { return this.generatesCode(i.block); }
-    public boolean generatesCode2(Java.Block b) throws CompileException {
-        for (int i = 0; i < b.statements.size(); ++i) {
-            if (this.generatesCode(((Java.BlockStatement) b.statements.get(i)))) return true;
+    //takes a List<Java.BlockStatement>
+    public boolean generatesCode2ListStatements(List l) throws CompileException {
+        for (int i = 0; i < l.size(); ++i) {
+            if (this.generatesCode(((Java.BlockStatement) l.get(i)))) return true;
         }
         return false;
     }
+    public boolean generatesCode2(Java.Block b) throws CompileException {
+        return generatesCode2ListStatements(b.statements);
+        }
+    public boolean generatesCode2(Java.StatementList sl) throws CompileException {
+        return generatesCode2ListStatements(sl.statements);
+    }
     public boolean generatesCode2(Java.FieldDeclaration fd) throws CompileException {
         // Code is only generated if at least one of the declared variables has a
         // non-constant-final initializer.
@@ -3622,6 +3679,7 @@
             public void visitFieldDeclaration                 (Java.FieldDeclaration                  fd  ) { UnitCompiler.this.leave2(fd,   optionalStackValueType); }
             public void visitLabeledStatement                 (Java.LabeledStatement                  ls  ) { UnitCompiler.this.leave2(ls,   optionalStackValueType); }
             public void visitBlock                            (Java.Block                             b   ) { UnitCompiler.this.leave2(b,    optionalStackValueType); }
+            public void visitStatementList                    (Java.StatementList                     sl  ) { UnitCompiler.this.leave2(sl,   optionalStackValueType); }
             public void visitExpressionStatement              (Java.ExpressionStatement               es  ) { UnitCompiler.this.leave2(es,   optionalStackValueType); }
             public void visitIfStatement                      (Java.IfStatement                       is  ) { UnitCompiler.this.leave2(is,   optionalStackValueType); }
             public void visitForStatement                     (Java.ForStatement                      fs  ) { UnitCompiler.this.leave2(fs,   optionalStackValueType); }
@@ -3687,6 +3745,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { UnitCompiler.this.compileSet2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { UnitCompiler.this.compileSet2(aae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { UnitCompiler.this.compileSet2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { UnitCompiler.this.compileSet2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { UnitCompiler.this.compileSet2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { UnitCompiler.this.compileSet2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       UnitCompiler.this.compileSet2(lva  );                                                    }
@@ -3721,6 +3780,32 @@
             fa.field.getDescriptor()                       // fieldFD
         );
     }
+    private void compileSet2(Java.IndirectFieldAccess fa) throws CompileException {
+        String syntheticMethodName = "set$" + fa.field.getName();
+        AbstractTypeDeclaration type = fa.typeDeclaration;
+        
+        //create the accessor if its here yet
+        Java.MethodDeclarator method = type.getMethodDeclaration(syntheticMethodName);
+        if(method == null) {
+            this.declareIndirectSetMethod(fa);
+            method = type.getMethodDeclaration(syntheticMethodName);
+        }
+        
+        // the value we wish to assign is already on the stack,
+        // so we just need to put our new value in place
+        // and then invoke the method we generated
+        IClass.IMethod iMethod = this.toIMethod(method);
+        if(!fa.field.isStatic()) {
+            compileGet(this.toRvalueOrCE(fa.lhs));
+        }
+        
+        this.writeOpcode(fa, Opcode.INVOKESTATIC);
+        this.writeConstantMethodrefInfo(
+            iMethod.getDeclaringIClass().getDescriptor(), // classFD
+            iMethod.getName(),                            // methodName
+            iMethod.getDescriptor()                       // methodMD
+        );
+    }
     private void compileSet2(Java.ArrayAccessExpression aae) throws CompileException {
         this.writeOpcode(aae, Opcode.IASTORE + UnitCompiler.ilfdabcs(this.getType(aae)));
     }
@@ -3775,6 +3860,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.getType2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) { try { res[0] = UnitCompiler.this.getType2(aae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) { try { res[0] = UnitCompiler.this.getType2(fa   ); } catch (CompileException e) { throw new UCE(e); } }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) { try { res[0] = UnitCompiler.this.getType2(ifa  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) { try { res[0] = UnitCompiler.this.getType2(fae  ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) { try { res[0] = UnitCompiler.this.getType2(scfae); } catch (CompileException e) { throw new UCE(e); } }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.getType2(lva  );                                                                }
@@ -3951,6 +4037,9 @@
     private IClass getType2(Java.FieldAccess fa) throws CompileException {
         return fa.field.getType();
     }
+    private IClass getType2(Java.IndirectFieldAccess ifa) throws CompileException {
+        return ifa.field.getType();
+    }
     private IClass getType2(Java.ArrayLength al) {
         return IClass.INT;
     }
@@ -4147,6 +4236,7 @@
         return this.getType(nia.arrayType);
     }
     private IClass getType2(Java.Literal l) {
+        if (l.value instanceof Byte     ) return IClass.BYTE;
         if (l.value instanceof Integer  ) return IClass.INT;
         if (l.value instanceof Long     ) return IClass.LONG;
         if (l.value instanceof Float    ) return IClass.FLOAT;
@@ -4159,6 +4249,7 @@
     }
     private IClass getType2(Java.ConstantValue cv) {
         IClass res = (
+            cv.constantValue instanceof Byte               ? IClass.BYTE    :
             cv.constantValue instanceof Integer            ? IClass.INT     :
             cv.constantValue instanceof Long               ? IClass.LONG    :
             cv.constantValue instanceof Float              ? IClass.FLOAT   :
@@ -4212,6 +4303,7 @@
             public void visitAmbiguousName                  (Java.AmbiguousName                   an   ) { try { res[0] = UnitCompiler.this.isType2(an   ); } catch (CompileException e) { throw new UCE(e); } }
             public void visitArrayAccessExpression          (Java.ArrayAccessExpression           aae  ) {       res[0] = UnitCompiler.this.isType2(aae  );                                                    }
             public void visitFieldAccess                    (Java.FieldAccess                     fa   ) {       res[0] = UnitCompiler.this.isType2(fa   );                                                    }
+            public void visitIndirectFieldAccess            (Java.IndirectFieldAccess             ifa  ) {       res[0] = UnitCompiler.this.isType2(ifa  );                                                    }
             public void visitFieldAccessExpression          (Java.FieldAccessExpression           fae  ) {       res[0] = UnitCompiler.this.isType2(fae  );                                                    }
             public void visitSuperclassFieldAccessExpression(Java.SuperclassFieldAccessExpression scfae) {       res[0] = UnitCompiler.this.isType2(scfae);                                                    }
             public void visitLocalVariableAccess            (Java.LocalVariableAccess             lva  ) {       res[0] = UnitCompiler.this.isType2(lva  );                                                    }
@@ -4351,10 +4443,10 @@
             }
             parentClass = parentClass.getOuterIClass();
         } while(parentClass != null);
+        
+            return "Protected member cannot be accessed from type \"" + iClassDeclaringContextBlockStatement + "\", which is neither declared in the same package as nor is a subclass of \"" + iClassDeclaringMember + "\".";
+        }
 
-        return "Protected member cannot be accessed from type \"" + iClassDeclaringContextBlockStatement + "\", which is neither declared in the same package as nor is a subclass of \"" + iClassDeclaringMember + "\".";
-    }
-
     /**
      * Determine whether the given {@link IClass} is accessible in the given context,
      * according to JLS2 6.6.1.2 and 6.6.1.4.
@@ -5316,7 +5408,8 @@
         for (Java.Scope s = scope; !(s instanceof Java.CompilationUnit); s = s.getEnclosingScope()) {
             if (s instanceof Java.BlockStatement && enclosingBlockStatement == null) enclosingBlockStatement = (Java.BlockStatement) s;
             if (s instanceof Java.TypeDeclaration) {
-                final IClass etd = UnitCompiler.this.resolve((Java.AbstractTypeDeclaration) s);
+                final Java.AbstractTypeDeclaration enclosingTypeDecl = (Java.AbstractTypeDeclaration)s;
+                final IClass etd = UnitCompiler.this.resolve(enclosingTypeDecl);
                 final IClass.IField f = this.findIField(etd, identifier, location);
                 if (f != null) {
                     if (f.isStatic()) {
@@ -5328,7 +5421,7 @@
                         this.warning("IANSFEI", "Implicit access to non-static field \"" + identifier + "\" of enclosing instance (better write \"" + f.getDeclaringIClass() + ".this." + f.getName() + "\")", location);
                     }
 
-                    Java.Type ct = new Java.SimpleType(scopeTypeDeclaration.getLocation(), (IClass) etd);
+                    Java.SimpleType ct = new Java.SimpleType(scopeTypeDeclaration.getLocation(), (IClass) etd);
                     Java.Atom lhs;
                     if (scopeTBD.isStatic()) {
 
@@ -5348,13 +5441,29 @@
                             lhs = new Java.QualifiedThisReference(location, ct);
                         }
                     }
-                    Java.FieldAccess fa = new Java.FieldAccess(
+                    Java.Rvalue res;
+                    
+                    // accessing an enclosing class's parent's protected variable
+                    // requires an indirect access, seems simple enough ;-)
+                    if(f.getAccess() == Access.PROTECTED
+                            && f.getDeclaringIClass() != etd
+                            && scopeTypeDeclaration != enclosingTypeDecl
+                    ) {
+                        res = new Java.IndirectFieldAccess(
+                                location,
+                                lhs,
+                                f,
+                                enclosingTypeDecl
+                        );
+                    } else {
+                        res = new Java.FieldAccess(
                         location,
                         lhs,
                         f
                     );
-                    fa.setEnclosingBlockStatement(enclosingBlockStatement);
-                    return fa;
+                    }
+                    res.setEnclosingBlockStatement(enclosingBlockStatement);
+                    return res;
                 }
             }
         }
@@ -5470,6 +5579,27 @@
     }
 
     /**
+     * Check to see if a local variable was declared in this statement.  Possibly recursing into a StatementList, as needed.
+     * @param varName  The name of the variable to find
+     * @param stmt     The statement in question
+     * @return         A local variable definition if found.  null if not found
+     * @throws CompileException
+     */
+    private Java.LocalVariable findLocalVariableInStatement(Java.BlockStatement stmt, String varName) throws CompileException {
+        if (stmt instanceof Java.LocalVariableDeclarationStatement) {
+            return this.findLocalVariable((Java.LocalVariableDeclarationStatement)stmt, varName);
+        }
+        if (stmt instanceof Java.StatementList) {
+            Java.StatementList sl = (Java.StatementList)stmt;
+            for (Iterator it = sl.statements.iterator(); it.hasNext();) {
+                Java.LocalVariable lv = findLocalVariableInStatement((Java.BlockStatement)it.next(), varName);
+                if (lv != null) return lv;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Find a local variable declared by the given <code>blockStatement</code> or any enclosing
      * scope up to the {@link Java.FunctionDeclarator}.
      */
@@ -5482,19 +5612,15 @@
             {
                 if (s instanceof Java.ForStatement) {
                     Java.BlockStatement optionalForInit = ((Java.ForStatement) s).optionalInit;
-                    if (optionalForInit instanceof Java.LocalVariableDeclarationStatement) {
-                        Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) optionalForInit, name);
+                    Java.LocalVariable lv = this.findLocalVariableInStatement(optionalForInit, name);
                         if (lv != null) return lv;
                     }
-                }
                 if (es instanceof Java.Block) {
                     Java.Block b = (Java.Block) es;
-                    for (Iterator it = b.statements.iterator();;) {
+                    for (Iterator it = b.statements.iterator(); it.hasNext();) {
                         Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                        if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                            Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                        Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                             if (lv != null) return lv;
-                        }
                         if (bs2 == s) break;
                     }
                 }
@@ -5504,10 +5630,8 @@
                         Java.SwitchStatement.SwitchBlockStatementGroup sbgs = (Java.SwitchStatement.SwitchBlockStatementGroup) it2.next();
                         for (Iterator it = sbgs.blockStatements.iterator(); it.hasNext();) {
                             Java.BlockStatement bs2 = (Java.BlockStatement) it.next();
-                            if (bs2 instanceof Java.LocalVariableDeclarationStatement) {
-                                Java.LocalVariable lv = this.findLocalVariable((Java.LocalVariableDeclarationStatement) bs2, name);
+                            Java.LocalVariable lv = this.findLocalVariableInStatement(bs2, name);
                                 if (lv != null) return lv;
-                            }
                             if (bs2 == s) break SBSGS;
                         }
                     }
@@ -5545,6 +5669,30 @@
         return null;
     }
 
+    private Java.AbstractTypeDeclaration findTypeDeclaration(Java.Rvalue rvalue) {
+        Java.Scope s = rvalue.getEnclosingBlockStatement().getEnclosingScope();
+        for(; !(s instanceof Java.CompilationUnit); s = s.getEnclosingScope()) {
+            if(s instanceof Java.AbstractTypeDeclaration) {
+                return (Java.AbstractTypeDeclaration)s;
+            }
+        }
+        return null;
+    }
+    
+    private Java.AbstractTypeDeclaration findTypeDeclaration(IClass iclass) {
+        List types = new ArrayList(); // Java.AbstractTypeDeclaration
+        Java.CompilationUnit cu = this.compilationUnit;
+        types.addAll(cu.packageMemberTypeDeclarations);
+        
+        for(int i = 0; i < types.size(); ++i) {
+            Java.AbstractTypeDeclaration td = (Java.AbstractTypeDeclaration) types.get(i);
+            IClass option = this.resolve(td);
+            if(option == iclass) { return td; }
+            types.addAll(td.getMemberTypeDeclarations());
+        }
+        return null;
+    }
+
     private void determineValue(Java.FieldAccessExpression fae) throws CompileException {
         if (fae.value != null) return;
 
@@ -5567,12 +5715,29 @@
                 };
                 return;
             }
+
+            // accessing an enclosing class's parent's protected variable
+            // requires an indirect access, seems simple enough ;-)
+            Java.AbstractTypeDeclaration scopeClass = this.findTypeDeclaration(fae);
+            Java.AbstractTypeDeclaration enclosingTypeDecl = this.findTypeDeclaration(lhsType);
+            if(iField.getAccess() == Access.PROTECTED
+                    && iField.getDeclaringIClass() != lhsType
+                    && scopeClass != enclosingTypeDecl
+            ) {
+                fae.value = new Java.IndirectFieldAccess(
+                        fae.getLocation(),
+                        fae.lhs,
+                        iField,
+                        enclosingTypeDecl
+                );
+            } else {
             fae.value = new Java.FieldAccess(
                 fae.getLocation(),
                 fae.lhs,
                 iField
             );
         }
+        }
         fae.value.setEnclosingBlockStatement(fae.getEnclosingBlockStatement());
     }
 
@@ -6665,7 +6830,148 @@
         return importedClass;
     }
     private final Map onDemandImportableTypes = new HashMap();   // String simpleTypeName => IClass
+    private void declareIndirectSetMethod(Java.IndirectFieldAccess ifa) throws CompileException {
+        // Method "set$XXX" is not yet declared; declare it like
+        //
+        // static field access looks like:
+        //   static FIELD_TYPE set$var(FIELD_TYPE value) {
+        //       return Class.var = value;
+        //   }
+        //
+        // non-static field access looks like:
+        //   static FIELD_TYPE set$var(FIELD_TYPE value, Class arg) {
+        //       return arg.var = value;
+        //   }
+        Location loc = ifa.getLocation();
+        
+        Java.Lvalue accessExpr;
+        Java.FunctionDeclarator.FormalParameter[] params;
+        Java.FunctionDeclarator.FormalParameter newValueParam = new Java.FunctionDeclarator.FormalParameter(
+                loc,
+                false,
+                new Java.SimpleType(loc, ifa.field.getType()),
+                "value"
+        );
+        Java.SimpleType classType = new Java.SimpleType(loc, ifa.typeDeclaration.resolvedType);
+        if(ifa.field.isStatic()) {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    newValueParam
+            };
+            
+            accessExpr = new Java.FieldAccessExpression(
+                    loc,
+                    classType,
+                    ifa.field.getName()
+            );
+        } else {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    newValueParam,
+                    new Java.FunctionDeclarator.FormalParameter(
+                            loc,
+                            false,
+                            classType,
+                            "parentClass"
+                    ),
+            };
+            accessExpr = new Java.AmbiguousName(
+                    loc,
+                    new String[] { "parentClass", ifa.field.getName() }
+            );
+        }
+        
+        Java.ReturnStatement ret = new Java.ReturnStatement(
+                loc,
+                new Java.Assignment(
+                        loc,
+                        accessExpr,
+                        "=",
+                        new Java.AmbiguousName(
+                                loc,
+                                new String[] { "value" }
+                        )
+                )
+        );
+        Java.Block body = new Java.Block(loc);
+        body.addStatement(ret);
+        
+        Java.MethodDeclarator method = new Java.MethodDeclarator(
+                loc,                                                  // location
+                null,                                                 // optionalDocComment
+                Mod.STATIC,                                           // modifiers
+                new Java.SimpleType(loc, ifa.field.getType()),        // type
+                "set$"+ifa.field.getName(),                           // name
+                params,                                               // formalPameters
+                new Java.Type[0],                                     // thrownExceptions
+                body                                                  // optionalBody
+        );
 
+        ifa.typeDeclaration.addDeclaredMethod(method);
+        ifa.typeDeclaration.invalidateMethodCaches();
+    }
+    
+    private void declareIndirectGetMethod(Java.IndirectFieldAccess ifa) throws CompileException {
+        // Method "get$XXX" is not yet declared; declare it like
+        //
+        // static field access looks like:
+        //   static FIELD_TYPE get$var() {
+        //       return Class.var;
+        //   }
+        //
+        // non-static field access looks like:
+        //   static FIELD_TYPE get$var(Class arg) {
+        //       return arg.var;
+        //   }
+        Location loc = ifa.getLocation();
+        
+        Java.Rvalue getExpr;
+        Java.FunctionDeclarator.FormalParameter[] params;
+        if(ifa.field.isStatic()) {
+            params = new Java.FunctionDeclarator.FormalParameter[0];
+            getExpr = new Java.FieldAccessExpression(
+                    loc,
+                    new Java.SimpleType(
+                            loc,
+                            ifa.typeDeclaration.resolvedType
+                    ),
+                    ifa.field.getName()
+            );
+        } else {
+            params = new Java.FunctionDeclarator.FormalParameter[] {
+                    new Java.FunctionDeclarator.FormalParameter(
+                            loc,
+                            false,
+                            new Java.SimpleType(
+                                    loc,
+                                    ifa.typeDeclaration.resolvedType
+                            ),
+                            "parentClass"
+                    )
+            };
+            getExpr = new Java.AmbiguousName(
+                    loc,
+                    new String[] { "parentClass", ifa.field.getName() }
+            );
+        }
+        
+        Java.ReturnStatement ret = new Java.ReturnStatement(loc, getExpr);
+        Java.Block body = new Java.Block(loc);
+        body.addStatement(ret);
+        
+        Java.MethodDeclarator method = new Java.MethodDeclarator(
+                loc,                                                  // location
+                null,                                                 // optionalDocComment
+                Mod.STATIC,                                           // modifiers
+                new Java.SimpleType(loc, ifa.field.getType()),        // type
+                "get$"+ifa.field.getName(),                           // name
+                params,                                               // formalPameters
+                new Java.Type[0],                                     // thrownExceptions
+                body                                                  // optionalBody
+        );
+        
+        ifa.typeDeclaration.addDeclaredMethod(method);
+        ifa.typeDeclaration.invalidateMethodCaches();
+    }
+
     private void declareClassDollarMethod(Java.ClassLiteral cl) {
 
         // Method "class$" is not yet declared; declare it like
@@ -6771,12 +7077,7 @@
         );
 
         declaringType.addDeclaredMethod(cdmd);
-
-        // Invalidate several caches.
-        if (declaringType.resolvedType != null) {
-            declaringType.resolvedType.declaredIMethods = null;
-            declaringType.resolvedType.declaredIMethodCache = null;
-        }
+        declaringType.invalidateMethodCaches();
     }
 
     private IClass pushConstant(Locatable l, Object value) {
=== src/org/codehaus/janino/CodeContext.java
==================================================================
--- src/org/codehaus/janino/CodeContext.java (revision 72158)
+++ src/org/codehaus/janino/CodeContext.java (patch janino-full level 2)
@@ -34,8 +34,15 @@
 
 package org.codehaus.janino;
 
-import java.io.*;
-import java.util.*;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
 
 import org.codehaus.janino.util.ClassFile;
 
@@ -135,10 +142,10 @@
         DataOutputStream dos,
         short            lineNumberTableAttributeNameIndex
     ) throws IOException {
-        dos.writeShort(this.maxStack);                     // max_stack
-        dos.writeShort(this.maxLocals);                    // max_locals
-        dos.writeInt(0xffff & this.end.offset);            // code_length
-        dos.write(this.code, 0, 0xffff & this.end.offset); // code
+        dos.writeShort(this.maxStack);            // max_stack
+        dos.writeShort(this.maxLocals);           // max_locals
+        dos.writeInt(this.end.offset);            // code_length
+        dos.write(this.code, 0, this.end.offset); // code
         dos.writeShort(this.exceptionTableEntries.size());        // exception_table_length
         for (int i = 0; i < this.exceptionTableEntries.size(); ++i) { // exception_table
             ExceptionTableEntry exceptionTableEntry = (ExceptionTableEntry) this.exceptionTableEntries.get(i);
@@ -178,17 +185,21 @@
      * Notice: On inconsistencies, a "RuntimeException" is thrown (KLUDGE).
      */
     public void flowAnalysis(String functionName) {
-        byte[] stackSizes = new byte[0xffff & this.end.offset];
+        if(CodeContext.DEBUG) {
+            System.err.println("flowAnalysis(" + functionName + ")");
+        }
+        
+        byte[] stackSizes = new byte[this.end.offset];
         Arrays.fill(stackSizes, CodeContext.UNEXAMINED);
 
         // Analyze flow from offset zero.
         this.flowAnalysis(
             functionName,
-            this.code,                // code
-            0xffff & this.end.offset, // codeSize
-            0,                        // offset
-            0,                        // stackSize
-            stackSizes                // stackSizes
+            this.code,       // code
+            this.end.offset, // codeSize
+            0,               // offset
+         &nb