Skip to content

Commit

Permalink
beginWhile support multiple expressions
Browse files Browse the repository at this point in the history
`beginWhile` now takes multiple expression which are assumed to be anded together.
Also add support for `and` as a stand-alone expression.
  • Loading branch information
pontusmelke committed Jun 29, 2016
1 parent ce48cad commit 505f9ef
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 9 deletions.
Expand Up @@ -154,9 +154,9 @@ public CodeBlock forEach( Parameter local, Expression iterable )
return block; return block;
} }


public CodeBlock whileLoop( Expression test ) public CodeBlock whileLoop( Expression...tests )
{ {
emitter.beginWhile( test ); emitter.beginWhile( tests );
return new CodeBlock( this ); return new CodeBlock( this );
} }


Expand Down
12 changes: 12 additions & 0 deletions community/codegen/src/main/java/org/neo4j/codegen/Expression.java
Expand Up @@ -46,6 +46,18 @@ public void accept( ExpressionVisitor visitor )
}; };
} }


public static Expression and( final Expression lhs, final Expression rhs )
{
return new Expression()
{
@Override
public void accept( ExpressionVisitor visitor )
{
visitor.and( lhs, rhs );
}
};
}

public static Expression or( final Expression lhs, final Expression rhs ) public static Expression or( final Expression lhs, final Expression rhs )
{ {
return new Expression() return new Expression()
Expand Down
Expand Up @@ -166,6 +166,17 @@ public void or( Expression lhs, Expression rhs )
rhs.accept( this ); rhs.accept( this );
result.append( ")" ); result.append( ")" );
} }

@Override
public void and( Expression lhs, Expression rhs )
{
result.append( "and(" );
lhs.accept( this );
result.append( ", " );
rhs.accept( this );
result.append( ")" );
}

@Override @Override
public void addInts( Expression lhs, Expression rhs ) public void addInts( Expression lhs, Expression rhs )
{ {
Expand Down
Expand Up @@ -49,6 +49,8 @@ public interface ExpressionVisitor


void or( Expression lhs, Expression rhs ); void or( Expression lhs, Expression rhs );


void and( Expression lhs, Expression rhs );

void addInts( Expression lhs, Expression rhs ); void addInts( Expression lhs, Expression rhs );


void addLongs( Expression lhs, Expression rhs ); void addLongs( Expression lhs, Expression rhs );
Expand Down
Expand Up @@ -89,7 +89,7 @@ public void assign( LocalVariable variable, Expression value )
} }


@Override @Override
public void beginWhile( Expression test ) public void beginWhile( Expression...tests )
{ {
throw new IllegalStateException( reason ); throw new IllegalStateException( reason );
} }
Expand Down
Expand Up @@ -35,7 +35,14 @@ public interface MethodEmitter


void assign( LocalVariable local, Expression value ); void assign( LocalVariable local, Expression value );


void beginWhile( Expression test ); /**
* Begin a while block,
* <code>
* while (tests[0] && tests[1]...)
* ...
* </code>
*/
void beginWhile( Expression...tests );


void beginIf( Expression test ); void beginIf( Expression test );


Expand Down
Expand Up @@ -333,6 +333,36 @@ public void or( Expression lhs, Expression rhs )


} }


@Override
public void and( Expression lhs, Expression rhs )
{
/*
* something like:
*
* LOAD lhs
* IF FALSE GOTO 0
* LOAD rhs
* IF FALSE GOTO 0
* LOAD TRUE
* GOTO 1
* 0:
* LOAD FALSE
* 1:
* ...continue doing stuff
*/
lhs.accept( this );
Label l0 = new Label();
methodVisitor.visitJumpInsn( IFEQ, l0 );
rhs.accept( this );
methodVisitor.visitJumpInsn( IFEQ, l0 );
methodVisitor.visitInsn( ICONST_1 );
Label l1 = new Label();
methodVisitor.visitJumpInsn( GOTO, l1 );
methodVisitor.visitLabel( l0 );
methodVisitor.visitInsn( ICONST_0 );
methodVisitor.visitLabel( l1 );
}

@Override @Override
public void addInts( Expression lhs, Expression rhs ) public void addInts( Expression lhs, Expression rhs )
{ {
Expand Down
Expand Up @@ -171,13 +171,16 @@ public void assign( LocalVariable variable, Expression value )
} }


@Override @Override
public void beginWhile( Expression test ) public void beginWhile( Expression...tests )
{ {
Label l0 = new Label(); Label l0 = new Label();
methodVisitor.visitLabel( l0 ); methodVisitor.visitLabel( l0 );
test.accept( expressionVisitor );
Label l1 = new Label(); Label l1 = new Label();
methodVisitor.visitJumpInsn( IFEQ, l1 ); for ( Expression test : tests )
{
test.accept( expressionVisitor );
methodVisitor.visitJumpInsn( IFEQ, l1 );
}


stateStack.push( new While( methodVisitor, l0, l1 ) ); stateStack.push( new While( methodVisitor, l0, l1 ) );
} }
Expand Down
Expand Up @@ -133,10 +133,16 @@ public void assign( LocalVariable variable, Expression value )
} }


@Override @Override
public void beginWhile( Expression test ) public void beginWhile( Expression...tests )
{ {
indent().append( "while( " ); indent().append( "while( " );
test.accept( this ); String sep = "";
for (Expression test: tests)
{
append( sep );
test.accept( this );
sep = " && ";
}
append( " )\n" ); append( " )\n" );
indent().append( "{\n" ); indent().append( "{\n" );
level.push( LEVEL ); level.push( LEVEL );
Expand Down Expand Up @@ -357,6 +363,15 @@ public void or( Expression lhs, Expression rhs )
rhs.accept( this ); rhs.accept( this );
} }


