Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce SignatureBuilder API #147

Merged
merged 5 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 47 additions & 9 deletions src/main/java/io/quarkus/gizmo/ClassCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClassCreator> {

public static Builder builder() {
Expand All @@ -65,7 +67,8 @@ public static Builder interfaceBuilder() {
private final Map<MethodDescriptor, MethodDescriptor> 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('.', '/');
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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
Expand All @@ -220,9 +224,10 @@ public void writeTo(ClassOutput classOutput) {
for (Map.Entry<MethodDescriptor, MethodCreatorImpl> 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<String, Object> e : annotation.getValues().entrySet()) {
for (AnnotationCreatorImpl annotation : annotations) {
AnnotationVisitor av = cv.visitAnnotation(DescriptorUtils.extToInt(annotation.getAnnotationType()),
annotation.getRetentionPolicy() == RetentionPolicy.RUNTIME);
for (Map.Entry<String, Object> e : annotation.getValues().entrySet()) {
AnnotationUtils.visitAnnotationValue(av, e.getKey(), e.getValue());
}
av.visitEnd();
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
}

}
Expand Down
91 changes: 91 additions & 0 deletions src/main/java/io/quarkus/gizmo/ClassSignatureBuilderImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.quarkus.gizmo;

import java.util.ArrayList;
import java.util.List;

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 {

List<TypeVariable> typeParameters = new ArrayList<>();
Type superClass = ClassType.OBJECT;
List<Type> 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
superClass.appendToSignature(signature);

// interfaces
if (!superInterfaces.isEmpty()) {
for (Type superInterface : superInterfaces) {
superInterface.appendToSignature(signature);
}
}
return signature.toString();
}

@Override
public ClassSignatureBuilder addTypeParameter(TypeVariable typeParameter) {
typeParameters.add(typeParameter);
return this;
}

@Override
public ClassSignatureBuilder setSuperClass(ClassType superClass) {
mkouba marked this conversation as resolved.
Show resolved Hide resolved
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 addInterface(ClassType interfaceType) {
superInterfaces.add(interfaceType);
return this;
}

@Override
public ClassSignatureBuilder addInterface(ParameterizedType interfaceType) {
if (containsWildcard(interfaceType)) {
throw new IllegalArgumentException("A super type may not specify a wilcard");
mkouba marked this conversation as resolved.
Show resolved Hide resolved
}
superInterfaces.add(interfaceType);
return this;
}

private boolean containsWildcard(Type type) {
if (type.isWildcard()) {
return true;
} else if (type.isParameterizedType()) {
for (Type typeArgument : type.asParameterizedType().getTypeArguments()) {
if (containsWildcard(typeArgument)) {
return true;
}
}
}
return false;
}
}
22 changes: 22 additions & 0 deletions src/main/java/io/quarkus/gizmo/FieldSignatureBuilderImpl.java
Original file line number Diff line number Diff line change
@@ -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() {
StringBuilder signature = new StringBuilder();
type.appendToSignature(signature);
return signature.toString();
}

@Override
public FieldSignatureBuilder setType(Type type) {
this.type = Objects.requireNonNull(type);
return this;
}
}
81 changes: 81 additions & 0 deletions src/main/java/io/quarkus/gizmo/MethodSignatureBuilderImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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;
import io.quarkus.gizmo.Type.VoidType;

class MethodSignatureBuilderImpl implements MethodSignatureBuilder {
private List<TypeVariable> typeParameters = new ArrayList<>();
private Type returnType = VoidType.INSTANCE;
private List<Type> parameterTypes = new ArrayList<>();
private List<Type> 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('>');
}

// param types
signature.append('(');
for (Type parameterType : parameterTypes) {
parameterType.appendToSignature(signature);
}
signature.append(')');

// return type
returnType.appendToSignature(signature);

// exception types
if (!exceptions.isEmpty()) {
for (Type exceptionType : exceptions) {
signature.append('^');
exceptionType.appendToSignature(signature);
}
}

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 addParameterType(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;
}
}
66 changes: 66 additions & 0 deletions src/main/java/io/quarkus/gizmo/SignatureBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.quarkus.gizmo;

import io.quarkus.gizmo.Type.ClassType;
import io.quarkus.gizmo.Type.ParameterizedType;
import io.quarkus.gizmo.Type.TypeVariable;

/**
* Builds a generic signature as defined in JVMS 17, chapter "4.7.9.1. Signatures".
*
* @see SignatureElement#setSignature(String)
*/
public interface SignatureBuilder {
mkouba marked this conversation as resolved.
Show resolved Hide resolved
static ClassSignatureBuilder forClass() {
return new ClassSignatureBuilderImpl();
}

static MethodSignatureBuilder forMethod() {
return new MethodSignatureBuilderImpl();
}

static FieldSignatureBuilder forField() {
return new FieldSignatureBuilderImpl();
}

/**
* @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);

ClassSignatureBuilder setSuperClass(ParameterizedType superClass);

ClassSignatureBuilder addInterface(ClassType interfaceType);

ClassSignatureBuilder addInterface(ParameterizedType interfaceType);
}

/**
* Builds a generic signature of a method (including constructors).
*/
interface MethodSignatureBuilder extends SignatureBuilder {
MethodSignatureBuilder addTypeParameter(TypeVariable typeParameter);

MethodSignatureBuilder setReturnType(Type returnType);

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);
}
}
Loading