From 8787f16ad1f9e1317b217df506336831c26bbabc Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Thu, 5 Jan 2023 17:46:43 +0100 Subject: [PATCH 1/5] Introduce SignatureBuilder API --- .../gizmo/ClassSignatureBuilderImpl.java | 93 ++++ .../gizmo/FieldSignatureBuilderImpl.java | 22 + .../gizmo/MethodSignatureBuilderImpl.java | 80 +++ .../io/quarkus/gizmo/SignatureBuilder.java | 62 +++ .../io/quarkus/gizmo/SignatureElement.java | 9 +- src/main/java/io/quarkus/gizmo/Type.java | 464 ++++++++++++++++++ .../java/io/quarkus/gizmo/SignaturesTest.java | 230 +++++++++ 7 files changed, 959 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java create mode 100644 src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java create mode 100644 src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java create mode 100644 src/main/java/io/quarkus/gizmo/SignatureBuilder.java create mode 100644 src/main/java/io/quarkus/gizmo/Type.java create mode 100644 src/test/java/io/quarkus/gizmo/SignaturesTest.java diff --git a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java new file mode 100644 index 0000000..3b2286e --- /dev/null +++ b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java @@ -0,0 +1,93 @@ +package io.quarkus.gizmo; + +import java.util.ArrayList; +import java.util.List; + +import org.jboss.jandex.DotName; + +import io.quarkus.gizmo.SignatureBuilder.ClassSignatureBuilder; +import io.quarkus.gizmo.Type.ClassType; +import io.quarkus.gizmo.Type.ParameterizedType; +import io.quarkus.gizmo.Type.TypeVariable; + +class ClassSignatureBuilderImpl implements ClassSignatureBuilder { + + private Type superClass = Type.classType(DotName.OBJECT_NAME); + private List typeParameters = new ArrayList<>(); + private List superInterfaces = new ArrayList<>(); + + @Override + public String build() { + StringBuilder signature = new StringBuilder(); + + // type params + if (!typeParameters.isEmpty()) { + signature.append('<'); + for (TypeVariable typeParameter : typeParameters) { + typeParameter.appendTypeParameterToSignature(signature); + } + signature.append('>'); + } + + // superclass + signature.append(superClass.toSignature()); + + // interfaces + if (!superInterfaces.isEmpty()) { + for (Type superInterface : superInterfaces) { + signature.append(superInterface.toSignature()); + } + } + return signature.toString(); + } + + @Override + public ClassSignatureBuilder addTypeParameter(TypeVariable typeParameter) { + typeParameters.add(typeParameter); + return this; + } + + @Override + public ClassSignatureBuilder setSuperClass(ClassType superClass) { + this.superClass = superClass; + return this; + } + + @Override + public ClassSignatureBuilder setSuperClass(ParameterizedType superClass) { + if (containsWildcard(superClass)) { + throw new IllegalArgumentException("A super type may not specify a wilcard"); + } + this.superClass = superClass; + return this; + } + + @Override + public ClassSignatureBuilder addSuperInterface(ClassType interfaceType) { + superInterfaces.add(interfaceType); + return this; + } + + @Override + public ClassSignatureBuilder addSuperInterface(ParameterizedType interfaceType) { + if (containsWildcard(interfaceType)) { + throw new IllegalArgumentException("A super type may not specify a wilcard"); + } + superInterfaces.add(interfaceType); + return this; + } + + private boolean containsWildcard(Type type) { + if (type.isWildcard()) { + return true; + } else if (type.isParameterizedType()) { + for (Type typeArgument : type.asParameterizedType().typeArguments) { + if (containsWildcard(typeArgument)) { + return true; + } + } + } + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java new file mode 100644 index 0000000..d26342e --- /dev/null +++ b/src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java @@ -0,0 +1,22 @@ +package io.quarkus.gizmo; + +import java.util.Objects; + +import io.quarkus.gizmo.SignatureBuilder.FieldSignatureBuilder; + +class FieldSignatureBuilderImpl implements FieldSignatureBuilder { + + private Type type; + + @Override + public String build() { + return type.toSignature(); + } + + @Override + public FieldSignatureBuilder setType(Type type) { + this.type = Objects.requireNonNull(type); + return this; + } + +} \ No newline at end of file diff --git a/src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java new file mode 100644 index 0000000..b184928 --- /dev/null +++ b/src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java @@ -0,0 +1,80 @@ +package io.quarkus.gizmo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import io.quarkus.gizmo.SignatureBuilder.MethodSignatureBuilder; +import io.quarkus.gizmo.Type.ClassType; +import io.quarkus.gizmo.Type.TypeVariable; + +class MethodSignatureBuilderImpl implements MethodSignatureBuilder { + + private Type returnType = Type.voidType(); + private List parameterTypes = new ArrayList<>(); + private List typeParameters = new ArrayList<>(); + private List exceptions = new ArrayList<>(); + + @Override + public String build() { + StringBuilder signature = new StringBuilder(); + + // type params + if (!typeParameters.isEmpty()) { + signature.append('<'); + for (TypeVariable typeParameter : typeParameters) { + typeParameter.appendTypeParameterToSignature(signature); + } + signature.append('>'); + } + + // params + signature.append('('); + for (Type parameterType : parameterTypes) { + signature.append(parameterType.toSignature()); + } + signature.append(')'); + + // return type + signature.append(returnType.toSignature()); + + // exceptions + if (!exceptions.isEmpty()) { + for (Type exceptionType : exceptions) { + signature.append('^').append(exceptionType.toSignature()); + } + } + return signature.toString(); + } + + @Override + public MethodSignatureBuilder addTypeParameter(TypeVariable typeParameter) { + typeParameters.add(Objects.requireNonNull(typeParameter)); + return this; + } + + @Override + public MethodSignatureBuilder setReturnType(Type returnType) { + this.returnType = Objects.requireNonNull(returnType); + return this; + } + + @Override + public MethodSignatureBuilder addParameter(Type parameterType) { + this.parameterTypes.add(Objects.requireNonNull(parameterType)); + return this; + } + + @Override + public MethodSignatureBuilder addException(ClassType exceptionType) { + this.exceptions.add(Objects.requireNonNull(exceptionType)); + return this; + } + + @Override + public MethodSignatureBuilder addException(TypeVariable exceptionType) { + this.exceptions.add(Objects.requireNonNull(exceptionType)); + return this; + } + +} \ No newline at end of file diff --git a/src/main/java/io/quarkus/gizmo/SignatureBuilder.java b/src/main/java/io/quarkus/gizmo/SignatureBuilder.java new file mode 100644 index 0000000..6c99d5a --- /dev/null +++ b/src/main/java/io/quarkus/gizmo/SignatureBuilder.java @@ -0,0 +1,62 @@ +package io.quarkus.gizmo; + +import io.quarkus.gizmo.Type.ClassType; +import io.quarkus.gizmo.Type.ParameterizedType; +import io.quarkus.gizmo.Type.TypeVariable; + +/** + * Builds a signature as defined in JVMS 17, chapter "4.7.9.1. Signatures". + * + * @see SignatureElement#setSignature(String) + */ +public interface SignatureBuilder { + + static ClassSignatureBuilder forClass() { + return new ClassSignatureBuilderImpl(); + } + + static MethodSignatureBuilder forMethod() { + return new MethodSignatureBuilderImpl(); + } + + static FieldSignatureBuilder forField() { + return new FieldSignatureBuilderImpl(); + } + + /** + * @return the signature + */ + String build(); + + interface ClassSignatureBuilder extends SignatureBuilder { + + ClassSignatureBuilder addTypeParameter(TypeVariable typeParameter); + + ClassSignatureBuilder setSuperClass(ClassType superClass); + + ClassSignatureBuilder setSuperClass(ParameterizedType superClass); + + ClassSignatureBuilder addSuperInterface(ClassType interfaceType); + + ClassSignatureBuilder addSuperInterface(ParameterizedType interfaceType); + } + + interface MethodSignatureBuilder extends SignatureBuilder { + + MethodSignatureBuilder addTypeParameter(TypeVariable typeParameter); + + MethodSignatureBuilder setReturnType(Type returnType); + + MethodSignatureBuilder addParameter(Type parameter); + + MethodSignatureBuilder addException(ClassType exceptionType); + + MethodSignatureBuilder addException(TypeVariable exceptionType); + } + + interface FieldSignatureBuilder extends SignatureBuilder { + + FieldSignatureBuilder setType(Type type); + } + +} diff --git a/src/main/java/io/quarkus/gizmo/SignatureElement.java b/src/main/java/io/quarkus/gizmo/SignatureElement.java index 68c2d4c..ce0b1ca 100644 --- a/src/main/java/io/quarkus/gizmo/SignatureElement.java +++ b/src/main/java/io/quarkus/gizmo/SignatureElement.java @@ -1,11 +1,18 @@ package io.quarkus.gizmo; /** - * An element that has a signature attribute + * An element that has a signature attribute. */ public interface SignatureElement { String getSignature(); + /** + * Use the convenient {@link SignatureBuilder} to build signatures for classes, methods and fields. + * + * @param signature The signature as defined in JVMS 17, chapter "4.7.9.1. Signatures" + * @return the element + * @see SignatureBuilder + */ S setSignature(String signature); } diff --git a/src/main/java/io/quarkus/gizmo/Type.java b/src/main/java/io/quarkus/gizmo/Type.java new file mode 100644 index 0000000..dae915a --- /dev/null +++ b/src/main/java/io/quarkus/gizmo/Type.java @@ -0,0 +1,464 @@ +package io.quarkus.gizmo; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import org.jboss.jandex.DotName; +import org.jboss.jandex.PrimitiveType.Primitive; + +/** + * This interface can be used to build a JVM signature for classes, methods and fields. + *

+ * Implementations are instantiated via factory methods; for example {@link #voidType()} and {@link #classType(Class)}. + * + * @see SignatureBuilder + */ +public interface Type { + + // Factory methods + + public static VoidType voidType() { + return VoidType.INSTANCE; + } + + public static PrimitiveType byteType() { + return PrimitiveType.BYTE; + } + + public static PrimitiveType booleanType() { + return PrimitiveType.BOOLEAN; + } + + public static PrimitiveType intType() { + return PrimitiveType.INT; + } + + public static PrimitiveType longType() { + return PrimitiveType.LONG; + } + + public static PrimitiveType shortType() { + return PrimitiveType.SHORT; + } + + public static PrimitiveType doubleType() { + return PrimitiveType.DOUBLE; + } + + public static PrimitiveType floatType() { + return PrimitiveType.FLOAT; + } + + public static PrimitiveType charType() { + return PrimitiveType.CHAR; + } + + public static ClassType classType(DotName name) { + return classType(Objects.requireNonNull(name).toString().replace('.', '/')); + } + + public static ClassType classType(String name) { + return new ClassType(name, null); + } + + public static ClassType classType(Class classType) { + return classType(Objects.requireNonNull(classType).getName().replace('.', '/')); + } + + public static ParameterizedType parameterizedType(ClassType classType, Type... typeArguments) { + if (typeArguments.length == 0) { + throw new IllegalArgumentException("No type arguments"); + } + return new ParameterizedType(classType, Arrays.asList(typeArguments), null); + } + + public static ArrayType arrayType(Type elementType) { + return new ArrayType(elementType, 1); + } + + public static ArrayType arrayType(Type elementType, int dimensions) { + return new ArrayType(elementType, dimensions); + } + + public static TypeVariable typeVariable(String name) { + return typeVariable(name, ClassType.OBJECT); + } + + public static TypeVariable typeVariable(String name, Type classBound, Type... interfaceBounds) { + return new TypeVariable(name, classBound, Arrays.asList(interfaceBounds)); + } + + public static WildcardType wildcardTypeWithUpperBound(Type bound) { + return new WildcardType(bound, null); + } + + public static WildcardType wildcardTypeWithLowerBound(Type bound) { + return new WildcardType(null, bound); + } + + public static WildcardType wildcardTypeUnbounded() { + return new WildcardType(ClassType.OBJECT, null); + } + + /** + * + * @param signature + */ + void appendToSignature(StringBuilder signature); + + /** + * + * @return the signature as defined in JVMS 17, chapter "4.7.9.1. Signatures" + */ + default String toSignature() { + StringBuilder sb = new StringBuilder(); + appendToSignature(sb); + return sb.toString(); + } + + default boolean isVoid() { + return false; + } + + default boolean isPrimitive() { + return false; + } + + default boolean isClass() { + return false; + } + + default boolean isArray() { + return false; + } + + default boolean isParameterizedType() { + return false; + } + + default boolean isTypeVariable() { + return false; + } + + default boolean isWildcard() { + return false; + } + + default VoidType asVoid() { + throw new IllegalStateException("Not a void"); + } + + default PrimitiveType asPrimitive() { + throw new IllegalStateException("Not a primitive"); + } + + default ClassType asClass() { + throw new IllegalStateException("Not a class"); + } + + default ArrayType asArray() { + throw new IllegalStateException("Not an array"); + } + + default ParameterizedType asParameterizedType() { + throw new IllegalStateException("Not a parameterized type"); + } + + default TypeVariable asTypeVariable() { + throw new IllegalStateException("Not a type variable"); + } + + default WildcardType asWildcard() { + throw new IllegalStateException("Not a wildcard type"); + } + + // Implementations + + public static class WildcardType implements Type { + + final Type lowerBound; + final Type upperBound; + + WildcardType(Type upperBound, Type lowerBound) { + if (upperBound == null && lowerBound == null) { + throw new NullPointerException(); + } + this.upperBound = upperBound; + this.lowerBound = lowerBound; + } + + @Override + public boolean isWildcard() { + return true; + } + + @Override + public WildcardType asWildcard() { + return this; + } + + @Override + public void appendToSignature(StringBuilder signature) { + if (lowerBound != null) { + signature.append('-').append(lowerBound.toSignature()); + } else if (upperBound.isClass() && upperBound.asClass().name.equals(ClassType.OBJECT.name)) { + signature.append('*'); + } else { + signature.append('+').append(upperBound.toSignature()); + } + } + + } + + public static class TypeVariable implements Type { + + final String name; + final Type classBound; + final List interfaceBounds; + + TypeVariable(String name, Type classBound, List interfaceBounds) { + this.name = Objects.requireNonNull(name); + this.classBound = classBound; + this.interfaceBounds = interfaceBounds; + } + + @Override + public void appendToSignature(StringBuilder signature) { + signature.append('T').append(name).append(';').toString(); + } + + public void appendTypeParameterToSignature(StringBuilder signature) { + signature.append(name).append(":"); + if (classBound != null) { + signature.append(classBound.toSignature()); + } + for (Type bound : interfaceBounds) { + signature.append(":").append(bound.toSignature()); + } + } + + @Override + public boolean isTypeVariable() { + return true; + } + + @Override + public TypeVariable asTypeVariable() { + return this; + } + + } + + public static class ArrayType implements Type { + + final Type elementType; + final int dimensions; + + ArrayType(Type elementType, int dimensions) { + this.elementType = Objects.requireNonNull(elementType); + this.dimensions = dimensions; + } + + @Override + public void appendToSignature(StringBuilder signature) { + for (int i = 0; i < dimensions; i++) { + signature.append('['); + } + signature.append(elementType.toSignature()); + } + + @Override + public boolean isArray() { + return true; + } + + @Override + public ArrayType asArray() { + return this; + } + + } + + public static class ParameterizedType implements Type { + + final ClassType classType; + final List typeArguments; + final Type declaringClassType; + + ParameterizedType(ClassType classType, List typeArguments, Type declaringClassType) { + this.classType = Objects.requireNonNull(classType); + this.typeArguments = typeArguments; + this.declaringClassType = declaringClassType; + } + + @Override + public void appendToSignature(StringBuilder signature) { + if (declaringClassType != null) { + // Append the declaring class and remove the last semicolon + declaringClassType.appendToSignature(signature); + signature.deleteCharAt(signature.length() - 1); + signature.append('.'); + } else { + signature.append('L'); + } + signature.append(classType.name); + if (!typeArguments.isEmpty()) { + signature.append('<'); + for (Type argument : typeArguments) { + signature.append(argument.toSignature()); + } + signature.append('>'); + } + signature.append(';'); + } + + @Override + public boolean isParameterizedType() { + return true; + } + + @Override + public ParameterizedType asParameterizedType() { + return this; + } + + /** + * Build a signature like Lorg/acme/Parent.Inner;. + * + * @param simpleName + * @return the nested class + */ + public ClassType nestedClassType(String simpleName) { + return new ClassType(simpleName, this); + } + + /** + * Build a signature like Lorg/acme/Parent.Inner;. + * + * @param simpleName + * @return the nested class + */ + public ParameterizedType nestedParameterizedType(String simpleName, Type... typeArguments) { + return new ParameterizedType(Type.classType(simpleName), Arrays.asList(typeArguments), this); + } + + } + + public static class ClassType implements Type { + + public static ClassType OBJECT = classType(DotName.OBJECT_NAME); + + final String name; + final Type declaringClassType; + + ClassType(String name, Type declaringClassType) { + this.name = Objects.requireNonNull(name); + this.declaringClassType = declaringClassType; + } + + @Override + public void appendToSignature(StringBuilder signature) { + if (declaringClassType != null) { + // Append the declaring class and remove the last semicolon + declaringClassType.appendToSignature(signature); + signature.deleteCharAt(signature.length() - 1); + signature.append('.'); + } else { + signature.append('L'); + } + signature.append(name).append(';'); + } + + @Override + public boolean isClass() { + return true; + } + + @Override + public ClassType asClass() { + return this; + } + + } + + public static class VoidType implements Type { + + public static final VoidType INSTANCE = new VoidType(); + + @Override + public void appendToSignature(StringBuilder signature) { + signature.append("V"); + } + + @Override + public boolean isVoid() { + return true; + } + + @Override + public VoidType asVoid() { + return this; + } + + } + + public static class PrimitiveType implements Type { + + public static final PrimitiveType BYTE = new PrimitiveType(Primitive.BYTE); + public static final PrimitiveType CHAR = new PrimitiveType(Primitive.CHAR); + public static final PrimitiveType DOUBLE = new PrimitiveType(Primitive.DOUBLE); + public static final PrimitiveType FLOAT = new PrimitiveType(Primitive.FLOAT); + public static final PrimitiveType INT = new PrimitiveType(Primitive.INT); + public static final PrimitiveType LONG = new PrimitiveType(Primitive.LONG); + public static final PrimitiveType SHORT = new PrimitiveType(Primitive.SHORT); + public static final PrimitiveType BOOLEAN = new PrimitiveType(Primitive.BOOLEAN); + + final Primitive primitive; + + PrimitiveType(Primitive primitive) { + this.primitive = Objects.requireNonNull(primitive); + } + + @Override + public void appendToSignature(StringBuilder signature) { + signature.append(toSignature()); + } + + @Override + public String toSignature() { + switch (primitive) { + case BOOLEAN: + return "Z"; + case BYTE: + return "B"; + case CHAR: + return "C"; + case DOUBLE: + return "D"; + case FLOAT: + return "F"; + case INT: + return "I"; + case LONG: + return "J"; + case SHORT: + return "S"; + default: + throw new IllegalStateException(); + } + } + + @Override + public boolean isPrimitive() { + return true; + } + + @Override + public PrimitiveType asPrimitive() { + return this; + } + + } + +} diff --git a/src/test/java/io/quarkus/gizmo/SignaturesTest.java b/src/test/java/io/quarkus/gizmo/SignaturesTest.java new file mode 100644 index 0000000..8e4b3c0 --- /dev/null +++ b/src/test/java/io/quarkus/gizmo/SignaturesTest.java @@ -0,0 +1,230 @@ +package io.quarkus.gizmo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.jboss.jandex.DotName; +import org.junit.Test; + +import io.quarkus.gizmo.SignaturesTest.Nested.Inner.Inner2; +import io.quarkus.gizmo.SignaturesTest.NestedParam.InnerParam; + +public class SignaturesTest { + + @Test + public void testMethodSignatures() { + // void test() + assertEquals("()V", + SignatureBuilder.forMethod().build()); + + // void test(long l) + assertEquals("(J)V", + SignatureBuilder.forMethod().addParameter(Type.longType()).build()); + + // List test(List list) + assertEquals("(Ljava/util/List<*>;)Ljava/util/List;", + SignatureBuilder.forMethod() + .setReturnType(Type.parameterizedType(Type.classType(List.class), Type.classType(String.class))) + .addParameter(Type.parameterizedType(Type.classType(List.class), Type.wildcardTypeUnbounded())) + .build()); + + // Object test() + assertEquals("()Ljava/lang/Object;", + SignatureBuilder.forMethod().setReturnType(Type.classType(DotName.OBJECT_NAME)).build()); + + // > String[] test(T t) + assertEquals(";>(TT;)[Ljava/lang/String;", + SignatureBuilder.forMethod() + .setReturnType(Type.arrayType(Type.classType(String.class))) + .addParameter(Type.typeVariable("T")) + .addTypeParameter(Type.typeVariable("T", null, + Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T")))) + .build()); + + // List test(int a, T t) + assertEquals("(ITT;)Ljava/util/List;", + SignatureBuilder.forMethod() + .setReturnType( + Type.parameterizedType(Type.classType(DotName.createSimple(List.class)), + Type.typeVariable("R"))) + .addParameter(Type.intType()) + .addParameter(Type.typeVariable("T")).build()); + + // boolean test(int i) + assertEquals("(I)Z", + SignatureBuilder.forMethod() + .setReturnType(Type.booleanType()) + .addParameter(Type.intType()).build()); + + // , U extends Comparable, V extends Exception> T bbb(U arg, W arg2, OuterParam self) + assertEquals( + ";U::Ljava/lang/Comparable;V:Ljava/lang/Exception;>(TU;TW;Ltest/OuterParam;)TT;", + SignatureBuilder.forMethod() + .setReturnType(Type.typeVariable("T")) + .addParameter(Type.typeVariable("U")) + .addParameter(Type.typeVariable("W")) + .addParameter(Type.parameterizedType(Type.classType("test/OuterParam"), + Type.typeVariable("W"))) + .addTypeParameter(Type.typeVariable("T", Type.classType(Number.class), + Type.parameterizedType( + Type.classType(Comparable.class), + Type.typeVariable("T")))) + .addTypeParameter(Type.typeVariable("U", null, + Type.parameterizedType(Type.classType(Comparable.class), + Type.typeVariable("U")))) + .addTypeParameter(Type.typeVariable("V", Type.classType(Exception.class))).build()); + + // , U extends Comparable, V extends Exception> T test(List arg, W arg2, Foo arg3) throws IllegalArgumentException, V + assertEquals( + ";U::Ljava/lang/Comparable;V:Ljava/lang/Exception;>(Ljava/util/List<+TU;>;TW;Lio/quarkus/gizmo/SignaturesTest$NestedParam.Inner;)TT;^Ljava/lang/IllegalArgumentException;^TV;", + SignatureBuilder.forMethod() + .setReturnType(Type.typeVariable("T")) + .addTypeParameter(Type.typeVariable("T", Type.classType(Number.class), + Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T")))) + .addTypeParameter(Type.typeVariable("U", null, + Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("U")))) + .addTypeParameter(Type.typeVariable("V", Type.classType(Exception.class))) + .addParameter(Type.parameterizedType(Type.classType(List.class), + Type.wildcardTypeWithUpperBound(Type.typeVariable("U")))) + .addParameter(Type.typeVariable("W")) + .addParameter(Type.parameterizedType(Type.classType(NestedParam.class), Type.typeVariable("P")) + .nestedClassType( + NestedParam.Inner.class.getSimpleName())) + .addException(Type.classType(IllegalArgumentException.class)) + .addException(Type.typeVariable("V")) + .build()); + + // Nested.Inner.Inner2 test() + assertEquals("()Lio/quarkus/gizmo/SignaturesTest$Nested$Inner$Inner2;", + SignatureBuilder.forMethod() + .setReturnType(Type.classType(Inner2.class)) + .build()); + } + + @Test + public void testFieldSignatures() { + // List foo; + assertEquals("Ljava/util/List;", + SignatureBuilder.forField() + .setType(Type.parameterizedType(Type.classType(List.class), Type.classType(String.class))) + .build()); + + // T foo; + assertEquals("TT;", + SignatureBuilder.forField() + .setType(Type.typeVariable("T")) + .build()); + + // List foo; + assertEquals("Ljava/util/List;", + SignatureBuilder.forField() + .setType(Type.parameterizedType(Type.classType(List.class), + Type.typeVariable("T", Type.classType(Number.class)))) + .build()); + + // double foo; + assertEquals("D", + SignatureBuilder.forField() + .setType(Type.doubleType()) + .build()); + + // List foo; + assertEquals("Ljava/util/List<*>;", + SignatureBuilder.forField() + .setType(Type.parameterizedType(Type.classType(List.class), Type.wildcardTypeUnbounded())) + .build()); + + // Map foo; + assertEquals("Ljava/util/Map<+Ljava/lang/Number;-Ljava/lang/Number;>;", + SignatureBuilder.forField() + .setType(Type.parameterizedType(Type.classType(Map.class), + Type.wildcardTypeWithUpperBound(Type.classType(Number.class)), + Type.wildcardTypeWithLowerBound(Type.classType(Number.class)))) + .build()); + + // Signature not needed + // Nested foo; + assertEquals("Lio/quarkus/gizmo/SignaturesTest$Nested;", + SignatureBuilder.forField() + .setType(Type.classType(Nested.class)) + .build()); + + // Signature not needed + // NestedParam.Inner.Inner2 foo; + assertEquals("Lio/quarkus/gizmo/SignaturesTest$NestedParam$InnerParam$Inner2;", + SignatureBuilder.forField() + .setType(Type.classType(NestedParam.InnerParam.Inner2.class)) + .build()); + + // NestedParam.InnerParam foo; + assertEquals("Lio/quarkus/gizmo/SignaturesTest$NestedParam.InnerParam;", + SignatureBuilder.forField() + .setType(Type.parameterizedType(Type.classType(NestedParam.class), Type.typeVariable("P")) + .nestedParameterizedType(InnerParam.class.getSimpleName(), Type.typeVariable("P"))) + .build()); + } + + @Test + public void testClassSignatures() { + // class Foo + assertEquals("Ljava/lang/Object;", + SignatureBuilder.forClass() + .addTypeParameter(Type.typeVariable("T")) + .build()); + + // class Foo extends List + assertEquals("Ljava/util/List;", + SignatureBuilder.forClass() + .addTypeParameter(Type.typeVariable("T")) + .setSuperClass(Type.parameterizedType(Type.classType(List.class), Type.typeVariable("T"))) + .build()); + + // class Foo extends List implements Serializable, Comparable + assertEquals("Ljava/util/List;Ljava/io/Serializable;Ljava/lang/Comparable;", + SignatureBuilder.forClass() + .addTypeParameter(Type.typeVariable("T")) + .setSuperClass(Type.parameterizedType(Type.classType(List.class), Type.typeVariable("T"))) + .addSuperInterface(Type.classType(Serializable.class)) + .addSuperInterface(Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T"))) + .build()); + + try { + SignatureBuilder.forClass() + .setSuperClass(Type.parameterizedType(Type.classType(List.class), Type.wildcardTypeUnbounded())); + fail(); + } catch (Exception expected) { + } + } + + public static class Nested { + + public class Inner { + + class Inner2 { + + } + } + + } + + public static class NestedParam

{ + + InnerParam

inner; + + public class Inner { + } + + public class InnerParam { + + class Inner2 { + + } + } + + } + +} From 634a4b61c15b0ee577a2528cbca65a75e2cb4e5d Mon Sep 17 00:00:00 2001 From: Ladislav Thon Date: Fri, 6 Jan 2023 18:23:21 +0100 Subject: [PATCH 2/5] Refactor the SignatureBuilder API for better encapsulation --- .../gizmo/ClassSignatureBuilderImpl.java | 15 +- .../gizmo/FieldSignatureBuilderImpl.java | 8 +- .../gizmo/MethodSignatureBuilderImpl.java | 23 +- .../io/quarkus/gizmo/SignatureBuilder.java | 20 +- .../io/quarkus/gizmo/SignatureElement.java | 2 +- src/main/java/io/quarkus/gizmo/Type.java | 466 +++++++++--------- .../java/io/quarkus/gizmo/SignaturesTest.java | 100 +++- 7 files changed, 357 insertions(+), 277 deletions(-) diff --git a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java index 3b2286e..ef21694 100644 --- a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java +++ b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java @@ -3,17 +3,14 @@ import java.util.ArrayList; import java.util.List; -import org.jboss.jandex.DotName; - import io.quarkus.gizmo.SignatureBuilder.ClassSignatureBuilder; import io.quarkus.gizmo.Type.ClassType; import io.quarkus.gizmo.Type.ParameterizedType; import io.quarkus.gizmo.Type.TypeVariable; class ClassSignatureBuilderImpl implements ClassSignatureBuilder { - - private Type superClass = Type.classType(DotName.OBJECT_NAME); private List typeParameters = new ArrayList<>(); + private Type superClass = ClassType.OBJECT; private List superInterfaces = new ArrayList<>(); @Override @@ -30,12 +27,12 @@ public String build() { } // superclass - signature.append(superClass.toSignature()); + superClass.appendToSignature(signature); // interfaces if (!superInterfaces.isEmpty()) { for (Type superInterface : superInterfaces) { - signature.append(superInterface.toSignature()); + superInterface.appendToSignature(signature); } } return signature.toString(); @@ -58,6 +55,7 @@ public ClassSignatureBuilder setSuperClass(ParameterizedType superClass) { if (containsWildcard(superClass)) { throw new IllegalArgumentException("A super type may not specify a wilcard"); } + this.superClass = superClass; return this; } @@ -81,7 +79,7 @@ private boolean containsWildcard(Type type) { if (type.isWildcard()) { return true; } else if (type.isParameterizedType()) { - for (Type typeArgument : type.asParameterizedType().typeArguments) { + for (Type typeArgument : type.asParameterizedType().getTypeArguments()) { if (containsWildcard(typeArgument)) { return true; } @@ -89,5 +87,4 @@ private boolean containsWildcard(Type type) { } return false; } - -} \ No newline at end of file +} diff --git a/src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java index d26342e..8364a1d 100644 --- a/src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java +++ b/src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java @@ -5,12 +5,13 @@ import io.quarkus.gizmo.SignatureBuilder.FieldSignatureBuilder; class FieldSignatureBuilderImpl implements FieldSignatureBuilder { - private Type type; @Override public String build() { - return type.toSignature(); + StringBuilder signature = new StringBuilder(); + type.appendToSignature(signature); + return signature.toString(); } @Override @@ -18,5 +19,4 @@ public FieldSignatureBuilder setType(Type type) { this.type = Objects.requireNonNull(type); return this; } - -} \ No newline at end of file +} diff --git a/src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java index b184928..78d4f0b 100644 --- a/src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java +++ b/src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java @@ -7,12 +7,12 @@ import io.quarkus.gizmo.SignatureBuilder.MethodSignatureBuilder; import io.quarkus.gizmo.Type.ClassType; import io.quarkus.gizmo.Type.TypeVariable; +import io.quarkus.gizmo.Type.VoidType; class MethodSignatureBuilderImpl implements MethodSignatureBuilder { - - private Type returnType = Type.voidType(); - private List parameterTypes = new ArrayList<>(); private List typeParameters = new ArrayList<>(); + private Type returnType = VoidType.INSTANCE; + private List parameterTypes = new ArrayList<>(); private List exceptions = new ArrayList<>(); @Override @@ -28,22 +28,24 @@ public String build() { signature.append('>'); } - // params + // param types signature.append('('); for (Type parameterType : parameterTypes) { - signature.append(parameterType.toSignature()); + parameterType.appendToSignature(signature); } signature.append(')'); // return type - signature.append(returnType.toSignature()); + returnType.appendToSignature(signature); - // exceptions + // exception types if (!exceptions.isEmpty()) { for (Type exceptionType : exceptions) { - signature.append('^').append(exceptionType.toSignature()); + signature.append('^'); + exceptionType.appendToSignature(signature); } } + return signature.toString(); } @@ -60,7 +62,7 @@ public MethodSignatureBuilder setReturnType(Type returnType) { } @Override - public MethodSignatureBuilder addParameter(Type parameterType) { + public MethodSignatureBuilder addParameterType(Type parameterType) { this.parameterTypes.add(Objects.requireNonNull(parameterType)); return this; } @@ -76,5 +78,4 @@ public MethodSignatureBuilder addException(TypeVariable exceptionType) { this.exceptions.add(Objects.requireNonNull(exceptionType)); return this; } - -} \ No newline at end of file +} diff --git a/src/main/java/io/quarkus/gizmo/SignatureBuilder.java b/src/main/java/io/quarkus/gizmo/SignatureBuilder.java index 6c99d5a..d90f3c2 100644 --- a/src/main/java/io/quarkus/gizmo/SignatureBuilder.java +++ b/src/main/java/io/quarkus/gizmo/SignatureBuilder.java @@ -5,12 +5,11 @@ import io.quarkus.gizmo.Type.TypeVariable; /** - * Builds a signature as defined in JVMS 17, chapter "4.7.9.1. Signatures". + * Builds a generic signature as defined in JVMS 17, chapter "4.7.9.1. Signatures". * * @see SignatureElement#setSignature(String) */ public interface SignatureBuilder { - static ClassSignatureBuilder forClass() { return new ClassSignatureBuilderImpl(); } @@ -24,12 +23,14 @@ static FieldSignatureBuilder forField() { } /** - * @return the signature + * @return the generic signature */ String build(); + /** + * Builds a generic signature of a class (including interfaces). + */ interface ClassSignatureBuilder extends SignatureBuilder { - ClassSignatureBuilder addTypeParameter(TypeVariable typeParameter); ClassSignatureBuilder setSuperClass(ClassType superClass); @@ -41,22 +42,25 @@ interface ClassSignatureBuilder extends SignatureBuilder { ClassSignatureBuilder addSuperInterface(ParameterizedType interfaceType); } + /** + * Builds a generic signature of a method (including constructors). + */ interface MethodSignatureBuilder extends SignatureBuilder { - MethodSignatureBuilder addTypeParameter(TypeVariable typeParameter); MethodSignatureBuilder setReturnType(Type returnType); - MethodSignatureBuilder addParameter(Type parameter); + MethodSignatureBuilder addParameterType(Type parameterType); MethodSignatureBuilder addException(ClassType exceptionType); MethodSignatureBuilder addException(TypeVariable exceptionType); } + /** + * Builds a generic signature of a field. Also usable for building generic signatures of record components. + */ interface FieldSignatureBuilder extends SignatureBuilder { - FieldSignatureBuilder setType(Type type); } - } diff --git a/src/main/java/io/quarkus/gizmo/SignatureElement.java b/src/main/java/io/quarkus/gizmo/SignatureElement.java index ce0b1ca..be911dc 100644 --- a/src/main/java/io/quarkus/gizmo/SignatureElement.java +++ b/src/main/java/io/quarkus/gizmo/SignatureElement.java @@ -10,7 +10,7 @@ public interface SignatureElement { /** * Use the convenient {@link SignatureBuilder} to build signatures for classes, methods and fields. * - * @param signature The signature as defined in JVMS 17, chapter "4.7.9.1. Signatures" + * @param signature The generic signature as defined in JVMS 17, chapter "4.7.9.1. Signatures" * @return the element * @see SignatureBuilder */ diff --git a/src/main/java/io/quarkus/gizmo/Type.java b/src/main/java/io/quarkus/gizmo/Type.java index dae915a..b593f0e 100644 --- a/src/main/java/io/quarkus/gizmo/Type.java +++ b/src/main/java/io/quarkus/gizmo/Type.java @@ -1,6 +1,7 @@ package io.quarkus.gizmo; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -8,26 +9,29 @@ import org.jboss.jandex.PrimitiveType.Primitive; /** - * This interface can be used to build a JVM signature for classes, methods and fields. + * Used to express types when {@linkplain SignatureBuilder building} a generic signature of some declaration. *

- * Implementations are instantiated via factory methods; for example {@link #voidType()} and {@link #classType(Class)}. + * Implementations are created via factory methods such as {@link #voidType()} and {@link #classType(Class)}. * * @see SignatureBuilder */ -public interface Type { - +public abstract class Type { // Factory methods public static VoidType voidType() { return VoidType.INSTANCE; } + public static PrimitiveType booleanType() { + return PrimitiveType.BOOLEAN; + } + public static PrimitiveType byteType() { return PrimitiveType.BYTE; } - public static PrimitiveType booleanType() { - return PrimitiveType.BOOLEAN; + public static PrimitiveType shortType() { + return PrimitiveType.SHORT; } public static PrimitiveType intType() { @@ -38,39 +42,35 @@ public static PrimitiveType longType() { return PrimitiveType.LONG; } - public static PrimitiveType shortType() { - return PrimitiveType.SHORT; + public static PrimitiveType floatType() { + return PrimitiveType.FLOAT; } public static PrimitiveType doubleType() { return PrimitiveType.DOUBLE; } - public static PrimitiveType floatType() { - return PrimitiveType.FLOAT; - } - public static PrimitiveType charType() { return PrimitiveType.CHAR; } - public static ClassType classType(DotName name) { - return classType(Objects.requireNonNull(name).toString().replace('.', '/')); + public static ClassType classType(DotName className) { + return classType(className.toString()); } - public static ClassType classType(String name) { - return new ClassType(name, null); + public static ClassType classType(String className) { + return new ClassType(className.replace('.', '/'), null); } - public static ClassType classType(Class classType) { - return classType(Objects.requireNonNull(classType).getName().replace('.', '/')); + public static ClassType classType(Class clazz) { + return classType(clazz.getName()); } - public static ParameterizedType parameterizedType(ClassType classType, Type... typeArguments) { + public static ParameterizedType parameterizedType(ClassType genericClass, Type... typeArguments) { if (typeArguments.length == 0) { throw new IllegalArgumentException("No type arguments"); } - return new ParameterizedType(classType, Arrays.asList(typeArguments), null); + return new ParameterizedType(genericClass, Arrays.asList(typeArguments), null); } public static ArrayType arrayType(Type elementType) { @@ -85,175 +85,239 @@ public static TypeVariable typeVariable(String name) { return typeVariable(name, ClassType.OBJECT); } + public static TypeVariable typeVariable(String name, Type classOrTypeVariableBound) { + Type bound = Objects.requireNonNull(classOrTypeVariableBound); + if (!bound.isClass() && !bound.isParameterizedType() && !bound.isTypeVariable()) { + throw new IllegalArgumentException("Type variable bound must be a class or a type variable"); + } + return new TypeVariable(name, bound, Collections.emptyList()); + } + public static TypeVariable typeVariable(String name, Type classBound, Type... interfaceBounds) { + if (classBound != null && !classBound.isClass() && !classBound.isParameterizedType()) { + throw new IllegalArgumentException("First type variable bound must be a class"); + } + for (Type interfaceBound : interfaceBounds) { + if (!interfaceBound.isClass() && !interfaceBound.isParameterizedType()) { + throw new IllegalArgumentException("Next type variable bounds must all be interfaces"); + } + } + return new TypeVariable(name, classBound, Arrays.asList(interfaceBounds)); } public static WildcardType wildcardTypeWithUpperBound(Type bound) { - return new WildcardType(bound, null); + return new WildcardType(Objects.requireNonNull(bound), null); } public static WildcardType wildcardTypeWithLowerBound(Type bound) { - return new WildcardType(null, bound); + return new WildcardType(null, Objects.requireNonNull(bound)); } public static WildcardType wildcardTypeUnbounded() { return new WildcardType(ClassType.OBJECT, null); } - /** - * - * @param signature - */ - void appendToSignature(StringBuilder signature); + // implementation details - /** - * - * @return the signature as defined in JVMS 17, chapter "4.7.9.1. Signatures" - */ - default String toSignature() { - StringBuilder sb = new StringBuilder(); - appendToSignature(sb); - return sb.toString(); - } + abstract void appendToSignature(StringBuilder signature); - default boolean isVoid() { + boolean isVoid() { return false; } - default boolean isPrimitive() { + boolean isPrimitive() { return false; } - default boolean isClass() { + boolean isClass() { return false; } - default boolean isArray() { + boolean isArray() { return false; } - default boolean isParameterizedType() { + boolean isParameterizedType() { return false; } - default boolean isTypeVariable() { + boolean isTypeVariable() { return false; } - default boolean isWildcard() { + boolean isWildcard() { return false; } - default VoidType asVoid() { + VoidType asVoid() { throw new IllegalStateException("Not a void"); } - default PrimitiveType asPrimitive() { + PrimitiveType asPrimitive() { throw new IllegalStateException("Not a primitive"); } - default ClassType asClass() { + ClassType asClass() { throw new IllegalStateException("Not a class"); } - default ArrayType asArray() { + ArrayType asArray() { throw new IllegalStateException("Not an array"); } - default ParameterizedType asParameterizedType() { + ParameterizedType asParameterizedType() { throw new IllegalStateException("Not a parameterized type"); } - default TypeVariable asTypeVariable() { + TypeVariable asTypeVariable() { throw new IllegalStateException("Not a type variable"); } - default WildcardType asWildcard() { + WildcardType asWildcard() { throw new IllegalStateException("Not a wildcard type"); } - // Implementations + // Types - public static class WildcardType implements Type { - - final Type lowerBound; - final Type upperBound; + public static final class VoidType extends Type { + public static final VoidType INSTANCE = new VoidType(); - WildcardType(Type upperBound, Type lowerBound) { - if (upperBound == null && lowerBound == null) { - throw new NullPointerException(); - } - this.upperBound = upperBound; - this.lowerBound = lowerBound; + @Override + void appendToSignature(StringBuilder signature) { + signature.append("V"); } @Override - public boolean isWildcard() { + boolean isVoid() { return true; } @Override - public WildcardType asWildcard() { + VoidType asVoid() { return this; } + } + + public static final class PrimitiveType extends Type { + public static final PrimitiveType BOOLEAN = new PrimitiveType(Primitive.BOOLEAN); + public static final PrimitiveType BYTE = new PrimitiveType(Primitive.BYTE); + public static final PrimitiveType SHORT = new PrimitiveType(Primitive.SHORT); + public static final PrimitiveType INT = new PrimitiveType(Primitive.INT); + public static final PrimitiveType LONG = new PrimitiveType(Primitive.LONG); + public static final PrimitiveType FLOAT = new PrimitiveType(Primitive.FLOAT); + public static final PrimitiveType DOUBLE = new PrimitiveType(Primitive.DOUBLE); + public static final PrimitiveType CHAR = new PrimitiveType(Primitive.CHAR); + + private final Primitive primitive; + + PrimitiveType(Primitive primitive) { + this.primitive = Objects.requireNonNull(primitive); + } @Override - public void appendToSignature(StringBuilder signature) { - if (lowerBound != null) { - signature.append('-').append(lowerBound.toSignature()); - } else if (upperBound.isClass() && upperBound.asClass().name.equals(ClassType.OBJECT.name)) { - signature.append('*'); - } else { - signature.append('+').append(upperBound.toSignature()); + void appendToSignature(StringBuilder signature) { + switch (primitive) { + case BOOLEAN: + signature.append("Z"); + break; + case BYTE: + signature.append("B"); + break; + case SHORT: + signature.append("S"); + case INT: + signature.append("I"); + break; + case LONG: + signature.append("J"); + break; + case FLOAT: + signature.append("F"); + break; + case DOUBLE: + signature.append("D"); + break; + case CHAR: + signature.append("C"); + break; + default: + throw new IllegalStateException("Unknown primitive type: " + primitive.toString()); } } + @Override + boolean isPrimitive() { + return true; + } + + @Override + PrimitiveType asPrimitive() { + return this; + } } - public static class TypeVariable implements Type { + public static final class ClassType extends Type { + public static final ClassType OBJECT = new ClassType("java/lang/Object", null); - final String name; - final Type classBound; - final List interfaceBounds; + private final String name; // always slash-delimited + private final Type owner; - TypeVariable(String name, Type classBound, List interfaceBounds) { + ClassType(String name, Type owner) { this.name = Objects.requireNonNull(name); - this.classBound = classBound; - this.interfaceBounds = interfaceBounds; + this.owner = owner; } - @Override - public void appendToSignature(StringBuilder signature) { - signature.append('T').append(name).append(';').toString(); + /** + * Allows building a signature like {@code Lcom/example/Outer.Inner;}. This is usually + * unnecessary, because {@code Lcom/example/Outer$Inner} is also a valid signature, + * but it's occasionally useful to build a signature of more complex inner types. + * + * @param simpleName simple name of the member class nested in this class + * @return the inner class + */ + public ClassType innerClass(String simpleName) { + return new ClassType(simpleName, this); } - public void appendTypeParameterToSignature(StringBuilder signature) { - signature.append(name).append(":"); - if (classBound != null) { - signature.append(classBound.toSignature()); - } - for (Type bound : interfaceBounds) { - signature.append(":").append(bound.toSignature()); + /** + * Allows build a signature like {@code Lcom/example/Outer.Inner;}. This is usually + * unnecessary, because {@code Lcom/example/Outer$Inner;} is also a valid signature, + * but it's occasionally useful to build a signature of more complex inner types. + * + * @param simpleName simple name of the generic member class nested in this class + * @return the inner parameterized type + */ + public ParameterizedType innerParameterizedType(String simpleName, Type... typeArguments) { + return new ParameterizedType(new ClassType(simpleName, null), Arrays.asList(typeArguments), this); + } + + @Override + void appendToSignature(StringBuilder signature) { + if (owner != null) { + // Append the owner class and replace the last semicolon with a period + owner.appendToSignature(signature); + signature.setCharAt(signature.length() - 1, '.'); + } else { + signature.append('L'); } + signature.append(name).append(';'); } @Override - public boolean isTypeVariable() { + boolean isClass() { return true; } @Override - public TypeVariable asTypeVariable() { + ClassType asClass() { return this; } - } - public static class ArrayType implements Type { - - final Type elementType; - final int dimensions; + public static final class ArrayType extends Type { + private final Type elementType; + private final int dimensions; ArrayType(Type elementType, int dimensions) { this.elementType = Objects.requireNonNull(elementType); @@ -261,52 +325,69 @@ public static class ArrayType implements Type { } @Override - public void appendToSignature(StringBuilder signature) { + void appendToSignature(StringBuilder signature) { for (int i = 0; i < dimensions; i++) { signature.append('['); } - signature.append(elementType.toSignature()); + elementType.appendToSignature(signature); } @Override - public boolean isArray() { + boolean isArray() { return true; } @Override - public ArrayType asArray() { + ArrayType asArray() { return this; } - } - public static class ParameterizedType implements Type { + public static final class ParameterizedType extends Type { + private final ClassType genericClass; + private final List typeArguments; + private final Type owner; - final ClassType classType; - final List typeArguments; - final Type declaringClassType; + ParameterizedType(ClassType genericClass, List typeArguments, Type owner) { + this.genericClass = Objects.requireNonNull(genericClass); + this.typeArguments = Objects.requireNonNull(typeArguments); + this.owner = owner; + } - ParameterizedType(ClassType classType, List typeArguments, Type declaringClassType) { - this.classType = Objects.requireNonNull(classType); - this.typeArguments = typeArguments; - this.declaringClassType = declaringClassType; + /** + * Allows build a signature like {@code Lcom/example/Outer.Inner;}. + * + * @param simpleName simple name of the member class nested in this parameterized type + * @return the inner class + */ + public ClassType innerClass(String simpleName) { + return new ClassType(simpleName, this); + } + + /** + * Allows building a signature like {@code Lcom/example/Outer.Inner;}. + * + * @param simpleName simple name of the generic member class nested in this parameterized type + * @return the inner parameterized type + */ + public ParameterizedType innerParameterizedType(String simpleName, Type... typeArguments) { + return new ParameterizedType(new ClassType(simpleName, null), Arrays.asList(typeArguments), this); } @Override - public void appendToSignature(StringBuilder signature) { - if (declaringClassType != null) { - // Append the declaring class and remove the last semicolon - declaringClassType.appendToSignature(signature); - signature.deleteCharAt(signature.length() - 1); - signature.append('.'); + void appendToSignature(StringBuilder signature) { + if (owner != null) { + // Append the owner class and replace the last semicolon with a period + owner.appendToSignature(signature); + signature.setCharAt(signature.length() - 1, '.'); } else { signature.append('L'); } - signature.append(classType.name); + signature.append(genericClass.name); if (!typeArguments.isEmpty()) { signature.append('<'); - for (Type argument : typeArguments) { - signature.append(argument.toSignature()); + for (Type typeArgument : typeArguments) { + typeArgument.appendToSignature(signature); } signature.append('>'); } @@ -314,151 +395,94 @@ public void appendToSignature(StringBuilder signature) { } @Override - public boolean isParameterizedType() { + boolean isParameterizedType() { return true; } @Override - public ParameterizedType asParameterizedType() { + ParameterizedType asParameterizedType() { return this; } - /** - * Build a signature like Lorg/acme/Parent.Inner;. - * - * @param simpleName - * @return the nested class - */ - public ClassType nestedClassType(String simpleName) { - return new ClassType(simpleName, this); - } - - /** - * Build a signature like Lorg/acme/Parent.Inner;. - * - * @param simpleName - * @return the nested class - */ - public ParameterizedType nestedParameterizedType(String simpleName, Type... typeArguments) { - return new ParameterizedType(Type.classType(simpleName), Arrays.asList(typeArguments), this); + List getTypeArguments() { + return Collections.unmodifiableList(typeArguments); } - } - public static class ClassType implements Type { - - public static ClassType OBJECT = classType(DotName.OBJECT_NAME); - - final String name; - final Type declaringClassType; + public static final class TypeVariable extends Type { + private final String name; + private final Type firstBound; // may be null if all bounds are interfaces + private final List nextBounds; - ClassType(String name, Type declaringClassType) { + TypeVariable(String name, Type firstBound, List nextBounds) { this.name = Objects.requireNonNull(name); - this.declaringClassType = declaringClassType; + this.firstBound = firstBound; + this.nextBounds = Objects.requireNonNull(nextBounds); } @Override - public void appendToSignature(StringBuilder signature) { - if (declaringClassType != null) { - // Append the declaring class and remove the last semicolon - declaringClassType.appendToSignature(signature); - signature.deleteCharAt(signature.length() - 1); - signature.append('.'); - } else { - signature.append('L'); + void appendToSignature(StringBuilder signature) { + signature.append('T').append(name).append(';'); + } + + void appendTypeParameterToSignature(StringBuilder signature) { + signature.append(name).append(":"); + if (firstBound != null) { + firstBound.appendToSignature(signature); + } + for (Type bound : nextBounds) { + signature.append(":"); + bound.appendToSignature(signature); } - signature.append(name).append(';'); } @Override - public boolean isClass() { + boolean isTypeVariable() { return true; } @Override - public ClassType asClass() { + TypeVariable asTypeVariable() { return this; } - } - public static class VoidType implements Type { + public static final class WildcardType extends Type { + private final Type upperBound; + private final Type lowerBound; - public static final VoidType INSTANCE = new VoidType(); - - @Override - public void appendToSignature(StringBuilder signature) { - signature.append("V"); + WildcardType(Type upperBound, Type lowerBound) { + if (upperBound == null && lowerBound == null) { + throw new NullPointerException(); + } + if (upperBound != null && lowerBound != null) { + throw new IllegalArgumentException(); + } + this.upperBound = upperBound; + this.lowerBound = lowerBound; } @Override - public boolean isVoid() { + boolean isWildcard() { return true; } @Override - public VoidType asVoid() { + WildcardType asWildcard() { return this; } - } - - public static class PrimitiveType implements Type { - - public static final PrimitiveType BYTE = new PrimitiveType(Primitive.BYTE); - public static final PrimitiveType CHAR = new PrimitiveType(Primitive.CHAR); - public static final PrimitiveType DOUBLE = new PrimitiveType(Primitive.DOUBLE); - public static final PrimitiveType FLOAT = new PrimitiveType(Primitive.FLOAT); - public static final PrimitiveType INT = new PrimitiveType(Primitive.INT); - public static final PrimitiveType LONG = new PrimitiveType(Primitive.LONG); - public static final PrimitiveType SHORT = new PrimitiveType(Primitive.SHORT); - public static final PrimitiveType BOOLEAN = new PrimitiveType(Primitive.BOOLEAN); - - final Primitive primitive; - - PrimitiveType(Primitive primitive) { - this.primitive = Objects.requireNonNull(primitive); - } - - @Override - public void appendToSignature(StringBuilder signature) { - signature.append(toSignature()); - } - @Override - public String toSignature() { - switch (primitive) { - case BOOLEAN: - return "Z"; - case BYTE: - return "B"; - case CHAR: - return "C"; - case DOUBLE: - return "D"; - case FLOAT: - return "F"; - case INT: - return "I"; - case LONG: - return "J"; - case SHORT: - return "S"; - default: - throw new IllegalStateException(); + void appendToSignature(StringBuilder signature) { + if (lowerBound != null) { + signature.append('-'); + lowerBound.appendToSignature(signature); + } else if (upperBound.isClass() && upperBound.asClass().name.equals(ClassType.OBJECT.name)) { + signature.append('*'); + } else { + signature.append('+'); + upperBound.appendToSignature(signature); } } - - @Override - public boolean isPrimitive() { - return true; - } - - @Override - public PrimitiveType asPrimitive() { - return this; - } - } - } diff --git a/src/test/java/io/quarkus/gizmo/SignaturesTest.java b/src/test/java/io/quarkus/gizmo/SignaturesTest.java index 8e4b3c0..ecf501d 100644 --- a/src/test/java/io/quarkus/gizmo/SignaturesTest.java +++ b/src/test/java/io/quarkus/gizmo/SignaturesTest.java @@ -23,52 +23,73 @@ public void testMethodSignatures() { // void test(long l) assertEquals("(J)V", - SignatureBuilder.forMethod().addParameter(Type.longType()).build()); + SignatureBuilder.forMethod().addParameterType(Type.longType()).build()); // List test(List list) assertEquals("(Ljava/util/List<*>;)Ljava/util/List;", SignatureBuilder.forMethod() .setReturnType(Type.parameterizedType(Type.classType(List.class), Type.classType(String.class))) - .addParameter(Type.parameterizedType(Type.classType(List.class), Type.wildcardTypeUnbounded())) + .addParameterType(Type.parameterizedType(Type.classType(List.class), Type.wildcardTypeUnbounded())) .build()); // Object test() assertEquals("()Ljava/lang/Object;", SignatureBuilder.forMethod().setReturnType(Type.classType(DotName.OBJECT_NAME)).build()); - // > String[] test(T t) + // > String[] test(T t) assertEquals(";>(TT;)[Ljava/lang/String;", SignatureBuilder.forMethod() - .setReturnType(Type.arrayType(Type.classType(String.class))) - .addParameter(Type.typeVariable("T")) .addTypeParameter(Type.typeVariable("T", null, Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T")))) + .setReturnType(Type.arrayType(Type.classType(String.class))) + .addParameterType(Type.typeVariable("T")) .build()); // List test(int a, T t) - assertEquals("(ITT;)Ljava/util/List;", + assertEquals("(ITT;)Ljava/util/List;", SignatureBuilder.forMethod() + .addTypeParameter(Type.typeVariable("R")) .setReturnType( Type.parameterizedType(Type.classType(DotName.createSimple(List.class)), Type.typeVariable("R"))) - .addParameter(Type.intType()) - .addParameter(Type.typeVariable("T")).build()); + .addParameterType(Type.intType()) + .addParameterType(Type.typeVariable("T")) + .build()); + + // List test(int a, T t) + assertEquals("(ITT;)Ljava/util/List;", + SignatureBuilder.forMethod() + .addTypeParameter(Type.typeVariable("R")) + .addTypeParameter(Type.typeVariable("S", Type.typeVariable("R"))) + .setReturnType( + Type.parameterizedType(Type.classType(DotName.createSimple(List.class)), + Type.typeVariable("S"))) + .addParameterType(Type.intType()) + .addParameterType(Type.typeVariable("T")) + .build()); + + // > List test(int a, T t) + assertEquals(";>(ITT;)Ljava/util/List;", + SignatureBuilder.forMethod() + .addTypeParameter(Type.typeVariable("R", null, + Type.classType(Serializable.class), + Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("R")))) + .setReturnType( + Type.parameterizedType(Type.classType(List.class), Type.typeVariable("R"))) + .addParameterType(Type.intType()) + .addParameterType(Type.typeVariable("T")) + .build()); // boolean test(int i) assertEquals("(I)Z", SignatureBuilder.forMethod() .setReturnType(Type.booleanType()) - .addParameter(Type.intType()).build()); + .addParameterType(Type.intType()).build()); // , U extends Comparable, V extends Exception> T bbb(U arg, W arg2, OuterParam self) assertEquals( ";U::Ljava/lang/Comparable;V:Ljava/lang/Exception;>(TU;TW;Ltest/OuterParam;)TT;", SignatureBuilder.forMethod() - .setReturnType(Type.typeVariable("T")) - .addParameter(Type.typeVariable("U")) - .addParameter(Type.typeVariable("W")) - .addParameter(Type.parameterizedType(Type.classType("test/OuterParam"), - Type.typeVariable("W"))) .addTypeParameter(Type.typeVariable("T", Type.classType(Number.class), Type.parameterizedType( Type.classType(Comparable.class), @@ -76,24 +97,29 @@ public void testMethodSignatures() { .addTypeParameter(Type.typeVariable("U", null, Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("U")))) - .addTypeParameter(Type.typeVariable("V", Type.classType(Exception.class))).build()); + .addTypeParameter(Type.typeVariable("V", Type.classType(Exception.class))) + .setReturnType(Type.typeVariable("T")) + .addParameterType(Type.typeVariable("U")) + .addParameterType(Type.typeVariable("W")) + .addParameterType(Type.parameterizedType(Type.classType("test/OuterParam"), + Type.typeVariable("W"))) + .build()); - // , U extends Comparable, V extends Exception> T test(List arg, W arg2, Foo arg3) throws IllegalArgumentException, V + // , U extends Comparable, V extends Exception> T test(List arg, W arg2, NestedParam

.Inner arg3) throws IllegalArgumentException, V assertEquals( ";U::Ljava/lang/Comparable;V:Ljava/lang/Exception;>(Ljava/util/List<+TU;>;TW;Lio/quarkus/gizmo/SignaturesTest$NestedParam.Inner;)TT;^Ljava/lang/IllegalArgumentException;^TV;", SignatureBuilder.forMethod() - .setReturnType(Type.typeVariable("T")) .addTypeParameter(Type.typeVariable("T", Type.classType(Number.class), Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T")))) .addTypeParameter(Type.typeVariable("U", null, Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("U")))) .addTypeParameter(Type.typeVariable("V", Type.classType(Exception.class))) - .addParameter(Type.parameterizedType(Type.classType(List.class), + .setReturnType(Type.typeVariable("T")) + .addParameterType(Type.parameterizedType(Type.classType(List.class), Type.wildcardTypeWithUpperBound(Type.typeVariable("U")))) - .addParameter(Type.typeVariable("W")) - .addParameter(Type.parameterizedType(Type.classType(NestedParam.class), Type.typeVariable("P")) - .nestedClassType( - NestedParam.Inner.class.getSimpleName())) + .addParameterType(Type.typeVariable("W")) + .addParameterType(Type.parameterizedType(Type.classType(NestedParam.class), Type.typeVariable("P")) + .innerClass(NestedParam.Inner.class.getSimpleName())) .addException(Type.classType(IllegalArgumentException.class)) .addException(Type.typeVariable("V")) .build()); @@ -164,7 +190,7 @@ public void testFieldSignatures() { assertEquals("Lio/quarkus/gizmo/SignaturesTest$NestedParam.InnerParam;", SignatureBuilder.forField() .setType(Type.parameterizedType(Type.classType(NestedParam.class), Type.typeVariable("P")) - .nestedParameterizedType(InnerParam.class.getSimpleName(), Type.typeVariable("P"))) + .innerParameterizedType(InnerParam.class.getSimpleName(), Type.typeVariable("P"))) .build()); } @@ -192,6 +218,34 @@ public void testClassSignatures() { .addSuperInterface(Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T"))) .build()); + // public class OuterParam { + // public interface NestedParam { + // } + // + // public class InnerParam { + // public class InnerInnerRaw { + // public class InnerInnerInnerParam { + // public class Test + // extends OuterParam.InnerParam.InnerInnerRaw.InnerInnerInnerParam + // implements NestedParam { + // } + // } + // } + // } + // } + assertEquals("Lio/quarkus/gizmo/test/OuterParam.InnerParam.InnerInnerRaw.InnerInnerInnerParam;Lio/quarkus/gizmo/test/OuterParam$NestedParam;", + SignatureBuilder.forClass() + .addTypeParameter(Type.typeVariable("X", Type.classType(String.class))) + .addTypeParameter(Type.typeVariable("Y", Type.classType(Integer.class))) + .setSuperClass( + Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam"), Type.typeVariable("X")) + .innerParameterizedType("InnerParam", Type.typeVariable("Y")) + .innerClass("InnerInnerRaw") + .innerParameterizedType("InnerInnerInnerParam", Type.classType(String.class)) + ) + .addSuperInterface(Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam$NestedParam"), Type.typeVariable("V"))) + .build()); + try { SignatureBuilder.forClass() .setSuperClass(Type.parameterizedType(Type.classType(List.class), Type.wildcardTypeUnbounded())); From 5a28fa8bf282671a87f19f14677e43dbe64abe22 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 10 Jan 2023 16:21:51 +0100 Subject: [PATCH 3/5] ClassSignatureBuilder - rename addSuperInterface() to addInterface() --- .../java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java | 4 ++-- src/main/java/io/quarkus/gizmo/SignatureBuilder.java | 4 ++-- src/test/java/io/quarkus/gizmo/SignaturesTest.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java index ef21694..2a5bca8 100644 --- a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java +++ b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java @@ -61,13 +61,13 @@ public ClassSignatureBuilder setSuperClass(ParameterizedType superClass) { } @Override - public ClassSignatureBuilder addSuperInterface(ClassType interfaceType) { + public ClassSignatureBuilder addInterface(ClassType interfaceType) { superInterfaces.add(interfaceType); return this; } @Override - public ClassSignatureBuilder addSuperInterface(ParameterizedType interfaceType) { + public ClassSignatureBuilder addInterface(ParameterizedType interfaceType) { if (containsWildcard(interfaceType)) { throw new IllegalArgumentException("A super type may not specify a wilcard"); } diff --git a/src/main/java/io/quarkus/gizmo/SignatureBuilder.java b/src/main/java/io/quarkus/gizmo/SignatureBuilder.java index d90f3c2..7e09fbe 100644 --- a/src/main/java/io/quarkus/gizmo/SignatureBuilder.java +++ b/src/main/java/io/quarkus/gizmo/SignatureBuilder.java @@ -37,9 +37,9 @@ interface ClassSignatureBuilder extends SignatureBuilder { ClassSignatureBuilder setSuperClass(ParameterizedType superClass); - ClassSignatureBuilder addSuperInterface(ClassType interfaceType); + ClassSignatureBuilder addInterface(ClassType interfaceType); - ClassSignatureBuilder addSuperInterface(ParameterizedType interfaceType); + ClassSignatureBuilder addInterface(ParameterizedType interfaceType); } /** diff --git a/src/test/java/io/quarkus/gizmo/SignaturesTest.java b/src/test/java/io/quarkus/gizmo/SignaturesTest.java index ecf501d..ddcffd8 100644 --- a/src/test/java/io/quarkus/gizmo/SignaturesTest.java +++ b/src/test/java/io/quarkus/gizmo/SignaturesTest.java @@ -214,8 +214,8 @@ public void testClassSignatures() { SignatureBuilder.forClass() .addTypeParameter(Type.typeVariable("T")) .setSuperClass(Type.parameterizedType(Type.classType(List.class), Type.typeVariable("T"))) - .addSuperInterface(Type.classType(Serializable.class)) - .addSuperInterface(Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T"))) + .addInterface(Type.classType(Serializable.class)) + .addInterface(Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T"))) .build()); // public class OuterParam { @@ -243,7 +243,7 @@ public void testClassSignatures() { .innerClass("InnerInnerRaw") .innerParameterizedType("InnerInnerInnerParam", Type.classType(String.class)) ) - .addSuperInterface(Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam$NestedParam"), Type.typeVariable("V"))) + .addInterface(Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam$NestedParam"), Type.typeVariable("V"))) .build()); try { From b34c2a284e8a7683db52946c040c6dfd9e48f452 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Tue, 10 Jan 2023 16:46:30 +0100 Subject: [PATCH 4/5] Add ClassCreator.Builder#signature() that extracts superclass/intefaces --- .../java/io/quarkus/gizmo/ClassCreator.java | 56 ++++++++++++++++--- .../gizmo/ClassSignatureBuilderImpl.java | 7 ++- src/main/java/io/quarkus/gizmo/Type.java | 11 ++-- .../java/io/quarkus/gizmo/SignaturesTest.java | 29 ++++++++-- 4 files changed, 81 insertions(+), 22 deletions(-) diff --git a/src/main/java/io/quarkus/gizmo/ClassCreator.java b/src/main/java/io/quarkus/gizmo/ClassCreator.java index 38c4dd3..4eb2daa 100644 --- a/src/main/java/io/quarkus/gizmo/ClassCreator.java +++ b/src/main/java/io/quarkus/gizmo/ClassCreator.java @@ -42,6 +42,8 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; +import io.quarkus.gizmo.SignatureBuilder.ClassSignatureBuilder; + public class ClassCreator implements AutoCloseable, AnnotatedElement, SignatureElement { public static Builder builder() { @@ -65,7 +67,8 @@ public static Builder interfaceBuilder() { private final Map superclassAccessors = new LinkedHashMap<>(); private final AtomicInteger accessorCount = new AtomicInteger(); - ClassCreator(BytecodeCreatorImpl enclosing, ClassOutput classOutput, String name, String signature, String superClass, int access, String... interfaces) { + ClassCreator(BytecodeCreatorImpl enclosing, ClassOutput classOutput, String name, String signature, String superClass, + int access, String... interfaces) { this.enclosing = enclosing; this.classOutput = classOutput; this.superClass = superClass.replace('.', '/'); @@ -159,7 +162,8 @@ MethodDescriptor getSupertypeAccessor(MethodDescriptor descriptor, String supert for (int i = 0; i < params.length; ++i) { params[i] = ctor.getMethodParam(i); } - MethodDescriptor superDescriptor = MethodDescriptor.ofMethod(supertype, descriptor.getName(), descriptor.getReturnType(), descriptor.getParameterTypes()); + MethodDescriptor superDescriptor = MethodDescriptor.ofMethod(supertype, descriptor.getName(), + descriptor.getReturnType(), descriptor.getParameterTypes()); ResultHandle ret; if (isInterface) { ret = ctor.invokeSpecialInterfaceMethod(superDescriptor, ctor.getThis(), params); @@ -183,7 +187,7 @@ public void writeTo(ClassOutput classOutput) { Writer sourceWriter = classOutput.getSourceWriter(className); ClassVisitor cv; if (sourceWriter != null) { - cv = new GizmoClassVisitor(Gizmo.ASM_API_VERSION, file, sourceWriter); + cv = new GizmoClassVisitor(Gizmo.ASM_API_VERSION, file, sourceWriter); } else { cv = file; } @@ -202,7 +206,7 @@ public void writeTo(ClassOutput classOutput) { if (requiresCtor) { // constructor if (cv instanceof GizmoClassVisitor) { - ((GizmoClassVisitor)cv).append("// Auto-generated constructor").newLine(); + ((GizmoClassVisitor) cv).append("// Auto-generated constructor").newLine(); } MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, MethodDescriptor.INIT, "()V", null, null); mv.visitVarInsn(ALOAD, 0); // push `this` to the operand stack @@ -220,9 +224,10 @@ public void writeTo(ClassOutput classOutput) { for (Map.Entry method : methods.entrySet()) { method.getValue().write(cv); } - for(AnnotationCreatorImpl annotation : annotations) { - AnnotationVisitor av = cv.visitAnnotation(DescriptorUtils.extToInt(annotation.getAnnotationType()), annotation.getRetentionPolicy() == RetentionPolicy.RUNTIME); - for(Map.Entry e : annotation.getValues().entrySet()) { + for (AnnotationCreatorImpl annotation : annotations) { + AnnotationVisitor av = cv.visitAnnotation(DescriptorUtils.extToInt(annotation.getAnnotationType()), + annotation.getRetentionPolicy() == RetentionPolicy.RUNTIME); + for (Map.Entry e : annotation.getValues().entrySet()) { AnnotationUtils.visitAnnotationValue(av, e.getKey(), e.getValue()); } av.visitEnd(); @@ -234,7 +239,7 @@ public void writeTo(ClassOutput classOutput) { } /** - * Finish the class creator. If a class output was configured for this class creator, the class bytes + * Finish the class creator. If a class output was configured for this class creator, the class bytes * will immediately be written there. */ @Override @@ -317,6 +322,30 @@ public Builder signature(String signature) { return this; } + /** + * The raw types of the superclass and superinterfaces are extracted and passed to {@link #superClass(String)} and + * {@link #interfaces(String...)} respectively. + * + * @param signatureBuilder + * @return self + */ + public Builder signature(ClassSignatureBuilder signatureBuilder) { + ClassSignatureBuilderImpl signatureBuilderImpl = (ClassSignatureBuilderImpl) signatureBuilder; + Type superClass = signatureBuilderImpl.superClass; + if (superClass != null) { + superClass(getRawType(superClass)); + } + if (!signatureBuilderImpl.superInterfaces.isEmpty()) { + String[] interfaces = new String[signatureBuilderImpl.superInterfaces.size()]; + int idx = 0; + for (Type superInterface : signatureBuilderImpl.superInterfaces) { + interfaces[idx++] = getRawType(superInterface); + } + interfaces(interfaces); + } + return signature(signatureBuilder.build()); + } + public Builder superClass(String superClass) { if ((access & ACC_INTERFACE) != 0 && !"java.lang.Object".equals(superClass) @@ -360,7 +389,16 @@ public Builder interfaces(Class... interfaces) { public ClassCreator build() { Objects.requireNonNull(className); Objects.requireNonNull(superClass); - return new ClassCreator(enclosing, classOutput, className, signature, superClass, access, interfaces.toArray(new String[0])); + return new ClassCreator(enclosing, classOutput, className, signature, superClass, access, + interfaces.toArray(new String[0])); + } + + private String getRawType(Type type) { + if (type.isClass()) { + return type.asClass().name; + } else { + return type.asParameterizedType().genericClass.name; + } } } diff --git a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java index 2a5bca8..7d20248 100644 --- a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java +++ b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java @@ -9,9 +9,10 @@ import io.quarkus.gizmo.Type.TypeVariable; class ClassSignatureBuilderImpl implements ClassSignatureBuilder { - private List typeParameters = new ArrayList<>(); - private Type superClass = ClassType.OBJECT; - private List superInterfaces = new ArrayList<>(); + + List typeParameters = new ArrayList<>(); + Type superClass = ClassType.OBJECT; + List superInterfaces = new ArrayList<>(); @Override public String build() { diff --git a/src/main/java/io/quarkus/gizmo/Type.java b/src/main/java/io/quarkus/gizmo/Type.java index b593f0e..d439fb7 100644 --- a/src/main/java/io/quarkus/gizmo/Type.java +++ b/src/main/java/io/quarkus/gizmo/Type.java @@ -260,8 +260,8 @@ PrimitiveType asPrimitive() { public static final class ClassType extends Type { public static final ClassType OBJECT = new ClassType("java/lang/Object", null); - private final String name; // always slash-delimited - private final Type owner; + final String name; // always slash-delimited + final Type owner; ClassType(String name, Type owner) { this.name = Objects.requireNonNull(name); @@ -344,9 +344,10 @@ ArrayType asArray() { } public static final class ParameterizedType extends Type { - private final ClassType genericClass; - private final List typeArguments; - private final Type owner; + + final ClassType genericClass; + final List typeArguments; + final Type owner; ParameterizedType(ClassType genericClass, List typeArguments, Type owner) { this.genericClass = Objects.requireNonNull(genericClass); diff --git a/src/test/java/io/quarkus/gizmo/SignaturesTest.java b/src/test/java/io/quarkus/gizmo/SignaturesTest.java index ddcffd8..4ba0c6a 100644 --- a/src/test/java/io/quarkus/gizmo/SignaturesTest.java +++ b/src/test/java/io/quarkus/gizmo/SignaturesTest.java @@ -10,6 +10,7 @@ import org.jboss.jandex.DotName; import org.junit.Test; +import io.quarkus.gizmo.SignatureBuilder.ClassSignatureBuilder; import io.quarkus.gizmo.SignaturesTest.Nested.Inner.Inner2; import io.quarkus.gizmo.SignaturesTest.NestedParam.InnerParam; @@ -233,17 +234,19 @@ public void testClassSignatures() { // } // } // } - assertEquals("Lio/quarkus/gizmo/test/OuterParam.InnerParam.InnerInnerRaw.InnerInnerInnerParam;Lio/quarkus/gizmo/test/OuterParam$NestedParam;", + assertEquals( + "Lio/quarkus/gizmo/test/OuterParam.InnerParam.InnerInnerRaw.InnerInnerInnerParam;Lio/quarkus/gizmo/test/OuterParam$NestedParam;", SignatureBuilder.forClass() .addTypeParameter(Type.typeVariable("X", Type.classType(String.class))) .addTypeParameter(Type.typeVariable("Y", Type.classType(Integer.class))) .setSuperClass( - Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam"), Type.typeVariable("X")) + Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam"), + Type.typeVariable("X")) .innerParameterizedType("InnerParam", Type.typeVariable("Y")) .innerClass("InnerInnerRaw") - .innerParameterizedType("InnerInnerInnerParam", Type.classType(String.class)) - ) - .addInterface(Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam$NestedParam"), Type.typeVariable("V"))) + .innerParameterizedType("InnerInnerInnerParam", Type.classType(String.class))) + .addInterface(Type.parameterizedType(Type.classType("io.quarkus.gizmo.test.OuterParam$NestedParam"), + Type.typeVariable("V"))) .build()); try { @@ -254,6 +257,22 @@ public void testClassSignatures() { } } + @Test + public void testClassCreatorSignatureBuilder() { + // class Foo extends List implements Serializable, Comparable + ClassSignatureBuilder classSignature = SignatureBuilder.forClass() + .addTypeParameter(Type.typeVariable("T")) + .setSuperClass(Type.parameterizedType(Type.classType(List.class), Type.typeVariable("T"))) + .addInterface(Type.classType(Serializable.class)) + .addInterface(Type.parameterizedType(Type.classType(Comparable.class), Type.typeVariable("T"))); + + ClassCreator creator = ClassCreator.builder().signature(classSignature).className("org.acme.Foo").build(); + assertEquals("java/util/List", creator.getSuperClass()); + assertEquals(2, creator.getInterfaces().length); + assertEquals("java/io/Serializable", creator.getInterfaces()[0]); + assertEquals("java/lang/Comparable", creator.getInterfaces()[1]); + } + public static class Nested { public class Inner { From ad229ec41440a044070bd40b26fb347a2b481a05 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Wed, 11 Jan 2023 11:06:42 +0100 Subject: [PATCH 5/5] ClassSignatureBuilder - improve the error message for invalid wildcards --- .../java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java index 7d20248..0b3e48a 100644 --- a/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java +++ b/src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java @@ -9,7 +9,7 @@ import io.quarkus.gizmo.Type.TypeVariable; class ClassSignatureBuilderImpl implements ClassSignatureBuilder { - + List typeParameters = new ArrayList<>(); Type superClass = ClassType.OBJECT; List superInterfaces = new ArrayList<>(); @@ -54,7 +54,7 @@ public ClassSignatureBuilder setSuperClass(ClassType superClass) { @Override public ClassSignatureBuilder setSuperClass(ParameterizedType superClass) { if (containsWildcard(superClass)) { - throw new IllegalArgumentException("A super type may not specify a wilcard"); + throw new IllegalArgumentException("An extended class type may not specify a wildcard"); } this.superClass = superClass; @@ -70,7 +70,7 @@ public ClassSignatureBuilder addInterface(ClassType interfaceType) { @Override public ClassSignatureBuilder addInterface(ParameterizedType interfaceType) { if (containsWildcard(interfaceType)) { - throw new IllegalArgumentException("A super type may not specify a wilcard"); + throw new IllegalArgumentException("An implemented interface type may not specify a wildcard"); } superInterfaces.add(interfaceType); return this;