@Override
public void and( Expression lhs, Expression rhs )
{
lhs.accept( this );
append( " && " );
rhs.accept( this );

}

@Override @Override
public void addInts( Expression lhs, Expression rhs ) public void addInts( Expression lhs, Expression rhs )
{ {
Expand Down
Expand Up @@ -59,6 +59,7 @@
import static org.neo4j.codegen.Expression.addDoubles; import static org.neo4j.codegen.Expression.addDoubles;
import static org.neo4j.codegen.Expression.addInts; import static org.neo4j.codegen.Expression.addInts;
import static org.neo4j.codegen.Expression.addLongs; import static org.neo4j.codegen.Expression.addLongs;
import static org.neo4j.codegen.Expression.and;
import static org.neo4j.codegen.Expression.constant; import static org.neo4j.codegen.Expression.constant;
import static org.neo4j.codegen.Expression.invoke; import static org.neo4j.codegen.Expression.invoke;
import static org.neo4j.codegen.Expression.newArray; import static org.neo4j.codegen.Expression.newArray;
Expand Down Expand Up @@ -482,6 +483,49 @@ public void shouldGenerateWhileLoop() throws Throwable
verifyNoMoreInteractions( a, b, c ); verifyNoMoreInteractions( a, b, c );
} }



@Test
public void shouldGenerateWhileLoopWithMultipleTestExpressions() throws Throwable
{
// given
ClassHandle handle;
try ( ClassGenerator simple = generateClass( "SimpleClass" ) )
{
try ( CodeBlock callEach = simple.generateMethod( void.class, "check",
param( boolean.class, "a"), param( boolean.class, "b"), param(Runnable.class, "runner") ) )
{
try ( CodeBlock loop = callEach.whileLoop( callEach.load("a"), callEach.load("b") ))
{
loop.expression( invoke(
loop.load("runner"),
methodReference( Runnable.class, void.class, "run" ) ) );
loop.returns();
}
}

handle = simple.handle();
}
Runnable a = mock( Runnable.class );
Runnable b = mock( Runnable.class );
Runnable c = mock( Runnable.class );
Runnable d = mock( Runnable.class );

// when
MethodHandle callEach = instanceMethod( handle.newInstance(), "check", boolean.class, boolean.class, Runnable.class );
callEach.invoke( true, true, a);
callEach.invoke( true, false, b);
callEach.invoke( false, true, c);
callEach.invoke( false, false, d);

// then
verify( a ).run();
verifyNoMoreInteractions( a );
verifyZeroInteractions( b );
verifyZeroInteractions( c );
verifyZeroInteractions( d );

}

@Test @Test
public void shouldGenerateNestedWhileLoop() throws Throwable public void shouldGenerateNestedWhileLoop() throws Throwable
{ {
Expand Down Expand Up @@ -902,6 +946,76 @@ public void shouldGenerateMethodUsingOr() throws Throwable
assertThat( conditional.invoke( false, false ), equalTo( false ) ); assertThat( conditional.invoke( false, false ), equalTo( false ) );
} }



@Test
public void shouldGenerateAnd() throws Throwable
{
// given
ClassHandle handle;
try ( ClassGenerator simple = generateClass( "SimpleClass" ) )
{
try ( CodeBlock conditional = simple.generateMethod( void.class, "conditional",
param( boolean.class, "test1" ), param( boolean.class, "test2" ),
param( Runnable.class, "runner" ) ) )
{
try ( CodeBlock doStuff = conditional.ifStatement( and( conditional.load( "test1" ),
conditional.load( "test2" ) ) ) )
{
doStuff.expression(
invoke( doStuff.load( "runner" ), RUN ) );
}
}

handle = simple.handle();
}

Runnable runner1 = mock( Runnable.class );
Runnable runner2 = mock( Runnable.class );
Runnable runner3 = mock( Runnable.class );
Runnable runner4 = mock( Runnable.class );

// when
MethodHandle conditional =
instanceMethod( handle.newInstance(), "conditional", boolean.class, boolean.class, Runnable.class );
conditional.invoke( true, true, runner1 );
conditional.invoke( true, false, runner2 );
conditional.invoke( false, true, runner3 );
conditional.invoke( false, false, runner4 );

// then
verify( runner1 ).run();
verifyZeroInteractions( runner2 );
verifyZeroInteractions( runner3 );
verifyZeroInteractions( runner4 );
}

@Test
public void shouldGenerateMethodUsingAnd() throws Throwable
{
// given
ClassHandle handle;
try ( ClassGenerator simple = generateClass( "SimpleClass" ) )
{
try ( CodeBlock conditional = simple.generateMethod( boolean.class, "conditional",
param( boolean.class, "test1" ), param( boolean.class, "test2" ) ) )
{
conditional.returns( and( conditional.load( "test1" ), conditional.load( "test2" ) ) );
}

handle = simple.handle();
}

// when
MethodHandle conditional =
instanceMethod( handle.newInstance(), "conditional", boolean.class, boolean.class );

// then
assertThat( conditional.invoke( true, true ), equalTo( true ) );
assertThat( conditional.invoke( true, false ), equalTo( false ) );
assertThat( conditional.invoke( false, true ), equalTo( false ) );
assertThat( conditional.invoke( false, false ), equalTo( false ) );
}

@Test @Test
public void shouldHandleNot() throws Throwable public void shouldHandleNot() throws Throwable
{ {
Expand Down

0 comments on commit 505f9ef

Please sign in to comment.