Skip to content

Commit

Permalink
Support for get field and return expression
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusmelke committed May 17, 2016
1 parent f1516ff commit 32b85b2
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 54 deletions.
Expand Up @@ -28,6 +28,10 @@ private ByteCodeUtils()
throw new UnsupportedOperationException();
}

public static String byteCodeName( TypeReference reference )
{
return byteCodeName( reference.name() );
}

public static String byteCodeName( String javaName )
{
Expand Down
Expand Up @@ -51,11 +51,17 @@
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DRETURN;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.FRETURN;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.LRETURN;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
import static org.objectweb.asm.Opcodes.RETURN;
Expand All @@ -68,16 +74,16 @@ class ClassByteCodeWriter implements ClassEmitter
private final Map<FieldReference,Expression> staticFields = new HashMap<>();
private final TypeReference base;

ClassByteCodeWriter(TypeReference type, TypeReference base, TypeReference[] interfaces)
ClassByteCodeWriter( TypeReference type, TypeReference base, TypeReference[] interfaces )
{
this.classWriter = new ClassWriter( ClassWriter.COMPUTE_MAXS );
String[] iNames = new String[interfaces.length];
for ( int i = 0; i < interfaces.length; i++ )
{
iNames[i] = byteCodeName( interfaces[i].name() );
}
classWriter.visit( V1_8, ACC_PUBLIC + ACC_SUPER, byteCodeName( type.name() ), signature( type ),
byteCodeName( base.name() ), iNames.length != 0 ? iNames : null );
classWriter.visit( V1_8, ACC_PUBLIC + ACC_SUPER, byteCodeName( type ), signature( type ),
byteCodeName( base ), iNames.length != 0 ? iNames : null );
this.type = type;
this.base = base;
}
Expand All @@ -97,7 +103,8 @@ public void field( FieldReference field, Expression value )
staticFields.put( field, value );
}
FieldVisitor fieldVisitor = classWriter
.visitField( field.modifiers(), field.name(), typeName( field.type() ), signature( field.type() ), null );
.visitField( field.modifiers(), field.name(), typeName( field.type() ), signature( field.type() ),
null );
fieldVisitor.visitEnd();
}

Expand All @@ -113,8 +120,8 @@ public void done()
{
FieldReference field = entry.getKey();
entry.getValue().accept( expressionVisitor );
methodVisitor.visitFieldInsn(PUTSTATIC, byteCodeName( field.owner().name() ),
field.name(), signature( field.type() ));
methodVisitor.visitFieldInsn( PUTSTATIC, byteCodeName( field.owner() ),
field.name(), signature( field.type() ) );
}
methodVisitor.visitInsn( RETURN );
methodVisitor.visitMaxs( 0, 0 );
Expand Down Expand Up @@ -187,7 +194,8 @@ public void put( Expression target, FieldReference field, Expression value )

target.accept( expressionVisitor );
value.accept( expressionVisitor );
methodVisitor.visitFieldInsn(PUTFIELD, byteCodeName(field.owner().name()) , field.name(), typeName( field.type() ));
methodVisitor
.visitFieldInsn( PUTFIELD, byteCodeName( field.owner() ), field.name(), typeName( field.type() ) );

}

Expand All @@ -203,6 +211,28 @@ public void returns()
public void returns( Expression value )
{
callSuperIfNecessary( );

value.accept( expressionVisitor );
switch ( declaration.returnType().simpleName() )
{
case "int":
case "byte":
case "short":
case "char":
methodVisitor.visitInsn( IRETURN );
break;
case "long":
methodVisitor.visitInsn( LRETURN );
break;
case "float":
methodVisitor.visitInsn( FRETURN );
break;
case "double":
methodVisitor.visitInsn( DRETURN );
break;
default:
methodVisitor.visitInsn( ARETURN );
}
}

@Override
Expand Down Expand Up @@ -309,7 +339,7 @@ public void invoke( Expression target, MethodReference method, Expression[] argu
private void callSuper()
{
methodVisitor.visitVarInsn( ALOAD, 0 );
methodVisitor.visitMethodInsn( INVOKESPECIAL, byteCodeName( base.name() ), "<init>", "()V", false );
methodVisitor.visitMethodInsn( INVOKESPECIAL, byteCodeName( base ), "<init>", "()V", false );
calledSuper = true;
}

Expand Down Expand Up @@ -353,7 +383,8 @@ public void invoke( Expression target, MethodReference method, Expression[] argu
{
argument.accept( this );
}
methodVisitor.visitMethodInsn(INVOKESPECIAL, byteCodeName( method.owner().name() ), method.name(), desc(method), false);
methodVisitor.visitMethodInsn( INVOKESPECIAL, byteCodeName( method.owner() ), method.name(), desc( method ),
false );
}

@Override
Expand Down Expand Up @@ -390,6 +421,9 @@ public void load( LocalVariable variable )
@Override
public void getField( Expression target, FieldReference field )
{
target.accept( this );
methodVisitor
.visitFieldInsn( GETFIELD, byteCodeName( field.owner() ), field.name(), typeName( field.type() ) );

}

Expand Down
Expand Up @@ -217,54 +217,18 @@ public void shouldGenerateField() throws Exception
assertSame( String.class, theField.getType() );
}

@Test
public void shouldGenerateConstructorFromTemplate() throws Throwable
{
// given
ClassHandle handle;
try ( ClassGenerator simple = generateClass( NamedBase.class, "SimpleClass" ) )
{
simple.field( String.class, "foo" );
simple.generate( MethodTemplate.constructor( param( String.class, "name" ), param( String.class, "foo" ) )
.invokeSuper( new ExpressionTemplate[]{load( "name" )}, new TypeReference[]{typeReference(String.class)} )
.put( self(), String.class, "foo", load( "foo" ) )
.build() );
handle = simple.handle();
}

// when
Object instance = constructor( handle.loadClass(), String.class, String.class ).invoke( "Pontus", "Tobias" );

// then
assertEquals( "SimpleClass", instance.getClass().getSimpleName() );
assertThat( instance, instanceOf( NamedBase.class ) );
assertEquals( "Pontus", ((NamedBase) instance).name );
assertEquals( "Tobias", getField( instance, "foo" ) );
}

@Test
public void shouldGenerateMethodReturningFieldValue() throws Throwable
{
// given
ClassHandle handle;
try ( ClassGenerator simple = generateClass( "SimpleClass" ) )
{
FieldReference value = simple.field( String.class, "value" );
try ( CodeBlock ctor = simple.generateConstructor( param( String.class, "value" ) ) )
{
ctor.put( ctor.self(), value, ctor.load( "value" ) );
}
simple.generate( MethodTemplate.method( String.class, "value" )
.returns( ExpressionTemplate.get( self(), String.class, "value" ) )
.build() );
handle = simple.handle();
}

// when
Object instance = constructor( handle.loadClass(), String.class ).invoke( "Hello World" );

// then
assertEquals( "Hello World", instanceMethod( instance, "value" ).invoke() );
assertMethodReturningField( byte.class, (byte) 42 );
assertMethodReturningField( short.class, (short) 42 );
assertMethodReturningField( char.class, (char) 42 );
assertMethodReturningField( int.class, 42 );
assertMethodReturningField( long.class, 42L );
assertMethodReturningField( float.class, 42F );
assertMethodReturningField( double.class, 42D );
assertMethodReturningField( String.class, "42" );
assertMethodReturningField( int[].class, new int[]{42} );
}

@Test
Expand Down Expand Up @@ -676,4 +640,29 @@ ClassGenerator generateClass( TypeReference base, String name, TypeReference...
{
return generator.generateClass( base, PACKAGE, name, interfaces );
}

private <T> void assertMethodReturningField( Class<T> clazz, T argument ) throws Throwable
{
// given
createGenerator();
ClassHandle handle;
try ( ClassGenerator simple = generateClass( "SimpleClass" ) )
{
FieldReference value = simple.field( clazz, "value" );
try ( CodeBlock ctor = simple.generateConstructor( param( clazz, "value" ) ) )
{
ctor.put( ctor.self(), value, ctor.load( "value" ) );
}
simple.generate( MethodTemplate.method( clazz, "value" )
.returns( ExpressionTemplate.get( self(), clazz, "value" ) )
.build() );
handle = simple.handle();
}

// when
Object instance = constructor( handle.loadClass(), clazz ).invoke( argument );

// then
assertEquals( argument, instanceMethod( instance, "value" ).invoke() );
}
}

0 comments on commit 32b85b2

Please sign in to comment.