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

Support Parameterized Types in Methods #116

Closed
Closed
Show file tree
Hide file tree
Changes from all 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
80 changes: 67 additions & 13 deletions src/main/java/io/quarkus/gizmo/DescriptorUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ public class DescriptorUtils {
void.class
};

public static String methodTypesToSignature(Object returnType, Object[] params) {
StringBuilder sb = new StringBuilder("(");
for (Object i : params) {
sb.append(objectToSignature(i));
}
sb.append(")");
sb.append(objectToSignature(returnType));
return sb.toString();
}

public static String methodSignatureToDescriptor(String returnType, String... params) {
StringBuilder sb = new StringBuilder("(");
for (String i : params) {
Expand Down Expand Up @@ -80,15 +90,38 @@ public static String classToStringRepresentation(Class<?> c) {
} else if (boolean.class.equals(c)) {
return "Z";
} else if (c.isArray()) {
return c.getName().replace('.', '/');
return normalizeClassName(c.getName());
} else {
return extToInt(c.getName());
}
}

public static String parameterizedClassToStringRepresentation(ParameterizedClass c) {
StringBuilder ret = new StringBuilder();
ret.append("L");
Object wrapperType = c.getType();
if (wrapperType instanceof String) {
ret.append(normalizeClassName((String) wrapperType));
} else if (wrapperType instanceof Class) {
ret.append(normalizeClassName(((Class) wrapperType).getName()));
} else {
throw new IllegalArgumentException("Wrapper type in the Parameterized class must be a Class, ParameterizedClass "
+ "or String, got " + wrapperType);
}

if (c.getParameterTypes().length > 0) {
ret.append("<");
for (Object paramType : c.getParameterTypes()) {
ret.append(objectToDescriptor(paramType));
}
ret.append(">");
}
ret.append(";");
return ret.toString();
}

public static String extToInt(String className) {
String repl = className.replace('.', '/');
return 'L' + repl + ';';
return 'L' + normalizeClassName(className) + ';';
}

public static boolean isPrimitive(String descriptor) {
Expand Down Expand Up @@ -127,7 +160,7 @@ public static String objectToDescriptor(Object param) {
return s; //primitive
}
if (s.startsWith("[")) {
return s.replace('.', '/');
return normalizeClassName(s);
}
if (s.endsWith(";")) {
//jvm internal name
Expand All @@ -138,11 +171,13 @@ public static String objectToDescriptor(Object param) {
return classToStringRepresentation(prim);
}
}
return "L" + s.replace('.', '/') + ';';
return "L" + normalizeClassName(s) + ';';
} else if (param instanceof Class) {
return classToStringRepresentation((Class<?>) param);
} else if (param instanceof ParameterizedClass) {
return objectToDescriptor(((ParameterizedClass) param).getType());
}
throw new IllegalArgumentException("Must be a Class or String, got " + param);
throw new IllegalArgumentException("Must be a Class, ParameterizedClass or String, got " + param);
}

/**
Expand All @@ -159,12 +194,27 @@ public static String[] objectsToDescriptor(Object[] param) {
return ret;
}

/**
* Similar to the {@link #objectToDescriptor(Object)} method but supporting parameterized types.
*
* @param param The param
* @return A descriptor
*/
public static String objectToSignature(Object param) {
if (param instanceof ParameterizedClass) {
return parameterizedClassToStringRepresentation((ParameterizedClass) param);
} else if (param instanceof Type) {
return typeToString((Type) param);
}

return objectToDescriptor(param);
}

