From edd0768962fcfd21088eadfa8ea651e2ab588d0e Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Mon, 4 Jan 2016 11:45:03 +0100 Subject: [PATCH] Support for calling super, put, load - always generate a default constructor when there are none - call super constructor - support for put field - support for load --- .../java/org/neo4j/codegen/ByteCodeUtils.java | 14 ++++++ .../org/neo4j/codegen/ClassGenerator.java | 11 ++++- .../java/org/neo4j/codegen/CodeBlock.java | 28 +++++++++-- .../java/org/neo4j/codegen/Expression.java | 4 +- .../org/neo4j/codegen/ExpressionTemplate.java | 12 +++-- .../org/neo4j/codegen/ExpressionToString.java | 8 +-- .../org/neo4j/codegen/ExpressionVisitor.java | 2 +- .../java/org/neo4j/codegen/LocalVariable.java | 11 ++++- .../org/neo4j/codegen/MethodReference.java | 20 ++++++-- .../org/neo4j/codegen/MethodTemplate.java | 21 +++++++- .../codegen/source/ClassByteCodeWriter.java | 49 +++++++++++++++---- .../codegen/source/MethodSourceWriter.java | 4 +- .../org/neo4j/codegen/CodeGenerationTest.java | 15 ++---- .../java/org/neo4j/codegen/NamedBase.java | 30 ++++++++++++ 14 files changed, 181 insertions(+), 48 deletions(-) create mode 100644 community/codegen/src/test/java/org/neo4j/codegen/NamedBase.java diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ByteCodeUtils.java b/community/codegen/src/main/java/org/neo4j/codegen/ByteCodeUtils.java index fa1abb39df9f2..e6234dceda733 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ByteCodeUtils.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ByteCodeUtils.java @@ -46,6 +46,20 @@ public static String desc( MethodDeclaration declaration ) return internalDesc( declaration.erased(), false ); } + public static String desc( MethodReference reference) + { + StringBuilder builder = new StringBuilder( ); + builder.append( "(" ); + for ( TypeReference parameter : reference.parameters() ) + { + internalType( builder, parameter, false ); + } + builder.append( ")" ); + internalType( builder, reference.returns(), false ); + + return builder.toString(); + } + public static String signature( TypeReference reference ) { if ( !reference.isGeneric() ) diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ClassGenerator.java b/community/codegen/src/main/java/org/neo4j/codegen/ClassGenerator.java index 56a2f78300b29..3ce3d7bd0fb37 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ClassGenerator.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ClassGenerator.java @@ -36,6 +36,7 @@ public class ClassGenerator implements AutoCloseable private final ClassHandle handle; private ClassEmitter emitter; private Map fields; + private boolean hasConstructor = false; ClassGenerator( ClassHandle handle, ClassEmitter emitter ) { @@ -46,6 +47,10 @@ public class ClassGenerator implements AutoCloseable @Override public void close() { + if (!hasConstructor) + { + generate( MethodTemplate.constructor().invokeSuper().build() ); + } emitter.done(); handle.generator.closeClass(); emitter = InvalidState.CLASS_DONE; @@ -128,7 +133,11 @@ public CodeBlock generate( MethodDeclaration.Builder builder ) private CodeBlock generate( MethodDeclaration declaration ) { - return new CodeBlock( this, emitter.method( declaration ) ); + if (declaration.isConstructor()) + { + hasConstructor = true; + } + return new CodeBlock( this, emitter.method( declaration ), declaration.parameters() ); } FieldReference getField( String name ) 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 be1ad22c1d8f6..d25b7619df817 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/CodeBlock.java @@ -19,6 +19,9 @@ */ package org.neo4j.codegen; +import java.util.HashMap; +import java.util.Map; + import static org.neo4j.codegen.Resource.withResource; import static org.neo4j.codegen.TypeReference.typeReference; @@ -28,6 +31,8 @@ public class CodeBlock implements AutoCloseable private MethodEmitter emitter; private final CodeBlock parent; private boolean done; + private Map localVariables = new HashMap<>( ); + private int varCount = 0; CodeBlock( CodeBlock parent ) { @@ -35,13 +40,19 @@ public class CodeBlock implements AutoCloseable this.emitter = parent.emitter; parent.emitter = InvalidState.IN_SUB_BLOCK; this.parent = parent; + this.localVariables = parent.localVariables; } - CodeBlock( ClassGenerator clazz, MethodEmitter emitter ) + CodeBlock( ClassGenerator clazz, MethodEmitter emitter, Parameter...parameters ) { this.clazz = clazz; this.emitter = emitter; this.parent = null; + localVariables.put("this", localVariable( clazz.handle(), "this" ) ); + for ( Parameter parameter : parameters ) + { + localVariables.put( parameter.name(), localVariable( parameter.type(), parameter.name() ) ); + } } public ClassGenerator classGenerator() @@ -78,14 +89,15 @@ public void expression( Expression expression ) emitter.expression( expression ); } - TypeReference local( String name ) + LocalVariable local( String name ) { - return null; + return localVariables.get( name); } public LocalVariable declare( TypeReference type, String name ) { - LocalVariable local = new LocalVariable( type, name ); + LocalVariable local = localVariable( type, name ); + localVariables.put(name, local); emitter.declare( local ); return local; } @@ -102,6 +114,7 @@ public void assign( Class type, String name, Expression value ) public void assign( TypeReference type, String name, Expression value ) { + localVariables.put(name, localVariable( type, name ) ); emitter.assign( type, name, value ); } @@ -117,7 +130,7 @@ public Expression self() public Expression load( String name ) { - return Expression.load( local( name ), name ); + return Expression.load( local( name ) ); } public CodeBlock forEach( Parameter local, Expression iterable ) @@ -187,4 +200,9 @@ 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 ef5ba5c43a922..a4b2eb6c1431f 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/Expression.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/Expression.java @@ -70,14 +70,14 @@ public void accept( ExpressionVisitor visitor ) }; } - static Expression load( final TypeReference type, final String name ) + static Expression load( final LocalVariable variable) { return new Expression() { @Override public void accept( ExpressionVisitor visitor ) { - visitor.load( type, name ); + visitor.load( variable ); } }; } diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java index 750888cfaea82..590bc5b421e61 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionTemplate.java @@ -19,6 +19,7 @@ */ package org.neo4j.codegen; +import static org.neo4j.codegen.TypeReference.VOID; import static org.neo4j.codegen.TypeReference.typeReference; public abstract class ExpressionTemplate @@ -30,7 +31,7 @@ public static ExpressionTemplate self() @Override void templateAccept( CodeBlock method, ExpressionVisitor visitor ) { - visitor.load( method.clazz.handle(), "this" ); + visitor.load( new LocalVariable( method.clazz.handle(), "this", 0) ); } }; } @@ -85,7 +86,7 @@ public static ExpressionTemplate load( final String name ) @Override protected void templateAccept( CodeBlock method, ExpressionVisitor visitor ) { - visitor.load( method.local( name ), name ); + visitor.load( method.local( name ) ); } }; } @@ -219,16 +220,17 @@ static Expression[] materialize( CodeBlock method, ExpressionTemplate[] template return expressions; } - static ExpressionTemplate invokeSuperConstructor( final ExpressionTemplate[] parameters ) + //TODO I am not crazy about the way type parameters are sent here + static ExpressionTemplate invokeSuperConstructor( final ExpressionTemplate[] parameters, final TypeReference[] parameterTypes ) { - // TODO: we need to get the parameter types in here eventually... + assert parameters.length == parameterTypes.length; return new ExpressionTemplate() { @Override void templateAccept( CodeBlock method, ExpressionVisitor visitor ) { visitor.invoke( Expression.SUPER, - new MethodReference( method.clazz.handle().parent(), "" ), + new MethodReference( method.clazz.handle().parent(), "", VOID, parameterTypes), materialize( method, parameters ) ); } }; diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java index c6e11a48de639..fba80bd12eb67 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionToString.java @@ -63,18 +63,18 @@ public void invoke( MethodReference method, Expression[] arguments ) } @Override - public void load( TypeReference type, String name ) + public void load( LocalVariable variable) { result.append( "load{type=" ); - if ( type == null ) + if ( variable.type() == null ) { result.append( "null" ); } else { - type.writeTo( result ); + variable.type().writeTo( result ); } - result.append( ", name=" ).append( name ).append( "}" ); + result.append( ", name=" ).append( variable.name() ).append( "}" ); } @Override diff --git a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java index dd5c088e244e0..8e6a8fa3d96d7 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/ExpressionVisitor.java @@ -25,7 +25,7 @@ public interface ExpressionVisitor void invoke( MethodReference method, Expression[] arguments ); - void load( TypeReference type, String name ); + void load( LocalVariable variable); void getField( Expression target, FieldReference field ); diff --git a/community/codegen/src/main/java/org/neo4j/codegen/LocalVariable.java b/community/codegen/src/main/java/org/neo4j/codegen/LocalVariable.java index 5f72a69980038..eb9e649980706 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/LocalVariable.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/LocalVariable.java @@ -23,11 +23,13 @@ public class LocalVariable extends Expression { private final TypeReference type; private final String name; + private final int index; - LocalVariable( TypeReference type, String name ) + LocalVariable( TypeReference type, String name, int index ) { this.type = type; this.name = name; + this.index = index; } public TypeReference type() @@ -40,9 +42,14 @@ public String name() return name; } + public int index() + { + return index; + } + @Override public void accept( ExpressionVisitor visitor ) { - visitor.load( type, name ); + visitor.load( this ); } } 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 105d53c1e8e08..3eeb7ff16084e 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/MethodReference.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/MethodReference.java @@ -45,7 +45,7 @@ public static MethodReference methodReference( Class owner, TypeReference ret public static MethodReference methodReference( TypeReference owner, TypeReference returns, String name, TypeReference... parameters ) { - return new MethodReference( owner, name ); + return new MethodReference( owner, name, returns, parameters ); } public static MethodReference constructorReference( Class owner, Class firstParameter, Class... parameters ) @@ -60,17 +60,21 @@ public static MethodReference constructorReference( Class owner, TypeReferenc public static MethodReference constructorReference( TypeReference owner, TypeReference... parameters ) { - return new MethodReference( owner, "" ); + return new MethodReference( owner, "", TypeReference.VOID, parameters ); } private final TypeReference owner; private final String name; + private final TypeReference returns; + private final TypeReference[] parameters; - MethodReference( TypeReference owner, String name ) + MethodReference( TypeReference owner, String name, TypeReference returns, TypeReference[] parameters ) { this.owner = owner; this.name = name; + this.returns = returns; + this.parameters = parameters; } public String name() @@ -83,6 +87,16 @@ public TypeReference owner() return owner; } + public TypeReference returns() + { + return returns; + } + + public TypeReference[] parameters() + { + return parameters; + } + public boolean isConstructor() { return "".equals( name ); diff --git a/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java b/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java index a490b4150f865..8221f9267c7a0 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/MethodTemplate.java @@ -80,9 +80,26 @@ public static class ConstructorBuilder extends Builder super( parameters ); } - public Builder invokeSuper( ExpressionTemplate... parameters ) + public Builder invokeSuper() { - return expression( ExpressionTemplate.invokeSuperConstructor( parameters ) ); + return expression( + ExpressionTemplate.invokeSuperConstructor( new ExpressionTemplate[]{}, TypeReference.NO_TYPES ) ); + } + + public Builder invokeSuper( ExpressionTemplate[] parameters, Class[] parameterTypes) + { + TypeReference[] references = new TypeReference[parameterTypes.length]; + for ( int i = 0; i < parameterTypes.length; i++ ) + { + references[i] = typeReference( parameterTypes[i] ); + } + + return invokeSuper( parameters, references ); + } + + public Builder invokeSuper( ExpressionTemplate[] parameters, TypeReference[] parameterTypes) + { + return expression( ExpressionTemplate.invokeSuperConstructor( parameters, parameterTypes ) ); } @Override diff --git a/community/codegen/src/main/java/org/neo4j/codegen/source/ClassByteCodeWriter.java b/community/codegen/src/main/java/org/neo4j/codegen/source/ClassByteCodeWriter.java index e974681926156..278aa23bbaa37 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/source/ClassByteCodeWriter.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/source/ClassByteCodeWriter.java @@ -50,7 +50,12 @@ 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.ILOAD; +import static org.objectweb.asm.Opcodes.DLOAD; +import static org.objectweb.asm.Opcodes.FLOAD; +import static org.objectweb.asm.Opcodes.LLOAD; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.PUTFIELD; import static org.objectweb.asm.Opcodes.PUTSTATIC; import static org.objectweb.asm.Opcodes.RETURN; import static org.objectweb.asm.Opcodes.V1_8; @@ -70,7 +75,7 @@ class ClassByteCodeWriter implements ClassEmitter iNames[i] = byteCodeName( interfaces[i].name() ); } classWriter.visit( V1_8, ACC_PUBLIC + ACC_SUPER, byteCodeName( type.name() ), signature( type ), - byteCodeName( base.name() ), iNames ); + byteCodeName( base.name() ), iNames.length != 0 ? iNames : null ); this.type = type; } @@ -145,12 +150,6 @@ public MethodByteCodeEmitter( ClassWriter classWriter, MethodDeclaration declara this.methodVisitor = classWriter.visitMethod( ACC_PUBLIC, declaration.name(), desc( declaration ), signature( declaration ), exceptions( declaration ) ); this.methodVisitor.visitCode(); - //if constructor must call default constructor of Object - if ( declaration.isConstructor() ) - { - this.methodVisitor.visitVarInsn( ALOAD, 0 ); - this.methodVisitor.visitMethodInsn( INVOKESPECIAL, "java/lang/Object", "", "()V", false ); - } this.expressionVisitor = new ByteCodeExpressionVisitor( this.methodVisitor ); } @@ -175,6 +174,10 @@ public void expression( Expression expression ) @Override 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() )); + } @Override @@ -268,6 +271,13 @@ private ByteCodeExpressionVisitor( MethodVisitor methodVisitor ) @Override public void invoke( Expression target, MethodReference method, Expression[] arguments ) { + target.accept( this ); + for ( Expression argument : arguments ) + { + argument.accept( this ); + } + methodVisitor.visitMethodInsn(INVOKESPECIAL, byteCodeName( method.owner().name() ), method.name(), desc(method), false); + System.out.println("methodVisitor.visitMethodInsn(INVOKESPECIAL, byteCodeName( method.owner().name() ), method.name(), desc(method), false);"); } @@ -278,9 +288,29 @@ public void invoke( MethodReference method, Expression[] arguments ) } @Override - public void load( TypeReference type, String name ) + public void load( LocalVariable variable ) { + switch ( variable.type().simpleName() ) + { + case "int": + case "byte": + case "short": + case "char": + methodVisitor.visitVarInsn( ILOAD, variable.index() ); + break; + case "long": + methodVisitor.visitVarInsn( LLOAD, variable.index() ); + break; + case "float": + methodVisitor.visitVarInsn( FLOAD, variable.index() ); + break; + case "double": + methodVisitor.visitVarInsn( DLOAD, variable.index() ); + break; + default: + methodVisitor.visitVarInsn( ALOAD, variable.index() ); + } } @Override @@ -304,7 +334,8 @@ public void getStatic( FieldReference field ) @Override public void loadThis( String sourceName ) { - + methodVisitor.visitVarInsn( ALOAD, 0 ); + System.out.println("methodVisitor.visitVarInsn( ALOAD, 0 );"); } @Override diff --git a/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java b/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java index 2691b2df6e8d7..65530231d0bd9 100644 --- a/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java +++ b/community/codegen/src/main/java/org/neo4j/codegen/source/MethodSourceWriter.java @@ -275,9 +275,9 @@ private void arglist( Expression[] arguments ) } @Override - public void load( TypeReference type, String name ) + public void load( LocalVariable variable) { - append( name ); + append( variable.name() ); } @Override diff --git a/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java b/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java index 05d5e3f760fd4..371d22f1830b3 100644 --- a/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java +++ b/community/codegen/src/test/java/org/neo4j/codegen/CodeGenerationTest.java @@ -60,6 +60,7 @@ import static org.neo4j.codegen.Parameter.param; import static org.neo4j.codegen.TypeReference.extending; import static org.neo4j.codegen.TypeReference.typeParameter; +import static org.neo4j.codegen.TypeReference.typeReference; @RunWith(Parameterized.class) public class CodeGenerationTest @@ -152,16 +153,6 @@ public void shouldGenerateField() throws Exception assertSame( String.class, theField.getType() ); } - public static class NamedBase - { - private final String name; - - public NamedBase( String name ) - { - this.name = name; - } - } - @Test public void shouldGenerateConstructorFromTemplate() throws Throwable { @@ -171,7 +162,7 @@ public void shouldGenerateConstructorFromTemplate() throws Throwable { simple.field( String.class, "foo" ); simple.generate( MethodTemplate.constructor( param( String.class, "name" ), param( String.class, "foo" ) ) - .invokeSuper( load( "name" ) ) + .invokeSuper( new ExpressionTemplate[]{load( "name" )}, new TypeReference[]{typeReference(String.class)} ) .put( self(), String.class, "foo", load( "foo" ) ) .build() ); handle = simple.handle(); @@ -570,7 +561,7 @@ public void shouldBeAbleToCast() throws Throwable { simple.field( String.class, "foo" ); simple.generate( MethodTemplate.constructor( param( String.class, "name" ), param( Object.class, "foo" ) ) - .invokeSuper( load( "name" ) ) + .invokeSuper( new ExpressionTemplate[]{load( "name" )}, new TypeReference[]{typeReference(String.class)} ) .put( self(), String.class, "foo", cast(String.class, load( "foo" )) ) .build() ); handle = simple.handle(); diff --git a/community/codegen/src/test/java/org/neo4j/codegen/NamedBase.java b/community/codegen/src/test/java/org/neo4j/codegen/NamedBase.java new file mode 100644 index 0000000000000..6f9e70129fec5 --- /dev/null +++ b/community/codegen/src/test/java/org/neo4j/codegen/NamedBase.java @@ -0,0 +1,30 @@ +/* + * 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; + +public class NamedBase +{ + final String name; + + public NamedBase( String name ) + { + this.name = name; + } +}