diff --git a/community/codegen/src/main/java/org/neo4j/codegen/CatchClause.java b/community/codegen/src/main/java/org/neo4j/codegen/CatchClause.java
new file mode 100644
index 000000000000..d37fa2d070e0
--- /dev/null
+++ b/community/codegen/src/main/java/org/neo4j/codegen/CatchClause.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.codegen;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+public class CatchClause
+{
+ private final Parameter exception;
+ private final List> actions;
+
+ public CatchClause( Parameter exception, List> actions )
+ {
+ this.exception = exception;
+ this.actions = actions;
+ }
+
+ public Parameter exception()
+ {
+ return exception;
+ }
+
+ public List> actions()
+ {
+ return actions;
+ }
+
+}
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java b/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java
index 17996fa55f77..f6f8226f0849 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java
@@ -19,20 +19,21 @@
*/
package org.neo4j.codegen;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.function.Consumer;
+import static org.neo4j.codegen.LocalVariables.copy;
import static org.neo4j.codegen.Resource.withResource;
import static org.neo4j.codegen.TypeReference.typeReference;
public class CodeBlock implements AutoCloseable
{
+
final ClassGenerator clazz;
private MethodEmitter emitter;
private final CodeBlock parent;
private boolean done;
- private Map localVariables = new HashMap<>( );
- private int varCount = 0;
+
+ protected LocalVariables localVariables = new LocalVariables();
CodeBlock( CodeBlock parent )
{
@@ -40,7 +41,8 @@ public class CodeBlock implements AutoCloseable
this.emitter = parent.emitter;
parent.emitter = InvalidState.IN_SUB_BLOCK;
this.parent = parent;
- this.localVariables = parent.localVariables;
+ //copy over local variables from parent
+ this.localVariables = copy(parent.localVariables);
}
CodeBlock( ClassGenerator clazz, MethodEmitter emitter, Parameter...parameters )
@@ -48,10 +50,10 @@ public class CodeBlock implements AutoCloseable
this.clazz = clazz;
this.emitter = emitter;
this.parent = null;
- localVariables.put("this", localVariable( clazz.handle(), "this" ) );
+ localVariables.createNew( clazz.handle(), "this" );
for ( Parameter parameter : parameters )
{
- localVariables.put( parameter.name(), localVariable( parameter.type(), parameter.name() ) );
+ localVariables.createNew( parameter.type(), parameter.name() );
}
}
@@ -75,7 +77,7 @@ public void close()
this.emitter = InvalidState.BLOCK_CLOSED;
}
- private void endBlock()
+ protected void endBlock()
{
if ( !done )
{
@@ -84,9 +86,14 @@ private void endBlock()
}
}
+ protected void emit( Consumer emitFunction )
+ {
+ emitFunction.accept( emitter );
+ }
+
public void expression( Expression expression )
{
- emitter.expression( expression );
+ emit( ( e ) -> e.expression( expression ) );
}
LocalVariable local( String name )
@@ -96,15 +103,14 @@ LocalVariable local( String name )
public LocalVariable declare( TypeReference type, String name )
{
- LocalVariable local = localVariable( type, name );
- localVariables.put(name, local);
- emitter.declare( local );
+ LocalVariable local = localVariables.createNew( type, name );
+ emit( e -> e.declare( local ) );
return local;
}
public void assign( LocalVariable local, Expression value )
{
- emitter.assignVariableInScope( local, value );
+ emit( e -> e.assignVariableInScope( local, value ) );
}
public void assign( Class> type, String name, Expression value )
@@ -114,14 +120,13 @@ public void assign( Class> type, String name, Expression value )
public void assign( TypeReference type, String name, Expression value )
{
- LocalVariable variable = localVariable( type, name );
- localVariables.put(name, variable );
- emitter.assign( variable, value );
+ LocalVariable variable = localVariables.createNew( type, name );
+ emit( e -> e.assign( variable, value ) );
}
public void put( Expression target, FieldReference field, Expression value )
{
- emitter.put( target, field, value );
+ emit( e -> e.put( target, field, value ) );
}
public Expression self()
@@ -136,65 +141,50 @@ public Expression load( String name )
public CodeBlock forEach( Parameter local, Expression iterable )
{
- emitter.beginForEach( local, iterable );
+ emit( e -> e.beginForEach( local, iterable ) );
return new CodeBlock( this );
}
public CodeBlock whileLoop( Expression test )
{
- emitter.beginWhile( test );
+ emit( e -> e.beginWhile( test ) );
return new CodeBlock( this );
}
public CodeBlock ifStatement( Expression test )
{
- emitter.beginIf( test );
+ emit( e -> e.beginIf( test ) );
return new CodeBlock( this );
}
- CodeBlock emitCatch( Parameter exception )
- {
- endBlock();
- emitter.beginCatch( exception );
- return new CodeBlock( this );
- }
-
- CodeBlock emitFinally()
- {
- endBlock();
- emitter.beginFinally();
- return new CodeBlock( this );
- }
-
- public CodeBlock tryBlock( Class> resourceType, String resourceName, Expression resource )
+ public TryBlock tryBlock( Class> resourceType, String resourceName, Expression resource )
{
return tryBlock( withResource( resourceType, resourceName, resource ) );
}
- public CodeBlock tryBlock( TypeReference resourceType, String resourceName, Expression resource )
+ public TryBlock tryBlock( TypeReference resourceType, String resourceName, Expression resource )
{
return tryBlock( withResource( resourceType, resourceName, resource ) );
}
public TryBlock tryBlock( Resource... resources )
{
- emitter.beginTry( resources );
- return new TryBlock( this );
+ return new TryBlock( this, resources );
}
public void returns()
{
- emitter.returns();
+ emit( MethodEmitter::returns );
}
public void returns( Expression value )
{
- emitter.returns( value );
+ emit( e -> e.returns( value ) );
}
public void throwException( Expression exception )
{
- emitter.throwException( exception );
+ emit( e -> e.throwException( exception ) );
}
public TypeReference owner()
@@ -202,8 +192,4 @@ public TypeReference owner()
return clazz.handle();
}
- private LocalVariable localVariable( TypeReference type, String name )
- {
- return new LocalVariable( type, name, varCount++ );
- }
}
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/Expression.java b/community/codegen/src/main/java/org/neo4j/codegen/Expression.java
index bdea9e3bd0c1..49f377a1abc6 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/Expression.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/Expression.java
@@ -70,7 +70,7 @@ public void accept( ExpressionVisitor visitor )
};
}
- static Expression load( final LocalVariable variable)
+ public static Expression load( final LocalVariable variable)
{
return new Expression()
{
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/InvalidState.java b/community/codegen/src/main/java/org/neo4j/codegen/InvalidState.java
index 69fe7cadf9aa..fb1e4149f258 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/InvalidState.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/InvalidState.java
@@ -19,6 +19,9 @@
*/
package org.neo4j.codegen;
+import java.util.List;
+import java.util.function.Consumer;
+
class InvalidState implements MethodEmitter
{
public static final ClassEmitter CLASS_DONE = new ClassEmitter()
@@ -98,12 +101,6 @@ public void beginIf( Expression test )
throw new IllegalStateException( reason );
}
- @Override
- public void beginFinally()
- {
- throw new IllegalStateException( reason );
- }
-
@Override
public void endBlock()
{
@@ -111,7 +108,8 @@ public void endBlock()
}
@Override
- public void beginTry( Resource... resources )
+ public void tryCatchBlock( List> body, List catchClauses,
+ List> finalClauses, LocalVariables localVariables, Resource... resources )
{
throw new IllegalStateException( reason );
}
@@ -122,12 +120,6 @@ public void throwException( Expression exception )
throw new IllegalStateException( reason );
}
- @Override
- public void beginCatch( Parameter exception )
- {
- throw new IllegalStateException( reason );
- }
-
@Override
public void declare( LocalVariable local )
{
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/LocalVariables.java b/community/codegen/src/main/java/org/neo4j/codegen/LocalVariables.java
new file mode 100644
index 000000000000..5ccffec4d454
--- /dev/null
+++ b/community/codegen/src/main/java/org/neo4j/codegen/LocalVariables.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.codegen;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Repository of local variables.
+ */
+public class LocalVariables
+{
+ private final AtomicInteger counter = new AtomicInteger( 0 );
+ private final Map localVariables = new HashMap<>();
+
+ public LocalVariable createNew( TypeReference type, String name )
+ {
+ LocalVariable localVariable = new LocalVariable( type, name, counter.getAndIncrement() );
+ localVariables.put( name, localVariable );
+ return localVariable;
+ }
+
+ public LocalVariable get( String name )
+ {
+ return localVariables.get( name );
+ }
+
+ public static LocalVariables copy( LocalVariables original )
+ {
+ LocalVariables variables = new LocalVariables();
+ variables.counter.set( original.counter.get() );
+ original.localVariables.forEach( variables.localVariables::put );
+ return variables;
+ }
+
+ public int nextIndex()
+ {
+ return counter.getAndIncrement();
+ }
+}
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/MethodEmitter.java b/community/codegen/src/main/java/org/neo4j/codegen/MethodEmitter.java
index 9e4e6f8d7443..543bae50c8b5 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/MethodEmitter.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/MethodEmitter.java
@@ -19,6 +19,9 @@
*/
package org.neo4j.codegen;
+import java.util.List;
+import java.util.function.Consumer;
+
public interface MethodEmitter
{
void done();
@@ -37,16 +40,15 @@ public interface MethodEmitter
void beginIf( Expression test );
- void beginFinally();
-
void endBlock();
- void beginTry( Resource... resources );
+ void tryCatchBlock( List> body,
+ List catchClauses,
+ List> finalClauses,
+ LocalVariables localVariables, Resource... resources );
void throwException( Expression exception );
- void beginCatch( Parameter exception );
-
void declare( LocalVariable local );
void assignVariableInScope( LocalVariable local, Expression value );
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/MethodReference.java b/community/codegen/src/main/java/org/neo4j/codegen/MethodReference.java
index 86eb65e1b426..592f2315c9d9 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/MethodReference.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/MethodReference.java
@@ -46,6 +46,12 @@ private static MethodReference methodReference( Class> owner, TypeReference re
return methodReference( typeReference( owner ), returns, name, modifiers, parameters );
}
+ public static MethodReference methodReference( TypeReference owner, TypeReference returns, String name,
+ TypeReference... parameters )
+ {
+ return new MethodReference( owner, name, returns, Modifier.PUBLIC, parameters );
+ }
+
public static MethodReference methodReference( TypeReference owner, TypeReference returns, String name,
int modifiers, TypeReference... parameters )
{
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/TryBlock.java b/community/codegen/src/main/java/org/neo4j/codegen/TryBlock.java
index cf1843fb8be2..18ed9b35365a 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/TryBlock.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/TryBlock.java
@@ -19,20 +19,165 @@
*/
package org.neo4j.codegen;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * TryBlock are not really block but tries to look like it from an API standpoint.
+ * Keeps a state-machine to figure out which state we're in (try, catch, finally).
+ * It is not until we call the last "try block" we actually call close on the actual
+ * code block and generates code for the entire try-catch block at once.
+ */
public class TryBlock extends CodeBlock
{
- TryBlock( CodeBlock parent )
+ private final Resource[] resources;
+ private final State tryState = newState( null );
+ private final List catchStates = new LinkedList<>();
+ private State finallyState = null;
+
+ private State currentState = tryState;
+
+ TryBlock( CodeBlock parent, Resource... resources )
{
super( parent );
+ this.resources = resources;
}
public CodeBlock catchBlock(Parameter exception)
{
- return emitCatch(exception);
+ CatchState catchState = new CatchState( exception, currentState );
+ this.currentState = catchState;
+ catchStates.add( catchState );
+ return this;
}
public CodeBlock finallyBlock()
{
- return emitFinally();
+ if ( finallyState != null )
+ {
+ throw new IllegalStateException( "Cannot have more than one finally block" );
+ }
+ finallyState = newState( currentState );
+ currentState = finallyState;
+ return this;
+ }
+
+ @Override
+ protected void emit( Consumer emitFunction )
+ {
+ currentState.addAction( emitFunction );
+ }
+
+ @Override
+ public void close()
+ {
+ State nextState = currentState.nextState();
+ if ( nextState == null )
+ {
+ super.emit( ( e ) -> e.tryCatchBlock( tryActions(), catchClauses(), finallyActions(), localVariables, resources ) );
+ super.close();
+ }
+ currentState = nextState;
+
+ }
+
+ @Override
+ protected void endBlock()
+ {
+ //do nothing
+ }
+
+ private List> tryActions()
+ {
+ return tryState.actions();
+ }
+
+ private List catchClauses()
+ {
+ return catchStates.stream().map( c -> new CatchClause( c.exception, c.actions) )
+ .collect( Collectors.toList() );
+ }
+
+ private List> finallyActions()
+ {
+ return finallyState == null ? Collections.emptyList() : finallyState.actions();
+ }
+
+ /**
+ * A Try block can be in different states depending on if we're in try, catch or finally.
+ * When nextState returns null it means we have no more blocks left and should close
+ * the underlying code block.
+ */
+ private interface State
+ {
+
+ void addAction( Consumer action );
+
+ List> actions();
+
+ State nextState();
+ }
+
+ /**
+ * Catch states are special states that stores what kind of exception we are catching.
+ */
+ private static class CatchState implements State
+ {
+
+ private final List> actions = new LinkedList<>();
+ private final Parameter exception;
+ private final State parent;
+
+ CatchState( Parameter exception, State parent )
+ {
+ this.exception = exception;
+ this.parent = parent;
+ }
+
+ @Override
+ public void addAction( Consumer action )
+ {
+ actions.add( action );
+ }
+
+ @Override
+ public List> actions()
+ {
+ return actions;
+ }
+
+ @Override
+ public State nextState()
+ {
+ return parent;
+ }
+ }
+ private State newState( State parent )
+ {
+ return new State()
+ {
+ private final List> actions = new LinkedList<>();
+
+ @Override
+ public void addAction( Consumer action )
+ {
+ actions.add( action );
+ }
+
+ @Override
+ public List> actions()
+ {
+ return actions;
+ }
+
+ @Override
+ public State nextState()
+ {
+ return parent;
+ }
+ };
}
}
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java
index 0d44158326c0..a429061f2979 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/ByteCodeExpressionVisitor.java
@@ -196,6 +196,7 @@ public void or( Expression lhs, Expression rhs )
methodVisitor.visitInsn( ICONST_1 );
Label l2 = new Label();
methodVisitor.visitJumpInsn( GOTO, l2 );
+ methodVisitor.visitLabel( l1 );
methodVisitor.visitInsn( ICONST_0 );
methodVisitor.visitLabel( l2 );
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/If.java b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/If.java
index 9efa81a7911d..6d1dbf593923 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/If.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/If.java
@@ -38,6 +38,5 @@ public If( MethodVisitor methodVisitor, Label l0)
public void endBlock()
{
methodVisitor.visitLabel( l0 );
- methodVisitor.visitFrame( F_SAME, 0, null, 0, null );
}
}
diff --git a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java
index 041cfc6824b7..5776de79ca65 100644
--- a/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java
+++ b/community/codegen/src/main/java/org/neo4j/codegen/bytecode/MethodByteCodeEmitter.java
@@ -24,13 +24,18 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
+import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Consumer;
import org.neo4j.codegen.BaseExpressionVisitor;
+import org.neo4j.codegen.CatchClause;
import org.neo4j.codegen.Expression;
import org.neo4j.codegen.FieldReference;
import org.neo4j.codegen.LocalVariable;
+import org.neo4j.codegen.LocalVariables;
import org.neo4j.codegen.MethodDeclaration;
import org.neo4j.codegen.MethodEmitter;
import org.neo4j.codegen.MethodReference;
@@ -44,6 +49,7 @@
import static org.neo4j.codegen.ByteCodeUtils.outerName;
import static org.neo4j.codegen.ByteCodeUtils.signature;
import static org.neo4j.codegen.ByteCodeUtils.typeName;
+import static org.neo4j.codegen.MethodReference.methodReference;
class MethodByteCodeEmitter implements MethodEmitter, Opcodes
{
@@ -121,6 +127,7 @@ public void returns( Expression value )
case "byte":
case "short":
case "char":
+ case "boolean":
methodVisitor.visitInsn( IRETURN );
break;
case "long":
@@ -162,7 +169,6 @@ public void assign( LocalVariable variable, Expression value )
default:
methodVisitor.visitVarInsn( ASTORE, variable.index() );
}
-
}
@Override
@@ -190,11 +196,6 @@ public void beginIf( Expression test )
stateStack.push(new If(methodVisitor, l0));
}
- @Override
- public void beginFinally()
- {
- callSuperIfNecessary();
- }
@Override
public void endBlock()
@@ -209,19 +210,153 @@ public void endBlock()
}
@Override
- public void beginTry( Resource... resources )
+ public void tryCatchBlock( List> body, List catchClauses,
+ List> finalClauses, LocalVariables localVariables, Resource... resources )
{
callSuperIfNecessary();
+ //Note we are not giving the same gurantee as built in try-with-resources
+ //we are essentially rewriting to:
+ //Resource resource = ...
+ //try...
+ //finally { ... resource.close}
+ List> updatedFinalClauses = resources.length == 0 ?
+ finalClauses : new ArrayList<>( finalClauses );
+ for ( Resource resource : resources )
+ {
+ LocalVariable variable = localVariables.createNew( resource.type(), resource.name() );
+ assign( variable, resource.producer() );
+
+ MethodReference close = methodReference( resource.type(), TypeReference.VOID, "close" );
+ updatedFinalClauses.add( ( e ) -> e.expression(
+ Expression.invoke( Expression.load( variable ), close ) ) );
+ }
+
+ if ( !catchClauses.isEmpty() )
+ {
+ tryCatchFinally( body, catchClauses, updatedFinalClauses, localVariables );
+ }
+ else
+ {
+ tryFinally( body, updatedFinalClauses, localVariables );
+ }
}
- @Override
- public void throwException( Expression exception )
+ private void tryFinally(List> body,
+ List> finalClauses, LocalVariables localVariables)
{
- callSuperIfNecessary();
+ Label startLabel = new Label();
+ Label endLabel = new Label();
+ Label continueLabel = new Label();
+ //if we have a simple try {} finally {} we only need to run finally after body
+ //and make sure we catch any errors and then run finally and rethrow
+ Label handlerLabel = new Label();
+ methodVisitor.visitTryCatchBlock( startLabel, endLabel, handlerLabel, null );
+ methodVisitor.visitLabel( startLabel );
+ body.forEach( e -> e.accept( this ) );
+ methodVisitor.visitLabel( endLabel );
+ //run finally on success
+ finalClauses.forEach( e -> e.accept( this ) );
+
+ int indexToStoreException = localVariables.nextIndex();
+ //catch error and run finally
+ methodVisitor.visitJumpInsn( GOTO, continueLabel );
+ methodVisitor.visitLabel( handlerLabel );
+ //store error
+ methodVisitor.visitVarInsn( ASTORE, indexToStoreException );
+ finalClauses.forEach( e -> e.accept( this ) );
+ //catch and rethrow
+ methodVisitor.visitVarInsn( ALOAD, indexToStoreException );
+ methodVisitor.visitInsn( ATHROW );
+ methodVisitor.visitLabel( continueLabel );
+ }
+
+ private void tryCatchFinally(List> body, List catchClauses,
+ List> finalClauses, LocalVariables localVariables)
+ {
+ Label startLabel = new Label();
+ Label endLabel = new Label();
+ Label continueLabel = new Label();
+ //if we have catch clauses we need to catch error, run body create labels for each catch clauses
+ //we also need to make sure that the final clauses run both after body and after each catch block
+ //and also make sure if any of the catch clauses fail we still run finally
+
+ //generate try-catch block for each catch clause
+ List