public static String objectToInternalClassName(Object param) {
if (param instanceof String) {
String s = (String) param;
return s.replace('.', '/');
return normalizeClassName((String) param);
} else if (param instanceof Class) {
return ((Class) param).getName().replace('.', '/');
return normalizeClassName(((Class) param).getName());
}
throw new IllegalArgumentException("Must be a Class or String, got " + param);
}
Expand Down Expand Up @@ -196,30 +246,34 @@ public static String typeToString(Type type) {
return "V";
} else if (type.kind() == Type.Kind.ARRAY) {
ArrayType array = type.asArrayType();
return array.name().toString().replace('.', '/');
return normalizeClassName(array.name().toString());
} else if (type.kind() == Type.Kind.PARAMETERIZED_TYPE) {
ParameterizedType pt = type.asParameterizedType();
StringBuilder ret = new StringBuilder();
ret.append("L");
ret.append(pt.name().toString().replace('.', '/'));
ret.append(normalizeClassName(pt.name().toString()));
ret.append(";");
return ret.toString();
} else if (type.kind() == Type.Kind.CLASS) {
ClassType pt = type.asClassType();
StringBuilder ret = new StringBuilder();
ret.append("L");
ret.append(pt.name().toString().replace('.', '/'));
ret.append(normalizeClassName(pt.name().toString()));
ret.append(";");
return ret.toString();
} else if (type.kind() == Type.Kind.TYPE_VARIABLE) {
TypeVariable pt = type.asTypeVariable();
StringBuilder ret = new StringBuilder();
ret.append("L");
ret.append(pt.name().toString().replace('.', '/'));
ret.append(normalizeClassName(pt.name().toString()));
ret.append(";");
return ret.toString();
} else {
throw new RuntimeException("Invalid type for descriptor " + type);
}
}

public static String normalizeClassName(String className) {
return className.replace('.', '/');
}
}
1 change: 1 addition & 0 deletions src/main/java/io/quarkus/gizmo/MethodCreatorImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class MethodCreatorImpl extends BytecodeCreatorImpl implements MethodCreator {
this.methodDescriptor = methodDescriptor;
this.declaringClassName = declaringClassName;
this.classCreator = classCreator;
this.signature = DescriptorUtils.methodTypesToSignature(methodDescriptor.getRawReturnType(), methodDescriptor.getRawParameterTypes());
}

@Override
Expand Down
47 changes: 30 additions & 17 deletions src/main/java/io/quarkus/gizmo/MethodDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,66 +21,71 @@
import java.util.Objects;

import org.jboss.jandex.MethodInfo;

import org.jboss.jandex.Type;

public class MethodDescriptor {

private final String declaringClass;
private final String name;
private final String returnType;
private final Object rawReturnType;
private final String[] parameterTypes;
private final Object[] rawParameterTypes;
private final String descriptor;

private MethodDescriptor(String declaringClass, String name, String returnType, String... parameterTypes) {
private MethodDescriptor(String declaringClass, String name, Object returnType, Object... parameterTypes) {
this.declaringClass = declaringClass;
this.name = name;
this.returnType = returnType;
this.parameterTypes = parameterTypes;
this.descriptor = DescriptorUtils.methodSignatureToDescriptor(returnType, parameterTypes);
for (String p : parameterTypes) {
this.rawReturnType = returnType;
this.rawParameterTypes = parameterTypes;
// Return type does not support parameterized types
this.returnType = DescriptorUtils.objectToDescriptor(returnType);
this.parameterTypes = DescriptorUtils.objectsToDescriptor(parameterTypes);
this.descriptor = DescriptorUtils.methodSignatureToDescriptor(this.returnType, this.parameterTypes);
for (String p : this.parameterTypes) {
if (p.length() != 1) {
if (!(p.startsWith("L") && p.endsWith(";") || p.startsWith("["))) {
throw new IllegalArgumentException("Invalid parameter type " + p + " it must be in the JVM descriptor format");
}
}
}
if (returnType.length() != 1) {
if (!(returnType.startsWith("L") && returnType.endsWith(";") || returnType.startsWith("["))) {
throw new IllegalArgumentException("Invalid return type " + returnType + " it must be in the JVM descriptor format");
if (this.returnType.length() != 1) {
if (!(this.returnType.startsWith("L") && this.returnType.endsWith(";") || this.returnType.startsWith("["))) {
throw new IllegalArgumentException("Invalid return type " + this.returnType + " it must be in the JVM descriptor format");
}
}
}

private MethodDescriptor(MethodInfo info) {
this.name = info.name();
this.rawReturnType = info.returnType();
this.returnType = DescriptorUtils.typeToString(info.returnType());
this.rawParameterTypes = new Object[info.parameters().size()];
String[] paramTypes = new String[info.parameters().size()];
for (int i = 0; i < paramTypes.length; ++i) {
paramTypes[i] = DescriptorUtils.typeToString(info.parameters().get(i));
Type paramType = info.parameters().get(i);
paramTypes[i] = DescriptorUtils.typeToString(paramType);
this.rawParameterTypes[i] = paramType;
}
this.parameterTypes = paramTypes;
this.declaringClass = info.declaringClass().toString().replace('.', '/');
this.descriptor = DescriptorUtils.methodSignatureToDescriptor(returnType, parameterTypes);
}

public static MethodDescriptor ofMethod(String declaringClass, String name, String returnType, String... parameterTypes) {
return new MethodDescriptor(DescriptorUtils.objectToInternalClassName(declaringClass), name, DescriptorUtils.objectToDescriptor(returnType), DescriptorUtils.objectsToDescriptor(parameterTypes));
return new MethodDescriptor(DescriptorUtils.objectToInternalClassName(declaringClass), name, returnType, parameterTypes);
}

public static MethodDescriptor ofMethod(Class<?> declaringClass, String name, Class<?> returnType, Class<?>... parameterTypes) {
String[] args = new String[parameterTypes.length];
for (int i = 0; i < args.length; ++i) {
args[i] = DescriptorUtils.classToStringRepresentation(parameterTypes[i]);
}
return new MethodDescriptor(DescriptorUtils.objectToInternalClassName(declaringClass), name, DescriptorUtils.classToStringRepresentation(returnType), args);
return new MethodDescriptor(DescriptorUtils.objectToInternalClassName(declaringClass), name, returnType, parameterTypes);
}

public static MethodDescriptor ofMethod(Method method) {
return ofMethod(method.getDeclaringClass(), method.getName(), method.getReturnType(), method.getParameterTypes());
}

public static MethodDescriptor ofMethod(Object declaringClass, String name, Object returnType, Object... parameterTypes) {
return new MethodDescriptor(DescriptorUtils.objectToInternalClassName(declaringClass), name, DescriptorUtils.objectToDescriptor(returnType), DescriptorUtils.objectsToDescriptor(parameterTypes));
return new MethodDescriptor(DescriptorUtils.objectToInternalClassName(declaringClass), name, returnType, parameterTypes);
}

public static MethodDescriptor ofConstructor(String declaringClass, String... parameterTypes) {
Expand All @@ -107,10 +112,18 @@ public String getReturnType() {
return returnType;
}

public Object getRawReturnType() {
return rawReturnType;
}

public String[] getParameterTypes() {
return parameterTypes;
}

public Object[] getRawParameterTypes() {
return rawParameterTypes;
}

public String getDeclaringClass() {
return declaringClass;
}
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/io/quarkus/gizmo/ParameterizedClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.quarkus.gizmo;

/**
* Wrapper class to represent a parameterized class with its param types.
*/
public class ParameterizedClass {
private final Object type;
private final Object[] parameterTypes;

public ParameterizedClass(Object type, Object... parameterTypes) {
this.type = type;
this.parameterTypes = parameterTypes;
}

public Object getType() {
return type;
}

public Object[] getParameterTypes() {
return parameterTypes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.quarkus.gizmo;

import java.util.List;

import org.junit.Assert;
import org.junit.Test;

public class MethodWithParameterizedReturnTypeTestCase {

@Test
public void testMethodWithParamType() {
try (ClassCreator creator = ClassCreator.builder().className("com.MyTest").build()) {
MethodCreator method = creator.getMethodCreator("m", new ParameterizedClass(List.class, String.class));

Assert.assertEquals("()Ljava/util/List<Ljava/lang/String;>;", method.getSignature());
}
}
}