|
View:
New views
15 Messages
—
Rating Filter:
Alert me
|
|
|
Manipulable ASTAll~
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 ASTAll~
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 ASTAll~
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 ASTAll~
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 ASTAll~
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 ASTAll~
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) eJztvQl4G9d1KDyxY8dWnDixs9uJxzBtARIJYuMqUSLFGZAgwUUEqcU0RQ2AATkSgIEwAClapM0B F0k2tVhe5EjW7iWyk3jJajtOs7Rp+7/XNu1rXpO8JE3bz6/ta9L9/e3/9fuXc++dGcwMZgBQkp38 /QJbJDhz77nnnnvuueece+65wezwhnbv7KZNntkar2c2sq23tXWQy8Um7/UGZmsaZ/m4kBOzNQ2z SX6KT9b4Z5PiRE1gNs2leHgriflsDH3JcdkJPoe+CLG9fG7TJi+AaybgWAxCBYuhRrmcmJZqWjD4 8VyW52t8sz5vu8832+5H/4/XeFtmJR7eYcDjWX5KkAQxDYg0+QOBABSp8c56AYKY4dPjWVHM4Vee Rk+7D9X2zMaSosSPowYQyAZU3lcDncIV4kKWjwFWM1Arx0s5CUNTIUBh36zXY1HaPytlY4ayGNcG pbViQSgCiJBm/RaAWkiz9QScrzI4X3vjLPwP3wI1zbNcPD6eEJJAtqYioPqRdIbLSvwweuDew01x qPFx6Eudl2DSoBKM1NUhUc9Occk8DEvWWFtPE+9so0VPDBgkxOy4xKXjUXH/uErXIgyMfrMBfb+u coeUs0e9xYi6oV4yaYO0LTX9ZHDwWy6TSc4AtvtzcT6ZQwAC4zUBX1MLsG8/RVH/8PDjkSM7Xmav e5a5fpFtfrSr7jwr3/AkK7sWmOsPMfJ7nmQ+8TT8kpnrGbl3satuaWBqsVv+0HFWTiwyBU9XY1fh g08whbU9ch4q9ssTjzAiU3hAHip8rBCW7+guNB7sKWzpKWwLFfqyhXH40rNwG7PQyMgTh1iZYeQb TrDyQ8HFREjeycrr2MUNvQ92L9z5KIBkC52LzOIOmSmMLzDyzSuM7F7uXRgLLvQdCxYmlgCfLtkH v9qXmNquxUSBkSNyUO4vdC3dxCx/bJ6BH6+xy9u6Dl4H33oOrnmcXd67yCzvf5rdcT68vO1w8GCq AG+WmYMf6TkUPsYcSpxmD3UfGj/4sUPMIccKe+gGKP3gEWZ55lH20Owh5vADi8xBgV1+6BSznF5k l8Vl5nCaOdTKHnzgUHB5TmYOUwvMocEl5uDa0CPhg72HP/lo1/IBmXlkxyPMox+Ug4+0LrOHAavt C8FHmd7ljTJ76C6Atsw84u16lF3qOjjKHg4Wug5/mBFfGFy580jPyh2P96y0R1YmF8dWDiz1rIwB xX3L7E0yc2dopX34SPciszI6eiS6NH70PQvMkYeWdx29Ln60l4GqR+64/+jDzIr3UXa5buLYvcux o1NLzGIn/Cr0rGzeefRhmZU/vQgDEIUnvSsz9z/20SVmZWch9tgt3fJUIXlsaCV2rGYhcdy7FDwW Tp0Q4cmh1OMfltkTGxahjvDYziOTj3/skaDcCX+tsI8Ndz+xLvEkA03yTzLZJ7OPTjxVU2BW0gtQ 6jBz8n1Q/3H+ydll5rGR6cc39Tw9nH0yID59rzxw/OFD7NM75a6jUHf6ke4jH1lgl3YET90mM4tB mT3lKzBHWgtdpzYwp6KhIw2Hek4l5PCpz8hQZ5E5dvdi6NQuQHxaZk7l4TFQoXE5efoz7MmYnDo9 uNh3evBQ8DRdCJ9q6zvdHFzZyi59col5ekRmn9m3wJ5qC55qW2ROpYOneg4DEObYUPczgsysNBa6 ntnzGHO69zFWfk/PqbHgkTlAIQu/2NP+rqO0DK12n71viT2VG16pldkzLczpjzIrtQvwXA6f/gxz fDtzJrjMHg32HBuSe0/ftdBzZjvAYc75u87OMAeWmGP9C4AjNFhgzs5AgeCZG0NnbmLOTYdXti5A U/ArfO7D/UcZ9Af6BWWg5AJzZg/6+ih7bgd7pmcJesSc/jTUvP/Mjeyx2wvMiS2FB85t6r+wSWYv MHL4nHPgwoTcfWpf+MKmrgv5Anv8vXLPafoEc/GjXRf9S4PnnMu7zm2ENwsj53Z1PdNRCD3TPXbh PvZsbveFMPxcBAgLzLO3F0JPrA9fvGvo4kTwmc77z7WHLyRk9tnbRy/dBy/5SwxqjbuU6nouJG99 dmPXs1vDz+2RJ5/d0fXcNLwNXuhEL5/fF7yQH3j24/BzgT3rZp7JQ0HmbH33mfeyFzsW+Yvvk3sv tnYd7e164ePd571y9/nWkc/d3nfq/t5Tm9gztMwcDfcee0iGfwXmhY6tZz/DXIRxvzgbunhn+HNC AQ3BEw+DrFqAV6HTDkBzGcai95ne7hc/NvS5HaHjtXLPi57CxOXre170yu3M5Sb28uz9l9kF9rH3 FqZfet9Cz9mx0Nnx9IvXDRyfA8acW0hcvn7w81yhvfNyU2H8pbal/V8ItTOf/zTzmFuGfwV4zb5M y5OXm1byl9lC8AU688V9vY+NyOFXpgu7Ll/3ZN+Lt3EvU8MnrpdHXs0Wtj7bvv3lDSOvShrAXV+6 UT7wpXsBJFQMn2hlXntfz4mNC7tevV0Wv5gNnhDkrq+0bnvZdXDktbugatdXNggvbwu+fCD60i7u lbvZl3YVBr98F/tqP/BdS/v2r4j8V9OTX3UOf+321KtszxPrZPhXmPjCdb2vfwK6+4VWefcXHpaD X7wfetj9ymz3qzcDjjL8MfhkQIZ/ha0XE9su3771zZ0YwUX2THKBfeW60a+7F9knG2Cy7f8Gs8Au btn35c/sfDIxdPbTzDf74Mvi+JuzC+3tr/q6n7pbhn+FiReDod/aKgsv7l/c/8pNy+y54V3fumch 80Z48Kle5rd2Dnxz1+5vpnu/dX3/t6Eh5hsNzHd83d+Y7Xr6zuDltge+dU/hwLfa5Yfemhw8SUFp +Lkw/mKwgBoYOOmT4V9hF/wNaG97+k4Z/i2OXb5+Af68/7Mf3fbNjfCzMPnmgdHfvUve+41A+5a3 7mV+b+fMW3v6v+fq/177+PcGMt8T+09RMvxDpBn4PxhZAMowX6QPvLS7+790Dn3tQ9yLHwof+6Tc 91/bFgB0QXzxuuFj3cHP5QaPhRcmXrtr2x84Ae3Xxpg/vH3mSzXwcynz5bt6QTTCP9T38B8NALVf 3C/vfmnjIvP4bQ/+Ed37B/6RP24uTP7ByLY/bpbb2T/8CPMn9+z+fkwO/vH1QNfMG31DT1Jy5L91 Mc9N7XrzA8yRXeLv3DB8MsH8Tjv8LOx67a7Ry+OFrlc69n9vH0AY/93WbX/cX9j9x3H2vzem//S2 yON5Gf7t/qOEvPVPPxz+mq/n3O7eZ5pgTnafj8rh83t7LoEIvdgavtTT+7mdPaeD8tiZ8b4z9z4a /OFDC8yPblgM/8DL/3BfYeT50MAPNwVPzyV/GJHZHwKv/Oi9XT++h//RraM/vifxo17mGc+uH0aC L4yB2Flkz4+BXOn9H+zg5RsKIAFHvxrpOeeFRXik68z2heGf3BT8Cb3U8xNX35/FVsZ/8h5oKvoT iflJkv3xvXL4v97Wf5mV+3900wM/TQfP1nM/+xDIgaGffWjbT7fK7I8+uOsn2YmfRid/5ltgfja2 AH+1D/zs/cyPepd3/fQT6Kvc9fNu9kcfKwR/PBX5ebh925/PdP9F18hfbGZ+mhz8y4bIX7TDF6i9 HPtJdiH2k33CX91QiP30E8JfRbr+aoT7WcvKAwBx8OcZ9u07CuzbjtGfJoNvfyr49t3jbzvjP2tc YN6+S9719r3s/2wusH/9wXb2z29ZZH66efdP9y7G/uYG4a93PMX8uFEO/yQrd/3N/vG/pSb/Jwdq SmPo9XsPDgLcHT/PLLF/M7u4Fb53/+z9vX8288DFjez/GHzg4mj4jK8Q/LsDC6EfugZ+8VGgD3u6 p8D89QdDv6gNf+59XV8ZHfxFOPTCxq3foLb+Igwk5f9seOsvOgrDvxiSd/xiIvxLseeFjdt/eVvf L8XgLwP9f+8f/WUXjGLXmZb+X4T7fpyP/v3DPb8Uh370keCfRZfW1K9bQ6+je7i0kBbpOrojTfOp KB+P83F4OMWNDveN0TExlQGNM7uG7hQzM1lhYjJHO2Mu2ufxeOvgR1Mt3ZGF6iPpvfASVFFaorO8 xGen+Lh7CMwPKZcVovkcGC80KMZ0XuJpIU0TyykqpLnsDA1ac0qqpaeF3CQtZsV8LiXGhYQQ42pp LsvTGT6bEnI5wCqTFaegEJejc5M8VEsmxWkhPQFIpqGhFJ9rpWna65ZoMRET4zydyks5wCbHQTtR cQpepkWw0PhaqC9IdBKQg3qxJCek+KxPSAP8eD7Gx8VYPsWnc/VilhahpSyd4nJ8VuCSfjc9DC0j 8y+fmxRnaCAXnRNpPh0XwfKA+ikxl5PoOJQGAtAJeCCJidw0LWX4GOAviNPZtCQNd4cidGQgOLy9 Y4il4fvg0MC2EMMy9Jad9HA3S3eMDHcPDNG7d3dE4PXatXRHPwP/dtLsjsEhNhKhB4ZCfYPhENQA EEMd/cMhNlJLh/o7wyNMqL+rlt4yMkz3DwzT4VBfaBiKDQ/U0gNBuo8d6uyG4h1bQuHQ8M5gaLg/ CA110IMdQ8OhzpFwxxA9ODI0OBBhATMmFOkMd4T6WMYNsAEezW5j+4fpSHdHOLyFBeAdW8Js/04m NMR2DndCDwByuJaODLKdIXYHCyh2DO2EhofozoH+CLt1BAqEOsIdfR1dbIR2Qrc7R4bYPkAsMrIl MhwaHhnuGhhgaGh9W6iTjWygw/BuJMLW0kzHcAeUB4wjG7aMREL9w+zQ0MjgcGig30V3D2wHzKCV DijL0AP9QMQB1PL2bha+9g8PdUSGh0KdgMjwwBBQhu0Kh7rY/k52YHsowrrojqFQJLS9Yyc9MIJa C9IdzLYQUCES6uyuX7NGSGXEbI5GFpZbEN1bZnJ8RzbLzYTSmXwuAhY8l9oAv4AZt2cF4JUN+bSQ cyeywCjTYnavGxlonZzER/LwVsxOuCe5PBhs7kEwWCMxLp0G23UbmPg50d2RigoTeTEv9XOpjliM lyR2fybLh/n0RG5SkoSJdE7cMgCTopNLcpIUhp/JTjEt5bh0bhsYsZ1ZPijwyXgoTSy+UIwXE2Ex xiW3ccDD0STfxwPvwvspsZ+f7kiL6ZkUNBcCjIHFhQf5+CBid0AN5sAkL23Nw9OEwMeHYdIM8Qk+ C92A9mMjazL5aFKI0TDnwXyESSDR9IE1UoyWnC541kan+Wmn2z3B55wu1wbJDXav0+nIATGTvEPq 49IzMDnzWRAHc2tc9AGotoGeg5nDTYlCnJ7kkxmhFiDD1MnV0lFRTPIclBUSMy6YwFlxWqLZ/TE+ Q2ec6XwyWeuEZl1ZaDbjdgoJZ2YkneZBvkhOWpqm81NOadrNxZz5KaAan82x+6Bf8CgnDk2NjsHs dSfHQBI5hXSOFgCIZwP82riBXr8eyCiNCmPwP4xzTkwBbFpwhZMuuq0NJEvSm51y0pyHSzpdtVzS nZyEFjmOc4tiNou+R0Xyn1vMOOlYLOYmXp3hGeiYGz1JbqDr63OiCAIrNknzCUAjh57zMT5Fx6am UG9i7kwW5CefhLbhA79SKSEluEUuOQy/UgAS5FKMiwlOOp1Oc+4c/IsLKfg/HYvBIKUFgXPSmQz8 2JfLwv+0BLjl83lANg10BYxBTsb5/U46AR/OnYDfPKrBSzFnIjvqcTq89Hrat87vqKVh4UgA+XJZ p2t0bHSsvp6PAYeAfE3XHaAdTqfXtd7nctQ6Djj9dF0d7aU3w79WJ91a56xz+lzwk67bT993H+2c oWdn6QdnXPuBmm20s65uvwP+RbkH3VEu63InRNG5v5YGMOvpQIPjbmeUC6Vz/ASfBXq10lDOSbfB 0AiJftE3t2bN3Jr2gN4Dhv0lyGvkRy62WFNLU1OsIdEQbeRavA1xT8LXEosFmv3xJl+CK+sVaUAQ AoFEzBNr8PP+Jk805uN5Hxdvaowl4p5o3B+NcTWNzQ1NxHHyg5B81OH/ys3U4zw1X/gM9W3x8e1f pbZ91zO/+Glq5Lve+YMtVP8f7J8/zFDbv1s//7iHmoCHh/d+8AdkpiCR4YQJRBtdUzB/4NdgVszB rODjREJ0xLKiJA1ysb3cBC85YLJ9YA2tfFYNbDsssUJaAbZaWJ2gL4BIAAESj8wg8QHrLQG7WkiR HAd1kVQi9TUsUXUCSBE/WFpUpAqfIxKtE0tNkYM12hnBoqhT0XHcWwYGhsdhwYtExsMDHQw7VIsk GI1rjI6BgCrx77m1JlXh6o6h0vScobMxd0wU9zodGYILRnbDA2kHvb5YSP04lF4ROMNiRpOtVbRO H7CDqqMXgRwC6ZgtX15XB0lFLMcr10AfRa5PcVnbbqpf5soVmKPt3s45ig9VWs+RX1Uxh4HLf62Y owTObziFfMpyinnIpepRgLbp9W2013PNMFBoEFklEaR3ggpVIyFdIzrYz1kddoSnYkih4vH3arBU yISmnVKnWBjPQ7DFMuRLG5pXMAT6Ce1yJ+ELfuJ0lE4RmEAO/cQkarIeR4CqNoBgMzzMjywfJwWd Dl1J0JCQSqoHNxDdAxOQzumBQE9CWHOP8U6LskgN1QF1C6Cy7+WduVLgZjoIqPzVUKKGdMOCHgAK YBYbsKIEPLKgQBFApBoIEXsQUhU4SOVwkKrBQbLGoWSYwDzQvT4A/+rrsSYsYRXGyNBQGBqG1tTR FEpbQB+9keJUNF43UpP4gQRo17UIkLmOVBHqtWndovk5MwHK9j1yNc37bDtfCey1ad9rgcBcFQqI rWr8n0D5CHveId0j7L12gEuB+64t8JIGqtScTHUV7SHsQ2uiuuiFPe6w1x32uZHr0o0UhcqagA1c byncqwfqMQG9OohSSfeLHYcp7LtyqMbOe/VQvVcO1a73xI9UFdSKhcorXVXMKI2Frlb5Qt114y9e 9YtP+aKAK6tB2uI659AtruXUm6tR88KeXzctT2n0Gmh6+A+NHs7SfsJaAW8V12zx2YHSwTCqRjq1 DOinYFW7mlreK6rls6g1Z9Ur6Wp6JV1Rr6Qr6pVURa9K3dC0HzuiXeY+KSw04SV6DXJTa6oNav4K dcsJr7mK0pCEGpKuTUMCNCTZNTThu3Y9Qg1N+Farrtn6H6vTiAbyOZjVq1Evrr1HpirFAzEaElfV +B7Qh6xqVbkq0Eci5e3dneqXyn4NtDOUAxUV7bnQ6vyoBo/Smnuq7cJ/chKClOQUsUn+OkCDSbQH pNHVKhvXyMHza6NIoFnrxpP610V9IDKvX8z1AxBn7qo1CyM8XNdyKw1vhEWbGpv9CS7g8zY3x6I8 52nw+RpbYs18o6eRb/Ym0FYajndu0sc7N3jKBUvXl5qsSkhzoxIKbRvQ3Fh2664JnnsDSjzz3AdH 57d0rzsysHGZ7ZGDA0v2pu+aNQapa2ERkpUDCwDVhiCOD8m3wW4rsgnRrxkIGPe2NHPxQDzmafTH muP+hqaEp7nB7482e31l+9M8XhPwNmoB2snHbzni/U2A9q9ZgPZnEckHV+5c6V9peiwody5vX3H3 rUQPkpjtpsiRwOLYka6lniMuJWa7ELxvaMU9fPQjzJ1jR5vlrpUs+sUcWTt6tHW566izZyUCL4/s OtaNw7cn7j82dzB23LE0cbxnMXosFzzqhMFY6DkS3HlsboldrkPR3PBcZlYGC7ETN8IYMXsfmwse uaf7xK74sewh5vbl3hO7oMRS8sTkEkAKPTZX6D3CQvUEgAT0ex7/1GHmSGt4ZSs0LDPHBDn45L7E 0fqD7FP39K64DzOPDy8yRzof6T6yG4dkP9VGQrKf2llgjt5Y6Hoqypy8OXT0PYd6Tn5A7n4qBG+A HY7ftRg6ecMSVJSZk3fC4wXm6HXssY2Lkyc7+k5sFU5m2cfugJ99T+za87RzgT3Odp1woqePRxbg 1+H+p9fK7BM3FoInOwFgPPxUPHRyNLjSzS598hH2VGIx+FR8kTn50eBT+5a7aw4NPLZlZfCoU4Ym mdOj/U/OFPqeqi30rtQWAN6h7adDiIXDp3Mr8HUZiANfd6xkek9e339sQ6im6+Qn4bfce3JL972H 4CdAQdWCZ2+We87sKgQ/mwqevWmh58xM/5kHDwfPsnLX2Q/KzNlbEa5dZ8Py8Bmx6/SHu8/2FvrP 9iwGn/EUwmci0MrS1nOfXIDfBeZc9wrAhUIF5ux2+OsQFN56Ltt1qrEQOdXWf/r60HH2YOizDczx nr6T74HHQLP+4+1y3+kIW3ig+8KHQ8czQxcc3Re2F9inP4Vim3vObhi8MN1/oXmBObx14OKdcuji HV0nt8hAr+HzvWOPB7vOTwbPJiKn1/bBi9MbgCbQ3fGLOxaht/AaoPVf2L314h3MudzQs85H4F/P 2eTks0725J3LwVNM70Ufe2YUxq6/7/Eg85wjfOkh9tLuofPr4Ofu85t7z8eXHzi/v+dC28CF3T0X P7DcfbFx58XN7PEPy4z8mYW+i2Kh+9SuxeATjYUHzs/1v/DJwecjzAvN3eemx59/EJ05iLzwme4X Ni5EXvDKUGDgcy5U4HMh9oWHZO5z1wMvAP7ws8A+7ypEAMLlgeDzTuCIpsXukx29zz+457IfyClv Pz/XfXlk8MI49L9h54s1i12Xp7vO7Y5d2lIA/loOPn9nEIgJ6J+8bvDxjzCFG5lzTPDkp+B7e9ez Nx/qeXYw8uzanpfykWfv7z03OfLcBwaeDC5s+0KYfc4x/IUs89KekefWMZc4IEPwxK4CQA5+/s6+ Fz/OvnjvyGX/7hND0NwCc2JX+BS8vDwG6EFnuC8+vO/Zm7u+QMlDXwgPPe9A4I5/ePDEtkUYgYlX Ju+/EIw9+/G+525jXm4ae1WMvNI29AoDvQi+3Dnymmfotd1bn3ONnmfDxz/e8zLNnti5MHBq1/0X KPg7++zNzJkt7EqIefXW4NInI1+6ib14x0Lw4h2PdH/+jgLz7KcWuy7Eg0tJmTl986GuL0/ILBr7 Nd3nB7uPTvU/+6neL433XHIz5z7BfLk5tOIGLpWZr3wS/t1VCL4MAM59QmbOPdDz1fc9BjIbRO57 F5jzt7LrIi/u7r4QH7qQLrBfvWsxeHJj5EIKutf1teuWmccHHvgaDMOX+2CGsi/VhZ7uk0OnW4Yv uYdOr+36yl6YmIXQyXXMSs3I2Xzi0pbwxaltZ1I9L8XhXfDr7+u7sPv+19ct8K93LwCJe16eHXiZ Xtn5avfQWT/Qvv+NtQswbYbeOIAG5okbJi8Ed77m2fm53NhrHvHVsZ5XwuEvdS2MvjkdejYuR77Q u+u1Nnnba7u3vZbderG27w2+7yVf10tjwZf2wCDAHF0YePXDA6c/0XcmxfzWZ5aDL8XZQhMIMn6R Xfq03PWlbnahoRA+mz/Ydek9vZfqCuGvDDPfuulI8NwDh5inRpZ7HpMOs0ueRRjjvpPZ4Eu3d586 MPi1/QOfbWyX/V+/5TD7nU2hF285CPiKr29ql6k37wBarj8Mgq19+zfWDZwXo9/YGboQgL97fvuO 8Ve3HPhG+9bfWQ8TZOert+743nD/a30L9781cf9r+3f8rrc9/tbE9lc2Q6Wxb36k//z1o7/13uC3 PrDMfO9BmfkOrLNPNDNf+3DwO9se6f/ytsFvpfq++HC7zH39FiDU3BsN7Qvb3rxD/L197LduZ86v kbvOuRdg6uz4/VsHXrxxiTm/IfKl2wa+tj90UmiXpd+X2vk36wbe2lFol+/6o5u3fT/C/On1/d9P bfv+w+EnxtvlPV+/pe8H0d7XD/T/IDr3xi3wcvq/PNS+EH/zA+2ZP5qR93x/T/dn07gY7nHwbE3X t9/HvdU08ebeoe/du8icunfXhS3tvb+T6PnyRuF77+1/dhJ+tssf/11n5i/YvW/u7f0yLK5fH1tg VqLM27UgT+XIX7l2/V6kXf7ombWzZ9n2Lec62tvPvSd7/vq+H9zVd7Z34vupnlN37fr6LZHHJJk9 /36gKHwbveBYbu9/s2/8Sw+2d/7FjeG375SH324LvR26/839hdG3GkIvOMW3mhaSb/kWgM4y84u5 Qvc3Qz1v7WrveWsivNK9EPqzPT0rPTAf+o8lFrr+VkK/YCQXmWPp4b99OPT9zNa/yRxl/gFWvh9u eDT49w2hb44u/iay+jeR1b+JrL6iyOrQAA7pNQRTJ7n0hDvLJ5JgfLlR2PIwjp8lFnW1kdad4T6y p6qcEraItkYB3cMzmS2cJIDBFts7khZw6DXyBPJhLqmPoSZmvRI+1kfcvEPIezGEvI5hIZhPx9xB mH5ccpAbzOIYb5jrqj1Jd4B9uaqgaajgmMnxQ3xiEMxePs6nY1lamsNh0ygaG4VOr0HuHWL7d6Tj KKjSScfyWrB0bS2N/gMLPyjm03HiVMgJySQ/wSWJk7NfjORjk06yKRzLE0cInWybQK4HehLkTB88 aXM7Hd0Oug25JmI8DWSJIXQd+SQQSvFqZHnV1QCChjggnAhKrXvLTtdcknvwwT4x7h4c2RIOdc5k +NGxA3lGzCPKDsAzdmQgww9kxEwtTSe3eGtpn88Zp+PugYQzLorIRwsvomJ8ptYpTQL/uJyjnjFs mNfSHjE7OgZ0ql+3bh3t6EQn97MOECUcnYThonNZLsbXR4bZpAQouUa9Yxvq66d5WkwnZ6ArUZCf Ui4IkjkspPn+vAeIL+Wj8L9jP3TN5/a4aBgW/Oc6fy47k+CEpNORFFIg78QMSNE5gJKLTfL7iUNl OJvnnfx+SeJQ990o7tDpoB9w7H/AQQsSl6azMKwoyp1uc0bhq8vbrwXiw1Pi03ZvGx1zoZgD6KYg JuhGQKPW7WlB0fiAlcMBDIWOBux3tHldtQMZp2+9Q4t6z0+5pxz7kXPC7WFgXYKXjGsDVMPR73N2 fopm5KdIeFuaEgGPj2uOewOJmC/WkPA3N/saW6KNjYEYHyjrp2jBIdPxWCOXaPY3ehOxaNwXbYx7 4l5/oyfG+flAzN8A7TYQT8b55gu33LiDOh+g5ncgvwreRFXDhLXT8RKa2GhqaI4rQzlDEgBjWdue Yjx9jdGANx7jEg18kz+R4PzNcZ7zxGM+rrGF41s8Sj4D36zXa5EGoAllRoBlbYKkTqhwAh+VKcKz ysvgbVQB1qMFFwkx0nRlyN4iZKvUCz5/CeT6PVhTIbhUbsDX3qw2EDCkJQh47UDXgyzNadEs2KuH 0TOmnDDmZ/DbIlqPlChbKI1GnOyhmGJs7OA1GbEqHRgNHhHytoCaqwVE1iZ7QC3V9tCw1tnC81ml +vA120IFnTNpBGPPK+UyTWD+8aNCcc7f4mlKgM7XEGhoSrT4vd54U9TrbQn4/C2BmkCg0esnMmL5 PPpfvvXoWko+eD81v5tabpQPNc/Le6llST52LyXfLH/3ZupVr3yyjxqSf+/B+cUbqbj8B/dRj3bI f3gjdblH/lEvtXRjYeHuefk2asuPKapwc+HQ2vkBamfhlEhxhUfd83IdtXBb4QhAHacKf95ROA5l r6fevGnxH7bOp6jtUOcvdy29uHN+hhKW/nkTdWj78pEmLIRS3EyU71S87YK65xuDxSqWqKWjOpk1 t8YovhRrwLhbIClVoZ5WWvtSXw+6pSSAMs4nEsjbD8sCVr5hOU6CXj2jrLq0pAab0QlQSpC9gPY9 aA4v+HpwGe2ERQbHoyGXNlZ2pAq4gm2TRaeZrPAFuMoMw6YFByYJj0wAQAMaAl7IcpLbCB99wQe6 zPs6ZT7QjJhBQLkkI8agRdRGycYQ+igKA+1E+kdkuAN0c3qWLiojrtoSyMRq4rOSJUC0W4UFElYc ER2clVDNQSFLWOonDrRFKiA6t4d6BXpT2fLG9t3bBkKMZXkbMI6NsaQADLvJUR3NUXwrKNzlyRFU uI3wCTrdoynESFsHxR6UmVoEK2F8XCSzfk8eZUDCm/KYfcJgYVq17qxYgnykJBADdGdQ4HDglgS4 gIZkWKb0zO5zSsn6+hy3l5dAkUSwN25BNoLW3CayFaRssKqHFdXaqIJW1IlRSybdEuh4SK8mkR0l 1CSDamiFjiILwWnxxkVjhnEKOiGjfoQE7bw7g5JIgaGj1ejk0jgkFUjejwcA9N/77iNTcIKHhQzK SZ0g+51RyYUbAwmm2hIKkbCxhl0QRfSVTVzrrkfdkvpd0tSycpQjfTWOqpS8ajykpAERIeFMxN2q BNkChgXd1kZ2SC0GBn30zMFms2LW6VD2grFvZJKbAmGHTRSYUwDbMJktxmhOPa+LD+8Sa8zA2JgO ZQoJCU7P0mU4GUpebWOranELGVLcMFm/0RLuPrVt/nCW+usOal6+jooun26n9sovjlKPBv6m6RD1 +kf+aeP8Q9QO+bPtVP/SxWbqSLf80mfmlyLU3MLJj1DdC4+0zvPUZ9fKX2/5OlX4SZ/8gw/Oy+8l YB5tWnw1Md9JvT25+I3B+W6qfekStUBFl849TE3AG+r0HUuX1qEVfTsqvXXpXzZRF8aWfutj8/Iu il/6y6Z5eR91fOvSX6bm5ZuofUvfvJdaeWDpX2+FNXNPPpUpLlYqq+o+YlYAhsKjjARbz0jf4Hgo OD48NMLCO+3vYEc4whJA1XBySAlWANGOAgN0pO3i8ZlvMLKndDwV0kUx0EIsqVYSdM+LpZF0IKDb UGEUax5mO/rNjI8h5NNRcb+QnugU02BVo5R0Tidma6QmuOjsVK0eRK2CiQZSp//QfFLiS3G4u81c xwoN47zrF3N4qpHR4JHLRkKowcTLTtlOvDmj0jGNXE1bslw6NulE3dCPJBAGTwjs2+HdhlHdTA9k kILsDgX7Wbq1+Be7tZaOSzlLIYcXNPPs0PDBbY0gBy465k9QyPPFlRvJZBgApEi7BxIJdBgGGtJW buC77ZN8lqd/dQyLhhMIJWL0AQwQ0HG3o9xQKlTQaqXjtaRTd+swNIlNIuAr6tQKl2wp4Q8ltQAf B0aBdq0YZdXDtkUwjltULDtu9JWP3LUeO8Pg4WQF9DuxSlRWtRQSGdapXw0mIF1tV8sEV81EIG7s oglGVIR+0HOhXRThXOOg19Mo2QICjVgQvXPqGL0jKiGPJc4WoWrSqA1lMYCqOeObYlWdDaaYgtgC w/gDf8A4CzmJxgw3w+eMfEwQLarudIooN2244WJAs65hp0UvXfplxqnCsNasMOXjxKhUKd6lBU4D vY2z/6oRKi4ABmsV0YjEeyEaKYF39LQARjWXBDLG0d4UjUYACsJbUIs5sAkyYD2DnAAq64FJaCJL KBIPucJxFQXvaZ5Wley4aeF2hxRNUujT+ohokxOVFwohXRZDXVzLyEIEdKvV1qT+bQO9LDF5FdAk QlGKZYUMDLLTRVt/kA2My/cVzUpd64qmrWdjbAYjSUIGFCbBsO0wOTC5axwulTPe5dmOwj51CXTs NWShukmvkEO4Vt2A/yvbvRZmktWnarvXYAFaWL8WJugmfRy9umIZ4RiMMEkxhKuhaQImo/lMRFkD GmuUpaas08Z2Xr19W7Zj9mZuOepcta1bFiejyXvNOaqUhaCHU5h1kIKj2tYRJDHxNEMCwbVh5Vuq QZj4Dj3/1GZKvvFE06tUVv777Hlqx/PUfJ7a/q1NVL/8z77ndX8P/3bLBfRlfiv16F5q/tAY9fr1 jzbOH8Avv7r7t1uOU8uR//jkInX4OllOgjH4Oi8/4j9LLaXl7+5/jnoxKP9+2/yD1Hb5X+eo/t9u oV5h5H/dOl/YTsnjhef2U4U/GSy8MDUv09TBOxf/4wPL1PJ7lgqu+YMZ6vN3Lz2ybv7QTdTSh5a+ 6n8D7MNvzFKF65feevgNanTp235qMbb0HecbVGzp98ASvXvp9zvm5QAVWfx39/xSL3Xw1qU/bS1S dxgNbNG5Y/GBBbo8dXNSOerqFjkd9xX9aVtgQdtbDgFlulQqhvxS5fGMXhWeSCcU0nneFgeCZ8Vi dKwSnrGrwpNNZXIzlelZsRjNV8KTvyo8cT44rGzoFmI9QgTPisXoZCwulcUTFai9Yjw7kjk+m4b2 8AKdzaN9JRQBQsw0Dc+KxUDrFcrTEwrUXjk9cUI828Y1PCsWo6VKeErV4jlX1M+ikhtp/ZkczMIp q+UId0RpwNrrrLpy7FoGlDdUgBmZScdA3qVRYEMRtFQFaKN2iw7lGLxOCESJf8s9sKWH7Ryuhbfu FDQK1A5PhVB+O3PkgF5bRqAUbblvoD80PDDE7ggNV0Wy4axuRudW0yusqkhufCgrOaN4Bu4uGklW TpOiJ0ECFAzpHSWnyVLCul6Jwxlvg9HSFNan1pS8BpV/IIo3DJIztXRPZIj4tbnkNDcjIQuI38/H 8mizEEcXIstGcdwoRlGUB6XfCiyPhN8GEv83LUg8MYVytAPHgOFgr218VkjMYMdNKx1Kx2DGgPqB 2QXDnuRRuKQVcC8inM9huQViPRJ6SptrYTpNFX2uRarDSCEGNFLeWTSliGpq3aLZ6LLaDtCGWoIi vIHdc7byFJh9ymprAT8peVzi9swVmR/GG+aUiScV0NeQttbz+Qo7qP1FK1hbNFoyjmABIQKXn0A6 oaoIAvwL2K3O9KHDxEWsOJAiOAmZuVCdUn3dOgJ3Hd2l6u00jq7FgbgYL2RuYYgotBfNsQPtSSG9 l/hoSFtzbhUKi92Zkl25tQg67jXtlHi1iFr5HqP3bc6Fg4rV1nFsgR3U4m72OtUvYhAFKoL1ts5U RCcdUDo5VcboIWWUmBYoOqUcYFWjXAzv0alTW/9eBPn3uDTO5KtLtUucw9pHUTHKF6I5jq9qM4y0 yfHVFHuX9+kiiruzElJK+l+0uumaLFJFp+vYFwJNByApWL1TDlbpNw5Ws/AzOVgj746DtUgoTCIy y6eRW1WaxMFIOHqVFiTNx6p3sNYaAEgiqondqmmenBLI5HO0mM9iMaCJy0ySixmcsUggAcz0O+6N hZG4W2M5QSKZJGCdN41HcZfBqUAkG4wD2U4WhgMlkrbesFy1t9dS5VV9nqQXWT4RSidEpwFDg5dY 8eOGlNwyZi0GR/KQ+c7YASFzr2yMkeZq7jcHGF2py/rd9yYjUYIlqRVi18CxjNLP4wcu9ZG6Z75z mN2gvS1+NusLoAet15YoKnq2RBGkIk1A1nEwUyVkO5CNlulJEcwAJegQyQBaykfxK3MIRKaYKA+l O9K5JkhQAlJdjLZrkfPjooWL2ASCxDICuiScHmmDwayYcuraLZnGupFBUtdOdUQfI/q6vxDX4OQS 6uzSR0pMTwIh9ShoarVuKSJKis1axqdRaK6yCYNeaEFkNjVcUqmWTZKGKAPB5+KWAw08ICaneGdJ iyTYmgg4nDHT0iCwfIhEN2Y+tNJzut4Q9lmrsgX6ooWwqrGrdhCz/L68gBVstB4QPleaASuD51MS LWGUoTkxPzFJb6hzWcJCoWNo+Mj8cOJNLPLdPTg0MMx2ojNiljXVz3330QlLEYvGGQhdqTI6d8Kb hxxVNY+BJSA72wx9SMJMLaLTQh44yyKHPknFw1U+gBWXnJQqF0pULlJdv9HHxvAmwUnV0+VXSo9V d87yKfTIDdoyq5LOKESL09m0cWbdhCINAWbnJI+OXIloRiF5y+HuJ7XZSU9zEq2opHFi44L817ap 3DQ9KMK8j4IxD4yXz+JZL6TxaS7D1lQt2oRH6iAf1+zh9gyKKEZtYXtAO2CqWrMaEgAOhFvcVE/K pYhrfpjooYprD5Dcl4fVC4ZQq6B0WP10mHsZ5xMovBrNSyBCAp2+c5MYd/R3WsyRZxo8G1XA0o4u Ouq1tDSoM4YnoaLT3tK/i7paq9pSCr2qjfPCZFLjJICypfhY7R2YpY5CQbyGlKDvdFYJlPRD7YCV 6myLsYGbzNhZ7stqa6ixKoJunBZ47zqUU2LhBJTcybAX6xaUd2grW8i5JzmpH/QY+Ms2Ktw43knk Aik76Ja73tBUGrdjRTI9wQC+5shTBio5Vd5FphtTVM8OaevxLmHXd7kmQr50jtkpVwiU6ZlTFy5K Z/Ev/VASrkGrNj7wSwogDcBO/LoML3FNvYqI8kU6pRKWttPt7PXXVemEFYZcv2dydcRUVE4Ba3t6 7PFERN4JdTXGTjr0FM0kFBJZpinjaBAhh18gpZaO5Y2R1tobnSWNGkbnTjuSSWcs71byiBWPQ+ka k6xitSzSfBKYZcJayhIP6+Tlx1BpweqkiGG/Su29qs/n4qbSoPaqJduKQ6PJ8bh5y9RArVyc+JOs KOV0nfi5EhKy419i829spJjCv1LzhV3UyOO184UJqvvnD88fepAq/GGQmn+ilxot/G/nvJykooV/ 46iRwr9vnn/sDouHB6gXHy78IfsyVfh8pPAf69AWnrLe5UTyRZ/C2bF582aHuQvqR9kBJPYQdmMr u6uqB7ojJ6ZU//QU+Q2wrxDYkMHdXQ7cXKn0NodI42LGzb5rZ11dC6uqLH9jQ0e1njUxbhYXyK28 oXqgVraxLWxQyclGu3kuCEHVv1zODrTkALDgirUtbUClVbvaOrJUtPqshD8QjOTKvWJbD0Go8rQi KlrWsCG0sH9f2boz797ZWXOW/f4V9bdiJywWWw3/cpabgqqtZqHFNqh7krQD5xNxJ5LoXIQDb1gX n1ju4sXRqc2UkObJWaTVbP9UaWPEimNl0kR1oQuKvgXULtLOYvUsXmOJArtzWf3oG94pzZY7uZfL lqO9Vr8S9dWP7qCXWcKoRFA3xpVbOWOat8dun13Zx7J1VJeDazmTDI9s2zM0kcvasjP6wIjpB6GT k3JWtCd3nhBHog5zUxl1t6rIgEBmeJw12IMlmzyYS5XJqF8ByAunoTtoY6aWcDDZ5EG2k/rAjl2w t1kBX2Z/znhoifDDJPJtIB8FqvyAA+1omhqHJw54UQkH9NHPJR3RyeSxZHmLQVa0livRoK6Z9nSN NCeT1lR6qKwC4WwXDCvuNLO98YFpKdANnK7tayJwdGaZLgwlCPyOPWRxZe8M5XujHcXrah3oOl4O SXqy052dyJNwN2yDof3V2KSI9nLgrQo0JSJvSYaPIamiwHXTimMQBbrgxEsYHMo6h5NCKKuAhLPU 4SUirXn1NmY2qV/7cc45WstbR8YOIChxKsYN3DmcFCPKG/yNBoUXiw4FuNm3h52AfJKovzbwgcs2 or3XTWh2b8R5U1RclVVTyY1u3FjGYkbZS9bFBuiCQ1NC9ceEzfvVRSYKhvqZ8T52uHuAaTWHFYLu rlTTqBOdMZFGeUXMtM0l6xO6jFhZREiCtnJH6LFPzOQHAQDlWHcDfbeFk8NkuEMpnMzdymFit9OB F9cSwFV4TdSPVXm9MV4CSqeg2AKFEcHzUZ2JOMxSmUc8maZu28qmWAU9h5XdUdCH4aWEWgUPGIiy tcyegloS6KGJjbKV0eXV2oZ/rdp1mxgAi8qqEFK3ppE9qz5a7d4I+uB1WiGfplZF0QEI/QSqdm/F tNZUM+mQJCO0U2LrqplqduHCSjOr56Qr4SAL7inBpBwvGZSskj6SCJMquMqCo0rQqIK/LHirBIw9 p9m40lfLWUb+MXMPiSxSg5g0JkJLBXKuINmXJHtX6K4ynGnTyEylMk25OkJUB57AUC7DwRCI99BA ZJvOinqharWvon4UuydudH4g+elUmM4FXwkEETvDnR6Xy9JfYj0xrwk3I+61LW3C/Yp59Qp5s6LU syPMFUq7SnKtEmfi13WEJevEdF2cT4H6aJJ02qCVxtVY7Kkp4fI6Rh1IMxjs6jbY1O0GjQfJAxet 7ZeVkkPTuVJVs5iZvWy5S1AYSkfdCoxlZqrKPGXmpysSbalK4f6WzGYbbayYwFqGXlWOpVRSozFu VexhNThQZ4pik5iektxqkVTJW0dFcSGkKvN+xS5azKdKhn+H2k1ijpEeGMZV6Z8g4fgBo00zY/bi Q5ksfo49eTl8lAI9UucjZ1wkwM4jyritF0E78r+XV7nc/giWiR/1zFYuJSIyEfEJ2bRm7UhONFUU ApdmIDCYPHMlxyywFsTZWW/KCYsJYYpPK3ZccZ4p1lwtjuiWNNcSj7JEqODxYyENsibBxXipeDBK DxKNpwIMG8wJPArGMhp1lIKa7RtKgDmNTWKwW0WwwqV8bFLlFH4/rI8gJ4omuMn2RhWcPeEI7W1w e71un8vO1i0xYkmUij7JhdpoTMwn48io1gewGF3EZaxdbfw0WYj0w6JTRBHFiCO0kSi+VbxPNK2T bsW3xPszOkabJFlVOXBA/nXx6LRbUtOai7iiXeeU1ZazkX8B35Cax1OHPm2YCpLJQ5hSd3+R8exx GTfU9egxqs+92L21xA9ThgNI0+ZZ4zSOkKu4gPVB7YhSOUT8EfimQg0LszZvAjU6BmsbCmzHVHLi e92MBbT+jhH1XtBaMa475TXxSh/L5cy486Fxq8qmOsmmuuV1g0i8jgoHpvFYagxXFG7VJLbQRdVC 3Uwug++8NHvQNZjwrlTuFTEjl/AqeJvXYaPXlqa1iPyqiKi5dlF3bU5EaxMWQON0+GmMvT0hTKBV 7ugftmlAS4RVPFaxKtwTXFJSkLd6ZtMhcm2ulqCU5Lot1ykNNoxmFZBLVrkynGO4JJHA8IzZtKHF 7Ov3pVdBLXX/GifGtThcXrrEdsTjBqlpuQYWJaC6vmAXLnL2qnqMCk/xhyjLcdmFGJ9XhKahvJA1 L8YqvJyoeIqlfDKH7ylRYE+Z11olClR/uFF5ZNali5Gm2t9VBXDqDpLr1wrz6pezWvSsVjwlRcsU EncKtFWteMQnr46c0SUlSHrvOG32HJjFPuiRku7kmJoqWuujrevAKs8PwHIn+fREblIJiZrCl2LA Y6MwnLPoTZFD3Ga6Fl/pMNXvGhpXZl1xTbEvWeeLhYzr/JTLqPKbts9tjuTZHsKp8rxQ0b+IT0Lu 2LFDsxdm+KLNsEH9huzopLBXf2hOD0sxEcg2JAnqAQVA3CvhSq36orRaOBhiw8z48M5BFl0bXTPF ZZ26RyWRkbgq+igCiLAVuVoTF95gLDpng2paTNe9Q+jWKlfHwpJbDnN4XRXeqoWFVClkbyY4o91l sVOgPynNaXEVG4zvq8i+PUZjsSWtuiZae3CoB35i2AytWLfUGWLQ+NUPXpNLH2sN6aIAUH1MN2Py OZdFUI4DE62YHcJl6noRKjGbh0ksQ9lWTaeD1X2QuClODMWIVXEmlIzI6kiKrm22dGMYhsnotDBK XaOqrbGUzTa37qS3ZbOWA4o+Gk2tXxvGkCimhnIl6YPfNdpZ43tVXF8VwdSPzWzQfyrQVv04dMcI S7OkoI9p2pg4xZo59Ldo8atkChzTg5UafP24AcFaK54wqbrlT0iT2BYskovHXbJob1oX+mJ8XaWE KnYeHxW1rlmx9+hTpKl9GUebw/7lasahaqz0wPXjQ0SoTWg0+pQekyw+MQtcJe0izrevu0gDPUUS VlcclUFqX3GYsnzOdnW0z66gNWIuYj3slahT+iFeEeMRA/Wz2ltNNIhl7jZZ3ZKog2l5CQlJmbHe YtqVw9vuPhAikVfbY92NICnjfSDoo1tRSxdfYBDjFTpWyRlKqwlp4GohDpxFanVyscliGiCLMyxW envXO6G3T/zq9fYJRRGuSk9/9/RzFa3VaOPXXAtXQo5RsNE1UsHfST3RM2ZczBW0r7WaZyWRrk7/ qaRnV6PI6GH9OmmZvz4q5KrHrerG1c8VjaP6qXAFVhWabTnF1mIu/OfSarFOoPTyN2rYr7saNvEb NaxEDVOrGq8UsK302XnqOCcfir/v36+f//fr4TvVpl0uFAwPdAwX0yf5jK5eHLmslCTXEWsl/caS d+dgjR7MCikhJ0zhjTStZMAepnK1ET07Sxse7xwuNtRgX72zu2NI11CjfclIN7qSWyvZpN+hBiUU M+1QPp0TUkU91OkYSasX45CwbxJ8kjOGzNhGV5CAFaLVGUIqDGEXxayV3SIKRCnZySlu3bjpCM/T KG6h2e0vbs9UiFVQEJCqCEwgh3J0h300KmnbmUoMlvZC20JNG3dgdGpkUfRUu/8S4blsbBJvgXAK /qbtM5y9RCFqLJ/F+bIsNzeUTsHag+EUk2UZdmNwGcmUSaBk+4VAMO7AGLUZ/R62Rk6ckgLVHBVM aifiUhx0zOO7rUn6JCLdXBqvJuz2dVjlAIr9zo4eBWPkoNWmlXEfqEgl650gFX+r3aCExbkx/YYQ CRNQWcN240rtoG4GmDqI9tcSRmxDWuFKwwk1y42liYQ+iz4BBBjTMv1RiQS1y0QE4hL6vTRVIpmn idOhmM854xm4tCEKjtPiBOtAS4FpQwrZZNYi1ZCgUYr5ypUr2tkO2w5jeiOm9xmfl0mTkahKlGIZ fGWStFbHRLizZQ70VC1ZcU1LwVRZ2GIpW8z78G5K2pIppCYOUSaRCbO0ObkquaBQnTtW4Vnlynq1 svgNNsVNfdySTyR4GL+ooh7rnzrxOezyfN9K42VaAa+LdbUTCF59zhGDSJCibi6T4dNxJwmgdbjU vwl4wQZ8aSArQCqWtJw7JWQpMyVQBGOseDeXwtqlCcR0OVvoPErnEp3RH3MpBjySuA0MEsfUBvMo b/g+cgZay09JBB3vngBKZPZOeN3ww+fG+RprQujKd0d1k4dT812SycPltHljDuG17IVxXhkCrhDz EpGlzBetSy6TkqGuLkq2GtPpH6W28lJJo2w0yayS4mgndQZJRf05OOUQIL2Z/Gq9GmBuHWIaHNMs 1eNus/pgzV2jEDozkM1J22FQDJXX02vda102cxx9imzTRuug5VGiEcTvOmDK9MILitd65SekHxb3 8uiGDJADOYMc0F44taZqaUeNw2zKWx5DtCQzjmizT1nklMhxB9xwyUn2nO4UuyWBcNJSlGsNnbbo E7M8hiOVejIJgvYJgezxqBqXkoXXcGCxUsKkuK3Fw+7Lw+KWxCegRXVVvscg/8jg1WrrlWuOzH5O hVEqKFQZqcWRVs6fr2+LToGqBINazVpYKq+VyrX6/LKlHQ9JEoo9UZuneVTXrBMooNw0SqqBhFxc TPPaiQPdCXGFcBiBbtBSknx2johHlB0SHyFLJouHh0zV7pF49fyIHoJT/4drDoxIFCrP0ZPkCVpv hBi+JYKQSYWqHQ8wk22ulo7mc2irBR0jB8SmeRBnILSFOM4gqXKecpW6m0b5tcHCgQFMztSq4LHl Bu+xjEZ3PeBbVaIgj7N8RjkiBR2vkpdUqKRtSWu8Fit75MKWXA5duIIwjCm3YynDBrBs1kNl5PCZ d/U77iE6G2IqqwprTSFDlTQWNtdaHQsXu1rSTDXcvX59CX93gn6aM0kyVEYFb8FIdotIpXpuwmmm aVXSjwq7DWWsoqpglpvAKNH2NJdNI4pbz12UHqEojtIqn1lP6ZIZtJ3AvrIpbazsNP7pmrNKBzGs BQoT0mvHbNQ0FfvyWK0jN6SoPcfOJkknD/ISDnrVz1i7DonI2sK7ZDjb2ZQQB81RhSzZzC6CnPWU qzC57GeR0qY6gUgbtfQq5tMB+1lh7Lcp/NW6kML9ykOnik8lnrXgVE3Lx3YnMmCU0dAfHLRZBOZM SyfRmYuGg2GeFwmAKaqfvlcvluy6t2UGJQfm8slcre3Co0+Dgu7FMKy6ElmWYHKChZYUDHxCp2bU uYbD5VF9GFl0wUYdzlDsslx7rebWFpAF0wQNFKmvHi3PZMVokk/R8TyxOnQWi5jIgSCJcXns0QUk 07D+igkz9moDEkrpPCGKcWt1IIYEt6RUUm7ywIS2X69BBeXTRaGURE56NNOh+7FJXZpoXS9DiVIv ixGPuMgTa40IZtshq8U3jZjlmtJ3xLlONg1CCW95RfgcVgvx2QUEFQlVhDzRFgw0xY90xiB2ughp QYIekVVfIdEkB+ZyDDuJ426YReS+NXpazMalViwqLfsHuk1R8CFcytAXpiJQgvBftT1UgSsZsXlV /yrXDsgjRBG0fYgOPxoZCDo6Vbx9Su2wteQtt8qX6uFYd8G3cGGiK9MUdDVoUUB+FIw4XkJscbcS P1i6VKG0lkO35FLEcl1rKwepGsGkLmjkhI4gxbhsHLFbqeDRSxwwD0ABFVOmSWBaRCsMlWnpsRwl tPTGyO1LyKUiFvGNIucKaDrxcuNQVtOwQcSW/qbabTb1LY8edhbvpaPxneTan4bYK30xchGB+VY7 o4KLy5R62Uv3/SLufveAu5XuR34aXSuOUm+ddoFHmS6A4o9uYNJ3Q/8acND9addBMRnX/1mur+Y3 xH3SaVVU6YUReMUOkTu+mHwqNWM3NrqzefoixWvmddXwA5IgM8mbE3EalTt8SeNMjseu26kS5tNf aIjLOp34dk8XXeclSROjUBkHojjRNxc9Rc8ZLzUtaS6CAFyz9pxT9KZNdLOrtnoEQPG7xs37AsX2 yRNvo/mJDYo2OCq3bmmZ5pUj7HjbBWY+elkF9kljrCH+S0jz/XnkDVPTXpq7RKBXpCLBULJAUQFI 4EjXAk0VVHmMlMtGy9FMPV6tmy5u5VrcuDGJkDWqShNVIlyLgVYgI27daYGQlI1dLboKcD0TY6h6 tMjyXE9vz6KNmKyEDSGHJj5QxI16n5vb7cY3ubkcbjoCypFEo9wpYgppTnGkrohTfHYSbAC3HVur kDB4DEsxgOLavWt28jrGlZXRMc6tky76Hsc4kzzUd6mIiA6DCkOm1sV71urtdvrdkSCj2ce6JK76 J0Hm3e2mAVUNRx1yKlZVdt14s59N3/XHVA2P+t7l3hux1dA0pMtQEauSAJrz4P9flLBBu0qS1K+j R/pHIkq2eXvikC7qCVJyHNdaXpX2BpfTdUAHmsDUkDP5rsgl5NY17ZDS7+HocbOnZwk6qyUVGpAJ PoshYJ3kWhFKD3iVlKqI0+rpVIrNagkVTIpcDtdPoG/XjlBFwKskU0WMVk8mMy7VyaKwqLBgUrTi 6ndeqmgImChYHm1GzOPbkdDai7/+KlDXIWFCXm/QW+hfAHI7un95WNyCcrAp12Chr0jrLMbORyWz Azwquad1Ne1yGZcUK2UkpL0rKp5lVL/CgCZIlpaHtsO/RYzP6Hf59bFrFu9Jv7eqYS3GGxD25bLV 3sMARd3xMu3oqWQgE8rHYdgrRndjp7WTacrhF/V+emM2Wn2WaOPmP45mQg4QhNfqU0dbdKBs+mhj 45Vpocv+bG7G3JNqiGs4DWaRMrBf1GKC1Z7S0L6QxAZWkdpEY3DUqlQz5Pgzj1oxzZiGmikRTAkt OtW8mSQ/N/rL0PWK/TQzc9lpUwma/TwyY2aYRCSY6p2YNgp5ymwqV5rM+0x3WZSjCG7N3ouRVbuD +ubUmeMla7be35HULhnvCA90MOMe+wgZfJBAoh1xIYUOHuH9dwdZQvDlAnhTzyEQxcOh3voO8y2L 9jLw7e/aRk0n9r/hu6U4lE8OX05gBLweP5AcNPzk0+jsImpFBeBAE0ZMwwzBlxmUS/WP8VK2W4jf L04aNXqSjcGtIOlJojuNdHoHR5El0jnlmx55m/e6UNhiNkRDP/B78/Klh4zjT9FFUXGclamN9qA/ DDCMp1isxLcDLWMbM2qhTaMb05vGHKXMa80n/ez2jqGhjp3m63O0Gti7WXr2y4CkxQmazXTAFFFY rhY5OIOuQ29YRS1yVgjValxFLXJuCNVqWkUt9X72zXTzKmqRYz6oVssqaoX6h2nSltezimrhgf4u pZqXbgXdzVDTJk+skdlQNjo0T5Qoe7vEsdZxkpbcbX2KgebQJkDZtvEMo8tgYD0PsCleJ2brtFgC qwmhFefSM9BEeoIUGh1zu91VT52OinOn1EvG5cgiovrK7NL3knKrHQurOKXVELw4dOvpK6R/kaCl tLSj+sbUplUXXt1Q9Y2Eh0PvyHiZBKWeiHYjWzkIDBthdZM4AAqdWUW6shZnYEqSjF2TSgiXVlgq hrGihA4xpV+1uvQOtWoseZ1yySS5nxodaTWupMSEU1RUEiZPTtIqJ5HALODTccl0PqnIhOpSXJKY dYNNEcNda8UySopUXdNOK0Y3p/s3lNGnhbWqbDjyaxZdJsjk7j3zETTMD2kSem7spPZWvYfNcJfc nLmjynUPZL+ZDB+5xE93lRcOpi6tqp7Mscgie8DgVTHnizXlitWORdvVx10ora+mgrXIBGuf2rU9 MDte4/XMItOOH0+A0l7jm/UGavy+WX80Fo3FW/jmZq836mlobm5pbPE0xmIejosGmhoT7b7Z9gZU u2EWRTzMjCNTNc4ncxwC0YBANCQCzb5Ei7eJ9zW0NHB+vy8Q9zYF/IlGPx9rgWYa/f6m2ci2fkq5 4VZ6opNa2UvN76cekVYGTlE/5E82oHtrl+7/x1vn5Vmq8NTo/76P2l44+fC8/BAlLhSyK1T/4uIc Jd+4sOi/TO1ZOEjNyz5Kdi480jTfRfX+3eb5AkvtKJx86Agl/8y/sLJrfrmFkm9Z+n1pvnAbJT/d tPi/DljctLZhDcn8TovZCey9mOTyknsPlxbSolstH9ZfqFZVDcMVbBvq69GpNKiixZQqoYQb1OEx jzE2UuxOeFuc6tAH9uOMEmVvRjQXdqtHcNR8npZXT1RVFeNoWX2uJCnChJo4SG/k6WRIyXFbm7sv VBQU5Fd360VpqgZ8f5X5cdlbMHAC87hbf7YX468dy0iRlBIIW9cGbV3SBl63NqkBxhLYG9A0CvPR oqyKySnmlLvilHUpVwzPIzYbClTEjiMuiw4mohKS6hUqHngM4bDAvMQn8klM3ExWnMhyqRSSKjgk OAVcncmjIDxyR3JHZNhq9SILi3ENU5tDyXn1f1TwMBUlljbQUi6VQ38g/1SxE7pBNowHPn/jVOsU B99q7A35PJxG55lLA0FGvYp7adWP9gDloOJhTACleJbHpwKkSRJMCoQX8RK0VtIdTsWDRi6dlUrI xBv+NN+gakEt7WKYa0gyI5Fwa2i6lKdd6YxBBVbbRT3J9SRWHawoqotO4aNUYBrq+U8bFN1EG+JR /Lpe80PuFe28tZqIAE8wq9u98SkP7RaRCT6NaKtdIOlWucAYqK/F2utuHtfdLk6QV90vdheSGzIf WKqRZD6SiWiVjk3VKZVcujplUj+f0SppHjf0MdwzbKhh1FGLH0xKmzp215ObcqSUqqoVL+rWgtXN +CvPjSpqmd5a6bI2nTTBrK5vtioxUYFtzt3j2Y1uEKbpNuOYaG8TyqW+Jvpr7014KFqz7sla91p0 tFqDpTvTvEE/C/Vai3IZrUFnopNTKJwN/XLj+2itBs+wOtpANV5tmyVQs1cLVX/zLo0uzEVQ4ZcF VNBdX+H/JNd2uvE0tXM5Nb9wKzVyYfMFauiFuVP4wSMPUeEL7ZS8ZSE4v5ChZGppOyUs755fWE8d naLml5qpC/uPpoiWiMGbLz01/23WGirM92L6jpLbVDVTsvjIZuqXBIZYWIq27ZSdhdaTr3ijDf5Y JflHn+LVNvhjcbVJ2QlUS1tdv6TNBt0VU21FFJRb4R3qPDBeROV0OewJUYZElceUU2SHJsZLhnGo rPQuvZVFP4baklNx2KwHrBT6VQix4h03bXps1TJVMn4/P604lpQ9wNXQCZ5aXgRvs2hhCwvjb/Ay rGIASEkzzlcwBnaYG8saEH4Hh9ISFd1pABviavVzKp6Gj9mboxW3vh3Jhosq6oyW84M4a7RLEU0X Ur3b42ibLqYI6Nd8JEsdA6sdSa2OoO0LGusIus1f9aM5SXIVnCPFFDBaceucL+hDtgSsG1Kwq74p JR9PhcbKwnFs3rzZfLej0T4tll3rXFvFHTTGwSmXQEvtvEBvQhmCdEjV0lb3TZp6r7/oqwwN7Drj WmucgKtTmmD+dqTF9ExKzEtXtYiUfFazqhhR0CvmnN2bSmtPyaf8YmRNhOrMKjvdzoI2mhmOPmp8 A8r7xU8rEQ74WL4Wy6DGC2iJpcj+mR6GZmWTP3HOInzSw+sbGx1DieMkLZ4BAdcXRh/UdH9JGIPy EhdAuVOtXhCvHCcJMezN7x8Jh2tND0mGS8va+ESaOloHrEqopcjBEezDSTpJJlclbtWN6w8knF6f ywrCnGXLPvNDQ13lOKLmsMYeCe0v7KEAivJkP8yiYlH8o4pRTjKGluhCSgzV1IGGuS08yJPTHrqQ liiPXSZJjIgyksVbHnEVa6BSEZd0XnWy6ABr97Cpuea1NnTw6q03UfAOSKM/wLVwsUSLh/f5/VxT wtPI+XlPwh/lEr6GqKf8JkojAtEU5TnemwhwfCIa8HsTTY0+H9/U7E3EY14f52+p8bb4yR6KfLBJ fvTWj8mfpt5oo6hHht7YNA/f5Zu/vZX67v3fxbPe7O7qFMW9NjnCiHWLMreohXHiUe1or4hjRt0x +GJ/rhe9VQLfjOmW9O2hv61piAnga/A0NvsDcS7mi8ejnkAgCt9b+Jg31sBFmz1N5WnYhEBEo74G byzBcU3+Ri7g96CKiUBzi6exJeD1J1pqAl5CwsL/2Vr4txs/cCslg0V+jJL/abO8eEAXKIm2m61R xe344gk+EeObogHOH/VG+QB8bfEGvLFGX5Rr8rWUR7UZg/DHot6An2/kG5oS3pYmYB4AGg9wzbHG Zr4lWtPSQFD9avRruz7EUM9spy5RJ/c9s+UCVRj68ocMDk5CeaPPU0pauUMMVzRYdxBjx3u9gUZf PBptaYonWnxNngAf4xpbElxz1OdtbGgu38EWBCIBHYw2BmAYGmINXq+nOdHCN0JPE4lmmCAtXI3X 6/c2Khz93Q8VVj5d+NR/fz917KPU/KV26nDDsbFL1PmGz75/vnAfJb/v9XFqefOb183fTw18u/kE 9ULoO9S8HKem/pf3dWox+3eO+Yeovf/YeoKSb/ynT83LfZR80z9vmx+k5OZ/2Tm/9CmqsOHf7p1f vJ6S7/iPG+cLM9To/+2aL8Dof/z/uW++h5I/9f82zz+ymcrJC5n5LqrwMXlx57wsUPJ18sH75oco eUY+dM88AK2XH7l9fg8Alx/dMx+i7pdX9qLHTvnIxvleqnCLfHR8Xk5RS7fLjzXPFyLUAfnkR+cf oA6l5ad9T1KFT8gXbjlOLbxHvnT7/C5KvkF+vhntdvb944PzOyi5Qb7sOk/J75dfHAFgT87IL40+ Rcnr5W+Hjt8rf2evapIoykI+J8I4wmhvR+ERWZoTpok2UDReSMlBdB2kUgh/MkpBpbQxfoCL7UXh tIPAH3wcRUjqyhrSk5DD7mIGpYTCchUfW84gN7VWl07yU7yW+0A74o6v3SYhtcr+lUBgFGv2cRmj rNFHb2by0mQRQXXLUMzoVVJlg18EVdYAFe0/OaFoUXMSEk7RPi7V/th4KJnkJ7gkCdpErkOyhaa2 RdJSGloqaq8GCrtRf5xi6a0rhu2KlDhFEjKowc46Mkt8Dm3RlaGYmNERTN9LEyZixuL2Fx0eXYKC ha515UY+kDjCxGQO6BgnebO4GM5eQhI7S5IYE2AVmBJyOIBVpZpkjzQX35OXckXsgmK2Qw/GqUZC TEK7xtwEypEN6BzanXKScxQui7663KSoEvyxQU8Xen2bApreTHvcDSjqD35tKDeKpCVNI4MSrlJ6 qr1U8U/zfFwaRJs7kjUz19cb4jr0rGvEQZDYVAYo43KViQWpijg8v7cMdRQQaZxtQANhOdFsYagh koDGJgSplO3qicYCAm1KiONsOilYbPAx1ayYKrIQYjaz0JFUEHXqGBLFjyTzILfJI3ZNztBRtKen zB5coZhSOcbhLD5QGLGikiAHK5ww8XM4s2OxmgNtKOZIclpjITwFolmQrXyOjC6vZB828IPBMgUS GiWXkhazm5Mm4S+0A4PuS+IyG9X8eGQUiB2kgNLHienHJpPPOR2jY47i3QgmxvU26E1vi8puXd1V V3a6rqJl4oFH+6iO2vKVgT7r1+PkQHV1eBCLA0sWrRn8FGfNzqAL41FRaDEh7NfDkER8sITnwODg i8wAyOTIGkayyUp0OawRfIBL+r3aLhOcFJqZKweMXc6nuewMcKMhNThGOC/xpb2zaK2u3NAGKqD6 0NVUvvtqKsNczWn1zZX9FSrDzCq2vdrK68qh7atQuf5qKt97NZXXl6vsrVC5LJNUqrxxY5n576lQ edOmq6q8yX6cK1Zus2+5pVKXr6ZuGVpXqrvpKuoW7eFSSVupbluZ/jZXEgNXUfe+Mv1tqlB3V5m6 jRXqzpapW0m833effX8rSb3Z2Suvu7nVvm4lmddWpr8VBU+Z8a1Ut+4q6q67irr1V1H33quoe99V 1N11FXVnr6LuRr2wW2XdTZuupq6u8rtYl+j9tZZ1tTUFX+NFji/rLS0t2T0YB04cEGSImJWcUV0I p/nKicy0O4O8O861c+pm3lyJaxYDrcJdqDc4rRCRkqWYzCnWT47bi+0zVGVjMU5Zq72p1Lq3agJj YkDEIuY7aXL/KOTH9hC2F1PYDRHFWZx57TJPdBIcbTFJOe1+C3zxH4qZFZFNoUQgGV0lTr1trjc9 sIVtGgS0o6qzs3FYuKDtpA9zWTCJ7TaEzZ1ym/dMSyC5leg1stVpclsZ8HKbd3qNfSS4rqqnLtxT YxWkQldJlXRMsAkwWAV1quuIHVautaZI/JIp01G839x03znNGY8dG1rk3KpfogpimPqlq2vRPaPP svo2SVfNhAHrf/UI4lpFNNQ7JHJiykVzHI/iUV02fGnbFzuRNqrnWrs5b4+KkI7z+1eNjAmHsbX2 RLejYBWchXY/w+RmFIJ18QHNJcswF2L81Q+a255QyXJDZmzFoey+O6qhY3V4W3JmVHQjt+Fqe0mq 2fQTXlbdz7U0CnrG4NBdOLSeBSq6pHNZlBgJVqKogP0wYoa4D9FeSZJP5Oi6TXQWr09Fjzhvi3P2 CqZTtdSszKadnKoqoG90rBxfYufLFbAmqbcqYWffVhVdwketlSCKYjYU5QmoBe/i3IslcXRZtZMP qyzv9NxDJuEVdBLVupJJAlTHswRlp8eTAmYHniUVJ0es7FpTEQO8J0LmKdrwzInKFTKroqsNrQhh 1RvFABJKDQAveXozrXpy6VZa8wjbNoDqrn4wcC3jFhFq3DLjjgYzplM8KhdL6y80trm+pVK1KnGY WzW721GtsnAoiZPQn5lKlNP5roFoSHCIoatboPCpnNIrsq+1bChHq4qxJXraCe808YRVU8/ihvF3 k3w66rCahVrCdMVXMNr8O81//KoZkHdrOXqvNfGuUffepbmgusnVKaCFkQliuVHT+devoH/62qvr aIV2rTWDKs17M5a42oaK5hv6GBb4NOdWg0FHhTG7hb6026siRDnnwDXt/zXDqnRcrrFWirLo7dO7 ZKrVT9Hf77h6qqhPV9BjtaZdB/KilfpBjiZUrc+Su7OUqJGksJenHXV0He13oHiDOmed32VNdLVx 0JCu0twrRyFC0tV5UjKKJ7s6vCxhm1GviABGO5/OcFmJNx94QDkhnVwsbovCNZy0Rpcldy18luXk nR386mUfAmF7VMSNgvCHjceZkH/DcAzIemzMI4jCRsQMDnzBBVbFoOWlW3UIWAcu46hjr6+lpbnB i2KO+QZfrCHhiya8LQF/C+8NRL1NURK4DMV93pqWWZh1aVI/4J+VsrF6MTtRryb1qSdJferzOSFZ P5zlpnhgyKx7Dwwaqu+paZht8nsaPe1+EghNsCHqr5idwWXKxkkDCvjoABdNNAf8gK0v0ejxNPpa Aj6vN9boiTV6A7GGmkCTGvf/L8Py/0W7Fz5NvdBMXaLYP3n4OPX6nS98ZH6OunTzz9fMP0DJt8jy XIH6/t3ywpp5+VbqfED+b3QVez/GIlWEjpeo92U/B+gi+cixOuVPq3YEwohFJUstvMo9LIutIykp YRO/RI8rh0/1Zk2JEeKOTZnlmtoSyY/gFCxZmTBGQ0uiscXfkIj5GloamxO+hkSAb/T5o80eP+dN tPA13tlAjb/R3zJLZ5t8jc2BdikjxPhpHiZ6shURYDoJ69As7fN4mus8gTq/h/Z6Whu8rZ4ADX97 PGvImSiShwTfjIiiFNGFhQmcSkZN84LyU3NCmtyjt3cN0A832FBNg9Cap9XbqDbYEY/jy0NRBi3U iHEE8QWHvIROruI2mv0VOtVQ5/HS3qZWn7/V49M6JaYw2mIee5L4LA61RHc3xvhi2B866oMTbKGd QTTItNft09qt0Dddu74mtd2gsF+9XihJPIrkBBPJA5kWQI4I3INq4CdpKNBSRUM+X2ugpdWvNSRN CzkYJO08Fr5hPjbJE1cWl6aV2PQO5bSfFrWuttrgraZVPzSp6x4aOk7NF4VGSYPmqw5aoKE10KxC IyG4+rFu8FcE46MRMzW1BjR+AqmKs+IkZ8hdP0RmE8A44hHTRqVUsa3KfKW21aARIIGTaaHLg2cQ nVPcXhILjxoDc1hI3q2Ab/FWZh8f7W1EU8OjgQfNjUMrh/7W0Hie3DGILzHiNfCN1YH3NbR6NIKD WqhE7pO7S/HhORhKoNw+NdW3EsgqpK3PiqntByqzD24f2MfvL3YPH2HAoxTno/mJCfUycA1sZT7y IT5qaNAJFDIKaAQmRHReg1cDwKERPoMlSgxH2eczINFmkkprsHBXbq1BmeLFTqApztEJfpqW0I3j MFATEqGkQZBJWiOVeRo34tdPDdJISiAZopA1gcUIOpOmChYNfmU+VuE3GDiBpLLChzcVSJVZlkBq bg00FYmfzuOEVkgLxEdEUcp80NJEElFPQLdURrKR9vpbvb5Wj0ZpMP1hRdSl5CI3wMb5bHJGS4pF x7kch8cYJ4kFqpG5iBd8rfnKPYPmA60+b6u3wcBWpsWJyKyEkJXUtCwxkOkP8mll3eJgzgLrZWfQ ta2T4jTCOs6jm7ERgwjofgeMkddXef4ARsASTa1er4oRuUITDVw+zeP88mi+YsmPOATjNpHlYjye 02pTfk81TcFUDbQ2FIUqWqSJNJ3i4mJMA1YVJZtbfc0gOA3A8tqCP6AelyoGUiE6jRB7T03DxGFB G0UnqC2G1OuvLACb0JACR/maS3pFFgcEUbEy1cB/BR9lhuBpjic41hmw9FcRaK48rzECAVi5fSYJ yKmzBN9KF5vk0hM8HQKOUyUhgK88Ywh4kPEag6C+Ia4L4Qu7gTnyGf2cjOOrYUVFtCNNU2ut8rDi 1ho8sITrhRSmJhEjqCshWgJ9chq3rMD2+Zsqwm7Gc79Ft1TxytzHI4B9zHhNMvEIUnWSSeiwlI/i 6ciTk0BJtDxnhdheVQr7GqpCorE14NdNAiIliT6qE0PF45VTXFZAR6MlchwJTXIUY4YEK1KVMUoa Bs3VYNCElraiHsthhUtj/2LSQJy+RRFBVvg48ZEWDSGU6CwrorIkJ7XkUtFqqSwdWhBhYIAaND7T 8mDT0kwaGEx/lY2kHPzjbTADlYY3pbtco8zHnCgqaPmr0ARbkFLsadFJbPXSaiU9bzFWEaEU5dE1 3HF0g7OktVJ5kuFWfAFoyDSHMYealiGgNcmfrWQXytaiGTEp5pNxfFs3IAFLRR5XQ0fh1WI4OIRD 12MpmAUCFTHz+rFlBZOmsWRC4ttDQHbG05hl0RFbC/MV2qrx+mc1MiVhidu0yTdb4/PNRrb1traS KMPWVkAVWaxc8t7G2ZrAbIbLTdb4Z7P8FPyRzwtxVEFxldShvJ51mrWIHSTQGTBSZ71cPBaIJqJ1 vpjfBxh7PXXN3mhLXTThSXhg5WxoicY3uXyzdo0AqvW5bD69V2kK3vr9zQgyHw00BALxxrrmlkCU QG7xe1vquJZGLxhyTZyvqaWmsaEqDFqtO9KKu/H/AQdjyG8= ==== END SVK PATCH BLOCK ==== --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email |
|
|
Re: Manipulable ASTAll~
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 ASTAll~
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 |