Skip to content

Commit

Permalink
Merge pull request #147 from mkouba/signature-from
Browse files Browse the repository at this point in the history
Introduce SignatureBuilder API
  • Loading branch information
mkouba committed Jan 11, 2023
2 parents 602dc3a + ad229ec commit 3260d3f
Show file tree
Hide file tree
Showing 8 changed files with 1,107 additions and 10 deletions.
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) {
this.superClass = superClass;
return this;
}

@Override
public ClassSignatureBuilder setSuperClass(ParameterizedType superClass) {
if (containsWildcard(superClass)) {
throw new IllegalArgumentException("An extended class type may not specify a wildcard");
}

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("An implemented interface type may not specify a wildcard");
}
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 {
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);
}
}

0 comments on commit 3260d3f

Please sign in to comment.