From e5cc41086cc9895a9b1dba1f4f17cb77786c0a90 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Wed, 29 Jan 2025 20:54:15 +0100 Subject: [PATCH] Add dynamic constant annotation. --- .../bind/annotation/DynamicConstant.java | 195 ++++++++++++++++++ .../TargetMethodAnnotationDrivenBinder.java | 1 + 2 files changed, 196 insertions(+) create mode 100644 byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/DynamicConstant.java diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/DynamicConstant.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/DynamicConstant.java new file mode 100644 index 0000000000..b20151cd1d --- /dev/null +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/DynamicConstant.java @@ -0,0 +1,195 @@ +package net.bytebuddy.implementation.bind.annotation; + +import net.bytebuddy.description.annotation.AnnotationDescription; +import net.bytebuddy.description.enumeration.EnumerationDescription; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.method.MethodList; +import net.bytebuddy.description.method.ParameterDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.Implementation; +import net.bytebuddy.implementation.bind.MethodDelegationBinder; +import net.bytebuddy.implementation.bytecode.assign.Assigner; +import net.bytebuddy.implementation.bytecode.constant.JavaConstantValue; +import net.bytebuddy.implementation.bytecode.member.Invokedynamic; +import net.bytebuddy.utility.JavaConstant; + +import java.lang.annotation.*; +import java.util.Arrays; +import java.util.Collections; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +/** + *

+ * Binds a dynamic constant to the annotated parameter. The constant is either bound by using constantdynamic + * or invokedynamic. + *

+ *

+ * Important: Don't confuse this annotation with {@link net.bytebuddy.asm.Advice.DynamicConstant} or + * {@link net.bytebuddy.asm.MemberSubstitution.DynamicConstant}. This annotation should be used with + * {@link net.bytebuddy.implementation.MethodDelegation} only. + *

+ * + * @see net.bytebuddy.implementation.MethodDelegation + * @see TargetMethodAnnotationDrivenBinder + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface DynamicConstant { + + /** + * Returns the name of the dynamic constant that is supplied to the bootstrap method. + * + * @return The name of the dynamic constant that is supplied to the bootstrap method. + */ + String name() default JavaConstant.Dynamic.DEFAULT_NAME; + + /** + * Returns the type of the bootstrap method handle to resolve. + * + * @return The type of the bootstrap method handle to resolve. + */ + JavaConstant.MethodHandle.HandleType bootstrapType(); + + /** + * Returns the owner type of the bootstrap method handle, or {@code void}, to represent the instrumented type. + * + * @return The owner type of the bootstrap method handle, or {@code void}, to represent the instrumented type. + */ + Class bootstrapOwner() default void.class; + + /** + * Returns the name of the bootstrap method handle. + * + * @return The name of the bootstrap method handle. + */ + String bootstrapName(); + + /** + * Returns the return type of the bootstrap method handle. + * + * @return The return type of the bootstrap method handle. + */ + Class bootstrapReturnType(); + + /** + * Returns the parameter types of the bootstrap method handle. + * + * @return The parameter types of the bootstrap method handle. + */ + Class[] bootstrapParameterTypes(); + + /** + * Returns {@code true} if invokedynamic should be used to bind the annotated parameter. + * + * @return {@code true} if invokedynamic should be used to bind the annotated parameter. + */ + boolean invokedynamic() default false; + + /** + * A binder for handling the + * {@link net.bytebuddy.implementation.bind.annotation.DynamicConstant} + * annotation. + * + * @see TargetMethodAnnotationDrivenBinder + */ + enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder { + + /** + * The singleton instance. + */ + INSTANCE; + + /** + * The {@link DynamicConstant#name()} method. + */ + private static final MethodDescription.InDefinedShape NAME; + + /** + * The {@link DynamicConstant#bootstrapType()} method. + */ + private static final MethodDescription.InDefinedShape BOOTSTRAP_TYPE; + + /** + * The {@link DynamicConstant#bootstrapOwner()} method. + */ + private static final MethodDescription.InDefinedShape BOOTSTRAP_OWNER; + + /** + * The {@link DynamicConstant#bootstrapName()} method. + */ + private static final MethodDescription.InDefinedShape BOOTSTRAP_NAME; + + /** + * The {@link DynamicConstant#bootstrapReturnType()} method. + */ + private static final MethodDescription.InDefinedShape BOOTSTRAP_RETURN_TYPE; + + /** + * The {@link DynamicConstant#bootstrapParameterTypes()} method. + */ + private static final MethodDescription.InDefinedShape BOOTSTRAP_PARAMETER_TYPES; + + /** + * The {@link DynamicConstant#invokedynamic()} method. + */ + private static final MethodDescription.InDefinedShape INVOKEDYNAMIC; + + /* + * Resolves all annotation properties. + */ + static { + MethodList methods = TypeDescription.ForLoadedType.of(DynamicConstant.class).getDeclaredMethods(); + NAME = methods.filter(named("name")).getOnly(); + BOOTSTRAP_TYPE = methods.filter(named("bootstrapType")).getOnly(); + BOOTSTRAP_OWNER = methods.filter(named("bootstrapOwner")).getOnly(); + BOOTSTRAP_NAME = methods.filter(named("bootstrapName")).getOnly(); + BOOTSTRAP_RETURN_TYPE = methods.filter(named("bootstrapReturnType")).getOnly(); + BOOTSTRAP_PARAMETER_TYPES = methods.filter(named("bootstrapParameterTypes")).getOnly(); + INVOKEDYNAMIC = methods.filter(named("invokedynamic")).getOnly(); + } + + /** + * {@inheritDoc} + */ + public Class getHandledType() { + return DynamicConstant.class; + } + + /** + * {@inheritDoc} + */ + public MethodDelegationBinder.ParameterBinding bind(AnnotationDescription.Loadable annotation, + MethodDescription source, + ParameterDescription target, + Implementation.Target implementationTarget, + Assigner assigner, + Assigner.Typing typing) { + TypeDescription bootstrapOwner = annotation.getValue(BOOTSTRAP_OWNER).resolve(TypeDescription.class); + if (annotation.getValue(INVOKEDYNAMIC).resolve(Boolean.class)) { + return new MethodDelegationBinder.ParameterBinding.Anonymous(new Invokedynamic( + annotation.getValue(NAME).resolve(String.class), + JavaConstant.MethodType.of(target.getType().asErasure()), + new JavaConstant.MethodHandle( + annotation.getValue(BOOTSTRAP_TYPE).resolve(EnumerationDescription.class).load(JavaConstant.MethodHandle.HandleType.class), + bootstrapOwner.represents(void.class) ? implementationTarget.getInstrumentedType() : bootstrapOwner, + annotation.getValue(BOOTSTRAP_NAME).resolve(String.class), + annotation.getValue(BOOTSTRAP_RETURN_TYPE).resolve(TypeDescription.class), + Arrays.asList(annotation.getValue(BOOTSTRAP_PARAMETER_TYPES).resolve(TypeDescription[].class))), + Collections.emptyList())); + } else { + return new MethodDelegationBinder.ParameterBinding.Anonymous(new JavaConstantValue(new JavaConstant.Dynamic( + annotation.getValue(NAME).resolve(String.class), + target.getType().asErasure(), + new JavaConstant.MethodHandle( + annotation.getValue(BOOTSTRAP_TYPE).resolve(EnumerationDescription.class).load(JavaConstant.MethodHandle.HandleType.class), + bootstrapOwner.represents(void.class) ? implementationTarget.getInstrumentedType() : bootstrapOwner, + annotation.getValue(BOOTSTRAP_NAME).resolve(String.class), + annotation.getValue(BOOTSTRAP_RETURN_TYPE).resolve(TypeDescription.class), + Arrays.asList(annotation.getValue(BOOTSTRAP_PARAMETER_TYPES).resolve(TypeDescription[].class))), + Collections.emptyList()))); + } + } + } +} diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java index 99093f4ba1..7dfc387a8d 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/bind/annotation/TargetMethodAnnotationDrivenBinder.java @@ -173,6 +173,7 @@ public interface ParameterBinder { SuperMethod.Binder.INSTANCE, SuperMethodHandle.Binder.INSTANCE, Handle.Binder.INSTANCE, + DynamicConstant.Binder.INSTANCE, DefaultMethod.Binder.INSTANCE, DefaultMethodHandle.Binder.INSTANCE, FieldValue.Binder.INSTANCE,