![JAR search and dependency download from the Maven repository](/logo.png)
org.microbean.lang.bytebuddy.ByteBuddy2 Maven / Gradle / Ivy
/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
*
* Copyright © 2023 microBean™.
*
* 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 org.microbean.lang.bytebuddy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.pool.TypePool;
import org.microbean.lang.CompletionLock;
import static net.bytebuddy.description.type.TypeDescription.Generic.Builder;
import static org.microbean.lang.Lang.generic;
public final class ByteBuddy2 {
private final TypePool typePool;
public ByteBuddy2(final TypePool typePool) {
super();
this.typePool = Objects.requireNonNull(typePool, "typePool");
}
public final TypePool typePool() {
return this.typePool;
}
public final TypeDescription typeDescription(final TypeMirror t) {
CompletionLock.acquire();
try {
return switch (t.getKind()) {
case NONE -> null;
case BOOLEAN -> TypeDefinition.Sort.describe(boolean.class).asErasure();
case BYTE -> TypeDefinition.Sort.describe(byte.class).asErasure();
case CHAR -> TypeDefinition.Sort.describe(char.class).asErasure();
case DOUBLE -> TypeDefinition.Sort.describe(double.class).asErasure();
case FLOAT -> TypeDefinition.Sort.describe(float.class).asErasure();
case INT -> TypeDefinition.Sort.describe(int.class).asErasure();
case LONG -> TypeDefinition.Sort.describe(long.class).asErasure();
case SHORT -> TypeDefinition.Sort.describe(short.class).asErasure();
case VOID -> TypeDefinition.Sort.describe(void.class).asErasure();
case ARRAY -> TypeDescription.ArrayProjection.of(typeDescription(((ArrayType)t).getComponentType()));
case DECLARED -> typeDescription((QualifiedNameable)((DeclaredType)t).asElement());
default -> throw new IllegalArgumentException("t: " + t + "; kind: " + t.getKind());
};
} finally {
CompletionLock.release();
}
}
public final TypeDescription typeDescription(final QualifiedNameable qn) {
CompletionLock.acquire();
try {
return this.typeDescription(qn.getQualifiedName().toString());
} finally {
CompletionLock.release();
}
}
public final TypeDescription typeDescription(final String name) { // TODO: better named name; maybe canonical name following JLS?
return this.typePool.describe(name).resolve();
}
public final TypeDescription.Generic typeDescriptionGeneric(final TypeMirror t) {
CompletionLock.acquire();
try {
return switch (t.getKind()) {
case NONE -> null;
// Primitives are easy. ByteBuddy caches them.
case BOOLEAN -> TypeDefinition.Sort.describe(boolean.class);
case BYTE -> TypeDefinition.Sort.describe(byte.class);
case CHAR -> TypeDefinition.Sort.describe(char.class);
case DOUBLE -> TypeDefinition.Sort.describe(double.class);
case FLOAT -> TypeDefinition.Sort.describe(float.class);
case INT -> TypeDefinition.Sort.describe(int.class);
case LONG -> TypeDefinition.Sort.describe(long.class);
case SHORT -> TypeDefinition.Sort.describe(short.class);
// void is easy. ByteBuddy caches it.
case VOID -> TypeDefinition.Sort.describe(void.class);
// Arrays are easy.
case ARRAY -> Builder.of(typeDescriptionGeneric(((ArrayType)t).getComponentType())).asArray().build();
// Declared types:
case DECLARED -> {
final DeclaredType dt = (DeclaredType)t;
final TypeElement te = (TypeElement)dt.asElement();
final String n = te.getQualifiedName().toString();
yield switch (n) {
// Certain declared types are easy because ByteBuddy also caches them, so the classes are loaded already. We add
// a few simple ones to the list.
case "java.lang.Boolean" -> TypeDefinition.Sort.describe(Boolean.class);
case "java.lang.Byte" -> TypeDefinition.Sort.describe(Byte.class);
case "java.lang.Character" -> TypeDefinition.Sort.describe(Character.class);
case "java.lang.Class" -> TypeDefinition.Sort.describe(Class.class);
case "java.lang.Double" -> TypeDefinition.Sort.describe(Double.class);
case "java.lang.Float" -> TypeDefinition.Sort.describe(Float.class);
case "java.lang.Integer" -> TypeDefinition.Sort.describe(Integer.class);
case "java.lang.Long" -> TypeDefinition.Sort.describe(Long.class);
case "java.lang.Object" -> TypeDefinition.Sort.describe(Object.class);
case "java.lang.Short" -> TypeDefinition.Sort.describe(Short.class);
case "java.lang.String" -> TypeDefinition.Sort.describe(String.class);
case "java.lang.Throwable" -> TypeDefinition.Sort.describe(Throwable.class);
// case "java.lang.Void" -> TypeDefinition.Sort.describe(Void.class); // ByteBuddy doesn't cache this for some reason
case "java.lang.annotation.Annotation" -> TypeDefinition.Sort.describe(java.lang.annotation.Annotation.class);
case "net.bytebuddy.dynamic.TargetType" -> TypeDefinition.Sort.describe(net.bytebuddy.dynamic.TargetType.class);
default -> {
// Some other declared type
final TypeDescription rawType = typeDescription(n);
if (!generic(te)) {
yield rawType.asGenericType();
}
final TypeMirror dtEnclosingType = dt.getEnclosingType();
final TypeDescription.Generic enclosingType =
dtEnclosingType == null ? TypeDescription.Generic.UNDEFINED : typeDescriptionGeneric(dtEnclosingType);
final List extends TypeMirror> typeArgumentMirrors = dt.getTypeArguments();
if (typeArgumentMirrors.isEmpty()) {
yield Builder.parameterizedType(rawType, enclosingType).build();
}
final List typeArguments = new ArrayList<>(typeArgumentMirrors.size());
for (final TypeMirror typeArgumentMirror : typeArgumentMirrors) {
typeArguments.add(typeDescriptionGeneric(typeArgumentMirror));
}
yield Builder.parameterizedType(rawType,
enclosingType == null ? TypeDescription.Generic.UNDEFINED : enclosingType,
typeArguments)
.build();
}
};
}
case TYPEVAR -> Builder.typeVariable(((TypeVariable)t).asElement().getSimpleName().toString()).build();
case WILDCARD -> {
final WildcardType w = (WildcardType)t;
final TypeMirror extendsBound = w.getExtendsBound();
final TypeMirror superBound = w.getSuperBound();
if (superBound == null) {
if (extendsBound == null) {
yield Builder.unboundWildcard();
}
yield Builder.of(typeDescriptionGeneric(extendsBound)).asWildcardUpperBound();
} else if (extendsBound == null) {
yield Builder.of(typeDescriptionGeneric(superBound)).asWildcardLowerBound();
} else {
throw new AssertionError();
}
}
case ERROR, EXECUTABLE, INTERSECTION, MODULE, NULL, OTHER, PACKAGE, UNION -> throw new IllegalArgumentException("t: "
+ t +
"; kind: " +
t.getKind());
};
} finally {
CompletionLock.release();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy