package net.bytebuddy.utility;
import edu.umd.cs.findbugs.annotations.
SuppressFBWarnings;
import net.bytebuddy.build.
HashCodeAndEqualsPlugin;
import net.bytebuddy.description.enumeration.
EnumerationDescription;
import net.bytebuddy.description.field.
FieldDescription;
import net.bytebuddy.description.method.
MethodDescription;
import net.bytebuddy.description.type.
TypeDescription;
import net.bytebuddy.description.type.
TypeList;
import net.bytebuddy.jar.asm.
Handle;
import net.bytebuddy.jar.asm.
Opcodes;
import net.bytebuddy.jar.asm.
Type;
import java.lang.reflect.
Constructor;
import java.lang.reflect.
Field;
import java.lang.reflect.
InvocationTargetException;
import java.lang.reflect.
Method;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.*;
/**
* Returns a Java instance of an object that has a special meaning to the Java virtual machine and that is not
* available to Java in versions 6.
*/
public interface
JavaConstant {
/**
* Returns the represented instance as a constant pool value.
*
* @return The constant pool value in a format that can be written by ASM.
*/
Object asConstantPoolValue();
/**
* Returns a description of the type of the represented instance or at least a stub.
*
* @return A description of the type of the represented instance or at least a stub.
*/
TypeDescription getType();
/**
* Represents a {@code java.lang.invoke.MethodType} object.
*/
class
MethodType implements
JavaConstant {
/**
* A dispatcher for extracting information from a {@code java.lang.invoke.MethodType} instance.
*/
private static final
Dispatcher DISPATCHER =
AccessController.
doPrivileged(
Dispatcher.
CreationAction.
INSTANCE);
/**
* The return type of this method type.
*/
private final
TypeDescription returnType;
/**
* The parameter types of this method type.
*/
private final
List<? extends
TypeDescription>
parameterTypes;
/**
* Creates a method type for the given types.
*
* @param returnType The return type of the method type.
* @param parameterTypes The parameter types of the method type.
*/
protected
MethodType(
TypeDescription returnType,
List<? extends
TypeDescription>
parameterTypes) {
this.
returnType =
returnType;
this.
parameterTypes =
parameterTypes;
}
/**
* Returns a method type representation of a loaded {@code MethodType} object.
*
* @param methodType A method type object to represent as a {@link JavaConstant}.
* @return The method type represented as a {@link MethodType}.
*/
public static
MethodType ofLoaded(
Object methodType) {
if (!
JavaType.
METHOD_TYPE.
getTypeStub().
isInstance(
methodType)) {
throw new
IllegalArgumentException("Expected method type object: " +
methodType);
}
return
of(
DISPATCHER.
returnType(
methodType),
DISPATCHER.
parameterArray(
methodType));
}
/**
* Returns a method type description of the given return type and parameter types.
*
* @param returnType The return type to represent.
* @param parameterType The parameter types to represent.
* @return A method type of the given return type and parameter types.
*/
public static
MethodType of(
Class<?>
returnType,
Class<?>...
parameterType) {
return
of(
TypeDescription.
ForLoadedType.
of(
returnType), new
TypeList.
ForLoadedTypes(
parameterType));
}
/**
* Returns a method type description of the given return type and parameter types.
*
* @param returnType The return type to represent.
* @param parameterTypes The parameter types to represent.
* @return A method type of the given return type and parameter types.
*/
public static
MethodType of(
TypeDescription returnType,
List<? extends
TypeDescription>
parameterTypes) {
return new
MethodType(
returnType,
parameterTypes);
}
/**
* Returns a method type description of the given method.
*
* @param method The method to extract the method type from.
* @return The method type of the given method.
*/
public static
MethodType of(
Method method) {
return
of(new
MethodDescription.
ForLoadedMethod(
method));
}
/**
* Returns a method type description of the given constructor.
*
* @param constructor The constructor to extract the method type from.
* @return The method type of the given constructor.
*/
public static
MethodType of(
Constructor<?>
constructor) {
return
of(new
MethodDescription.
ForLoadedConstructor(
constructor));
}
/**
* Returns a method type description of the given method.
*
* @param methodDescription The method to extract the method type from.
* @return The method type of the given method.
*/
public static
MethodType of(
MethodDescription methodDescription) {
return new
MethodType(
methodDescription.
getReturnType().
asErasure(),
methodDescription.
getParameters().
asTypeList().
asErasures());
}
/**
* Returns a method type for a setter of the given field.
*
* @param field The field to extract a setter type for.
* @return The type of a setter for the given field.
*/
public static
MethodType ofSetter(
Field field) {
return
ofSetter(new
FieldDescription.
ForLoadedField(
field));
}
/**
* Returns a method type for a setter of the given field.
*
* @param fieldDescription The field to extract a setter type for.
* @return The type of a setter for the given field.
*/
public static
MethodType ofSetter(
FieldDescription fieldDescription) {
return new
MethodType(
TypeDescription.
VOID,
Collections.
singletonList(
fieldDescription.
getType().
asErasure()));
}
/**
* Returns a method type for a getter of the given field.
*
* @param field The field to extract a getter type for.
* @return The type of a getter for the given field.
*/
public static
MethodType ofGetter(
Field field) {
return
ofGetter(new
FieldDescription.
ForLoadedField(
field));
}
/**
* Returns a method type for a getter of the given field.
*
* @param fieldDescription The field to extract a getter type for.
* @return The type of a getter for the given field.
*/
public static
MethodType ofGetter(
FieldDescription fieldDescription) {
return new
MethodType(
fieldDescription.
getType().
asErasure(),
Collections.<
TypeDescription>
emptyList());
}
/**
* Returns a method type for the given constant.
*
* @param instance The constant for which a constant method type should be created.
* @return A method type for the given constant.
*/
public static
MethodType ofConstant(
Object instance) {
return
ofConstant(
instance.
getClass());
}
/**
* Returns a method type for the given constant type.
*
* @param type The constant type for which a constant method type should be created.
* @return A method type for the given constant type.
*/
public static
MethodType ofConstant(
Class<?>
type) {
return
ofConstant(
TypeDescription.
ForLoadedType.
of(
type));
}
/**
* Returns a method type for the given constant type.
*
* @param typeDescription The constant type for which a constant method type should be created.
* @return A method type for the given constant type.
*/
public static
MethodType ofConstant(
TypeDescription typeDescription) {
return new
MethodType(
typeDescription,
Collections.<
TypeDescription>
emptyList());
}
/**
* Returns the return type of this method type.
*
* @return The return type of this method type.
*/
public
TypeDescription getReturnType() {
return
returnType;
}
/**
* Returns the parameter types of this method type.
*
* @return The parameter types of this method type.
*/
public
TypeList getParameterTypes() {
return new
TypeList.
Explicit(
parameterTypes);
}
/**
* Returns the method descriptor of this method type representation.
*
* @return The method descriptor of this method type representation.
*/
public
String getDescriptor() {
StringBuilder stringBuilder = new
StringBuilder("(");
for (
TypeDescription parameterType :
parameterTypes) {
stringBuilder.
append(
parameterType.
getDescriptor());
}
return
stringBuilder.
append(')').
append(
returnType.
getDescriptor()).
toString();
}
@
Override
public
Object asConstantPoolValue() {
StringBuilder stringBuilder = new
StringBuilder().
append('(');
for (
TypeDescription parameterType :
getParameterTypes()) {
stringBuilder.
append(
parameterType.
getDescriptor());
}
return
Type.
getMethodType(
stringBuilder.
append(')').
append(
getReturnType().
getDescriptor()).
toString());
}
@
Override
public
TypeDescription getType() {
return
JavaType.
METHOD_TYPE.
getTypeStub();
}
@
Override
public boolean
equals(
Object other) {
if (this ==
other) {
return true;
}
if (!(
other instanceof
MethodType)) {
return false;
}
MethodType methodType = (
MethodType)
other;
return
parameterTypes.
equals(
methodType.
parameterTypes) &&
returnType.
equals(
methodType.
returnType);
}
@
Override
public int
hashCode() {
int
result =
returnType.
hashCode();
result = 31 *
result +
parameterTypes.
hashCode();
return
result;
}
/**
* A dispatcher for extracting information from a {@code java.lang.invoke.MethodType} instance.
*/
protected interface
Dispatcher {
/**
* Extracts the return type of the supplied method type.
*
* @param methodType An instance of {@code java.lang.invoke.MethodType}.
* @return The return type that is described by the supplied instance.
*/
Class<?>
returnType(
Object methodType);
/**
* Extracts the parameter types of the supplied method type.
*
* @param methodType An instance of {@code java.lang.invoke.MethodType}.
* @return The parameter types that are described by the supplied instance.
*/
Class<?>[]
parameterArray(
Object methodType);
/**
* A creation action for a dispatcher.
*/
enum
CreationAction implements
PrivilegedAction<
Dispatcher> {
/**
* The singleton instance.
*/
INSTANCE;
@
Override
@
SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
public
Dispatcher run() {
try {
Class<?>
methodType =
JavaType.
METHOD_TYPE.
load();
return new
Dispatcher.
ForJava7CapableVm(
methodType.
getMethod("returnType"),
methodType.
getMethod("parameterArray"));
} catch (
Exception ignored) {
return
Dispatcher.
ForLegacyVm.
INSTANCE;
}
}
}
/**
* A dispatcher for virtual machines that are aware of the {@code java.lang.invoke.MethodType} type that was added in Java version 7.
*/
@
HashCodeAndEqualsPlugin.
Enhance
class
ForJava7CapableVm implements
Dispatcher {
/**
* An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
*/
private static final
Object[]
NO_ARGUMENTS = new
Object[0];
/**
* A reference to {@code java.lang.invoke.MethodType#returnType}.
*/
private final
Method returnType;
/**
* A reference to {@code java.lang.invoke.MethodType#returnType}.
*/
private final
Method parameterArray;
/**
* Creates a new dispatcher for a modern JVM.
*
* @param returnType A reference to {@code java.lang.invoke.MethodType#returnType}.
* @param parameterArray A reference to {@code java.lang.invoke.MethodType#returnType}.
*/
protected
ForJava7CapableVm(
Method returnType,
Method parameterArray) {
this.
returnType =
returnType;
this.
parameterArray =
parameterArray;
}
@
Override
public
Class<?>
returnType(
Object methodType) {
try {
return (
Class<?>)
returnType.
invoke(
methodType,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodType#returnType",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodType#returnType",
exception.
getCause());
}
}
@
Override
public
Class<?>[]
parameterArray(
Object methodType) {
try {
return (
Class<?>[])
parameterArray.
invoke(
methodType,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodType#parameterArray",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodType#parameterArray",
exception.
getCause());
}
}
}
/**
* A dispatcher for virtual machines that are <b>not</b> aware of the {@code java.lang.invoke.MethodType} type that was added in Java version 7.
*/
enum
ForLegacyVm implements
Dispatcher {
/**
* The singleton instance.
*/
INSTANCE;
@
Override
public
Class<?>
returnType(
Object methodType) {
throw new
IllegalStateException("Unsupported type for the current JVM: java.lang.invoke.MethodType");
}
@
Override
public
Class<?>[]
parameterArray(
Object methodType) {
throw new
IllegalStateException("Unsupported type for the current JVM: java.lang.invoke.MethodType");
}
}
}
}
/**
* Represents a {@code java.lang.invoke.MethodHandle} object. Note that constant {@code MethodHandle}s cannot
* be represented within the constant pool of a Java class and can therefore not be represented as an instance of
* this representation order.
*/
class
MethodHandle implements
JavaConstant {
/**
* A dispatcher for receiving the type information that is represented by a {@code java.lang.invoke.MethodHandle} instance.
*/
private static final
Dispatcher.
Initializable DISPATCHER =
AccessController.
doPrivileged(
Dispatcher.
CreationAction.
INSTANCE);
/**
* The handle type that is represented by this instance.
*/
private final
HandleType handleType;
/**
* The owner type that is represented by this instance.
*/
private final
TypeDescription ownerType;
/**
* The name that is represented by this instance.
*/
private final
String name;
/**
* The return type that is represented by this instance.
*/
private final
TypeDescription returnType;
/**
* The parameter types that is represented by this instance.
*/
private final
List<? extends
TypeDescription>
parameterTypes;
/**
* Creates a method handle representation.
*
* @param handleType The handle type that is represented by this instance.
* @param ownerType The owner type that is represented by this instance.
* @param name The name that is represented by this instance.
* @param returnType The return type that is represented by this instance.
* @param parameterTypes The parameter types that is represented by this instance.
*/
protected
MethodHandle(
HandleType handleType,
TypeDescription ownerType,
String name,
TypeDescription returnType,
List<? extends
TypeDescription>
parameterTypes) {
this.
handleType =
handleType;
this.
ownerType =
ownerType;
this.
name =
name;
this.
returnType =
returnType;
this.
parameterTypes =
parameterTypes;
}
/**
* Creates a method handles representation of a loaded method handle which is analyzed using a public {@code MethodHandles.Lookup} object.
* A method handle can only be analyzed on virtual machines that support the corresponding API (Java 7+). For virtual machines before Java 8+,
* a method handle instance can only be analyzed by taking advantage of private APIs what might require a access context.
*
* @param methodHandle The loaded method handle to represent.
* @return A representation of the loaded method handle
*/
public static
MethodHandle ofLoaded(
Object methodHandle) {
return
ofLoaded(
methodHandle,
DISPATCHER.
publicLookup());
}
/**
* Creates a method handles representation of a loaded method handle which is analyzed using the given lookup context.
* A method handle can only be analyzed on virtual machines that support the corresponding API (Java 7+). For virtual machines before Java 8+,
* a method handle instance can only be analyzed by taking advantage of private APIs what might require a access context.
*
* @param methodHandle The loaded method handle to represent.
* @param lookup The lookup object to use for analyzing the method handle.
* @return A representation of the loaded method handle
*/
public static
MethodHandle ofLoaded(
Object methodHandle,
Object lookup) {
if (!
JavaType.
METHOD_HANDLE.
getTypeStub().
isInstance(
methodHandle)) {
throw new
IllegalArgumentException("Expected method handle object: " +
methodHandle);
} else if (!
JavaType.
METHOD_HANDLES_LOOKUP.
getTypeStub().
isInstance(
lookup)) {
throw new
IllegalArgumentException("Expected method handle lookup object: " +
lookup);
}
Dispatcher dispatcher =
DISPATCHER.
initialize();
Object methodHandleInfo =
dispatcher.
reveal(
lookup,
methodHandle);
Object methodType =
dispatcher.
getMethodType(
methodHandleInfo);
return new
MethodHandle(
HandleType.
of(
dispatcher.
getReferenceKind(
methodHandleInfo)),
TypeDescription.
ForLoadedType.
of(
dispatcher.
getDeclaringClass(
methodHandleInfo)),
dispatcher.
getName(
methodHandleInfo),
TypeDescription.
ForLoadedType.
of(
dispatcher.
returnType(
methodType)),
new
TypeList.
ForLoadedTypes(
dispatcher.
parameterArray(
methodType)));
}
/**
* Creates a method handle representation of the given method.
*
* @param method The method ro represent.
* @return A method handle representing the given method.
*/
public static
MethodHandle of(
Method method) {
return
of(new
MethodDescription.
ForLoadedMethod(
method));
}
/**
* Creates a method handle representation of the given constructor.
*
* @param constructor The constructor ro represent.
* @return A method handle representing the given constructor.
*/
public static
MethodHandle of(
Constructor<?>
constructor) {
return
of(new
MethodDescription.
ForLoadedConstructor(
constructor));
}
/**
* Creates a method handle representation of the given method.
*
* @param methodDescription The method ro represent.
* @return A method handle representing the given method.
*/
public static
MethodHandle of(
MethodDescription.
InDefinedShape methodDescription) {
return new
MethodHandle(
HandleType.
of(
methodDescription),
methodDescription.
getDeclaringType().
asErasure(),
methodDescription.
getInternalName(),
methodDescription.
getReturnType().
asErasure(),
methodDescription.
getParameters().
asTypeList().
asErasures());
}
/**
* Creates a method handle representation of the given method for an explicit special method invocation of an otherwise virtual method.
*
* @param method The method ro represent.
* @param type The type on which the method is to be invoked on as a special method invocation.
* @return A method handle representing the given method as special method invocation.
*/
public static
MethodHandle ofSpecial(
Method method,
Class<?>
type) {
return
ofSpecial(new
MethodDescription.
ForLoadedMethod(
method),
TypeDescription.
ForLoadedType.
of(
type));
}
/**
* Creates a method handle representation of the given method for an explicit special method invocation of an otherwise virtual method.
*
* @param methodDescription The method ro represent.
* @param typeDescription The type on which the method is to be invoked on as a special method invocation.
* @return A method handle representing the given method as special method invocation.
*/
public static
MethodHandle ofSpecial(
MethodDescription.
InDefinedShape methodDescription,
TypeDescription typeDescription) {
if (!
methodDescription.
isSpecializableFor(
typeDescription)) {
throw new
IllegalArgumentException("Cannot specialize " +
methodDescription + " for " +
typeDescription);
}
return new
MethodHandle(
HandleType.
ofSpecial(
methodDescription),
typeDescription,
methodDescription.
getInternalName(),
methodDescription.
getReturnType().
asErasure(),
methodDescription.
getParameters().
asTypeList().
asErasures());
}
/**
* Returns a method handle for a setter of the given field.
*
* @param field The field to represent.
* @return A method handle for a setter of the given field.
*/
public static
MethodHandle ofGetter(
Field field) {
return
ofGetter(new
FieldDescription.
ForLoadedField(
field));
}
/**
* Returns a method handle for a setter of the given field.
*
* @param fieldDescription The field to represent.
* @return A method handle for a setter of the given field.
*/
public static
MethodHandle ofGetter(
FieldDescription.
InDefinedShape fieldDescription) {
return new
MethodHandle(
HandleType.
ofGetter(
fieldDescription),
fieldDescription.
getDeclaringType().
asErasure(),
fieldDescription.
getInternalName(),
fieldDescription.
getType().
asErasure(),
Collections.<
TypeDescription>
emptyList());
}
/**
* Returns a method handle for a getter of the given field.
*
* @param field The field to represent.
* @return A method handle for a getter of the given field.
*/
public static
MethodHandle ofSetter(
Field field) {
return
ofSetter(new
FieldDescription.
ForLoadedField(
field));
}
/**
* Returns a method handle for a getter of the given field.
*
* @param fieldDescription The field to represent.
* @return A method handle for a getter of the given field.
*/
public static
MethodHandle ofSetter(
FieldDescription.
InDefinedShape fieldDescription) {
return new
MethodHandle(
HandleType.
ofSetter(
fieldDescription),
fieldDescription.
getDeclaringType().
asErasure(),
fieldDescription.
getInternalName(),
TypeDescription.
VOID,
Collections.
singletonList(
fieldDescription.
getType().
asErasure()));
}
@
Override
public
Object asConstantPoolValue() {
StringBuilder stringBuilder = new
StringBuilder().
append('(');
for (
TypeDescription parameterType :
getParameterTypes()) {
stringBuilder.
append(
parameterType.
getDescriptor());
}
String descriptor =
stringBuilder.
append(')').
append(
getReturnType().
getDescriptor()).
toString();
return new
Handle(
getHandleType().
getIdentifier(),
getOwnerType().
getInternalName(),
getName(),
descriptor,
getOwnerType().
isInterface());
}
@
Override
public
TypeDescription getType() {
return
JavaType.
METHOD_HANDLE.
getTypeStub();
}
/**
* Returns the handle type represented by this instance.
*
* @return The handle type represented by this instance.
*/
public
HandleType getHandleType() {
return
handleType;
}
/**
* Returns the owner type of this instance.
*
* @return The owner type of this instance.
*/
public
TypeDescription getOwnerType() {
return
ownerType;
}
/**
* Returns the name represented by this instance.
*
* @return The name represented by this instance.
*/
public
String getName() {
return
name;
}
/**
* Returns the return type represented by this instance.
*
* @return The return type represented by this instance.
*/
public
TypeDescription getReturnType() {
return
returnType;
}
/**
* Returns the parameter types represented by this instance.
*
* @return The parameter types represented by this instance.
*/
public
TypeList getParameterTypes() {
return new
TypeList.
Explicit(
parameterTypes);
}
/**
* Returns the method descriptor of this method handle representation.
*
* @return The method descriptor of this method handle representation.
*/
public
String getDescriptor() {
StringBuilder stringBuilder = new
StringBuilder().
append('(');
for (
TypeDescription parameterType :
parameterTypes) {
stringBuilder.
append(
parameterType.
getDescriptor());
}
return
stringBuilder.
append(')').
append(
returnType.
getDescriptor()).
toString();
}
@
Override
public boolean
equals(
Object other) {
if (this ==
other) {
return true;
} else if (!(
other instanceof
MethodHandle)) {
return false;
}
MethodHandle methodHandle = (
MethodHandle)
other;
return
handleType ==
methodHandle.
handleType
&&
name.
equals(
methodHandle.
name)
&&
ownerType.
equals(
methodHandle.
ownerType)
&&
parameterTypes.
equals(
methodHandle.
parameterTypes)
&&
returnType.
equals(
methodHandle.
returnType);
}
@
Override
public int
hashCode() {
int
result =
handleType.
hashCode();
result = 31 *
result +
ownerType.
hashCode();
result = 31 *
result +
name.
hashCode();
result = 31 *
result +
returnType.
hashCode();
result = 31 *
result +
parameterTypes.
hashCode();
return
result;
}
/**
* Returns the lookup type of the provided {@code java.lang.invoke.MethodHandles$Lookup} instance.
*
* @param callerClassLookup An instance of {@code java.lang.invoke.MethodHandles$Lookup}.
* @return The instance's lookup type.
*/
public static
Class<?>
lookupType(
Object callerClassLookup) {
return
DISPATCHER.
lookupType(
callerClassLookup);
}
/**
* A dispatcher for analyzing a {@code java.lang.invoke.MethodHandle} instance.
*/
protected interface
Dispatcher {
/**
* Reveals a method handle's information object.
*
* @param lookup The lookup to be used for introspecting the instance.
* @param methodHandle The method handle to be introspected.
* @return The {@code java.lang.invoke.MethodHandleInfo} object that describes the instance.
*/
Object reveal(
Object lookup,
Object methodHandle);
/**
* Returns a method handle info's method type.
*
* @param methodHandleInfo The method handle info to introspect.
* @return The {@code java.lang.invoke.MethodType} instance representing the method handle's type.
*/
Object getMethodType(
Object methodHandleInfo);
/**
* Returns the reference kind of the supplied method handle info.
*
* @param methodHandleInfo The method handle to be introspected.
* @return The method handle info's reference type.
*/
int
getReferenceKind(
Object methodHandleInfo);
/**
* Returns the declaring class of the supplied method handle info.
*
* @param methodHandleInfo The method handle to be introspected.
* @return The method handle info's declaring class.
*/
Class<?>
getDeclaringClass(
Object methodHandleInfo);
/**
* Returns the method name of the supplied method handle info.
*
* @param methodHandleInfo The method handle to be introspected.
* @return The method handle info's method name.
*/
String getName(
Object methodHandleInfo);
/**
* Returns the return type of the supplied method type.
*
* @param methodType The method type to be introspected.
* @return The method type's return type.
*/
Class<?>
returnType(
Object methodType);
/**
* Returns the parameter types of the supplied method type.
*
* @param methodType The method type to be introspected.
* @return The method type's parameter types.
*/
List<? extends
Class<?>>
parameterArray(
Object methodType);
/**
* An initializable version of a dispatcher that is not yet made accessible.
*/
interface
Initializable {
/**
* Initializes the dispatcher, if required.
*
* @return The initialized dispatcher.
*/
Dispatcher initialize();
/**
* Returns a public {@code java.lang.invoke.MethodHandles.Lookup} instance.
*
* @return A public {@code java.lang.invoke.MethodHandles.Lookup} instance.
*/
Object publicLookup();
/**
* Returns the lookup type of a given {@code java.lang.invoke.MethodHandles$Lookup} instance.
*
* @param lookup A {@code java.lang.invoke.MethodHandles$Lookup} instance.
* @return The provided instance's lookup type.
*/
Class<?>
lookupType(
Object lookup);
}
/**
* A creation action for a dispatcher.
*/
enum
CreationAction implements
PrivilegedAction<
Initializable> {
/**
* The singleton instance.
*/
INSTANCE;
@
Override
@
SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback")
public
Initializable run() {
try {
try {
return new
Dispatcher.
ForJava8CapableVm(
Class.
forName("java.lang.invoke.MethodHandles").
getMethod("publicLookup"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getName"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getDeclaringClass"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getReferenceKind"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getMethodType"),
JavaType.
METHOD_TYPE.
load().
getMethod("returnType"),
JavaType.
METHOD_TYPE.
load().
getMethod("parameterArray"),
JavaType.
METHOD_HANDLES_LOOKUP.
load().
getMethod("lookupClass"),
JavaType.
METHOD_HANDLES_LOOKUP.
load().
getMethod("revealDirect",
JavaType.
METHOD_HANDLE.
load()));
} catch (
Exception ignored) {
return new
Dispatcher.
ForJava7CapableVm(
Class.
forName("java.lang.invoke.MethodHandles").
getMethod("publicLookup"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getName"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getDeclaringClass"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getReferenceKind"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getMethod("getMethodType"),
JavaType.
METHOD_TYPE.
load().
getMethod("returnType"),
JavaType.
METHOD_TYPE.
load().
getMethod("parameterArray"),
JavaType.
METHOD_HANDLES_LOOKUP.
load().
getMethod("lookupClass"),
Class.
forName("java.lang.invoke.MethodHandleInfo").
getConstructor(
JavaType.
METHOD_HANDLE.
load()));
}
} catch (
Exception ignored) {
return
Dispatcher.
ForLegacyVm.
INSTANCE;
}
}
}
/**
* An abstract base implementation of a dispatcher.
*/
@
HashCodeAndEqualsPlugin.
Enhance
abstract class
AbstractBase implements
Dispatcher,
Initializable {
/**
* An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
*/
private static final
Object[]
NO_ARGUMENTS = new
Object[0];
/**
* A reference to {@code java.lang.invoke.MethodHandles#publicLookup}.
*/
protected final
Method publicLookup;
/**
* A reference to {@code java.lang.invoke.MethodHandleInfo#getName}.
*/
protected final
Method getName;
/**
* A reference to {@code java.lang.invoke.MethodHandleInfo#getDeclaringClass}.
*/
protected final
Method getDeclaringClass;
/**
* A reference to {@code java.lang.invoke.MethodHandleInfo#getReferenceKind}.
*/
protected final
Method getReferenceKind;
/**
* A reference to {@code java.lang.invoke.MethodHandleInfo#getMethodType}.
*/
protected final
Method getMethodType;
/**
* A reference to {@code java.lang.invoke.MethodType#returnType}.
*/
protected final
Method returnType;
/**
* A reference to {@code java.lang.invoke.MethodType#parameterArray}.
*/
protected final
Method parameterArray;
/**
* A reference to {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
*/
protected final
Method lookupClass;
/**
* Creates a legal dispatcher.
*
* @param publicLookup A reference to {@code java.lang.invoke.MethodHandles#publicLookup}.
* @param getName A reference to {@code java.lang.invoke.MethodHandleInfo#getName}.
* @param getDeclaringClass A reference to {@code java.lang.invoke.MethodHandleInfo#getDeclaringClass}.
* @param getReferenceKind A reference to {@code java.lang.invoke.MethodHandleInfo#getReferenceKind}.
* @param getMethodType A reference to {@code java.lang.invoke.MethodHandleInfo#getMethodType}.
* @param returnType A reference to {@code java.lang.invoke.MethodType#returnType}.
* @param parameterArray A reference to {@code java.lang.invoke.MethodType#parameterArray}.
* @param lookupClass A reference to {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
*/
protected
AbstractBase(
Method publicLookup,
Method getName,
Method getDeclaringClass,
Method getReferenceKind,
Method getMethodType,
Method returnType,
Method parameterArray,
Method lookupClass) {
this.
publicLookup =
publicLookup;
this.
getName =
getName;
this.
getDeclaringClass =
getDeclaringClass;
this.
getReferenceKind =
getReferenceKind;
this.
getMethodType =
getMethodType;
this.
returnType =
returnType;
this.
parameterArray =
parameterArray;
this.
lookupClass =
lookupClass;
}
@
Override
public
Object publicLookup() {
try {
return
publicLookup.
invoke(null,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodHandles#publicLookup",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodHandles#publicLookup",
exception.
getCause());
}
}
@
Override
public
Object getMethodType(
Object methodHandleInfo) {
try {
return
getMethodType.
invoke(
methodHandleInfo,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodHandleInfo#getMethodType",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodHandleInfo#getMethodType",
exception.
getCause());
}
}
@
Override
public int
getReferenceKind(
Object methodHandleInfo) {
try {
return (
Integer)
getReferenceKind.
invoke(
methodHandleInfo,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodHandleInfo#getReferenceKind",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodHandleInfo#getReferenceKind",
exception.
getCause());
}
}
@
Override
public
Class<?>
getDeclaringClass(
Object methodHandleInfo) {
try {
return (
Class<?>)
getDeclaringClass.
invoke(
methodHandleInfo,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodHandleInfo#getDeclaringClass",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodHandleInfo#getDeclaringClass",
exception.
getCause());
}
}
@
Override
public
String getName(
Object methodHandleInfo) {
try {
return (
String)
getName.
invoke(
methodHandleInfo,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodHandleInfo#getName",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodHandleInfo#getName",
exception.
getCause());
}
}
@
Override
public
Class<?>
returnType(
Object methodType) {
try {
return (
Class<?>)
returnType.
invoke(
methodType,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodType#returnType",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.reflect.MethodType#returnType",
exception.
getCause());
}
}
@
Override
public
List<? extends
Class<?>>
parameterArray(
Object methodType) {
try {
return
Arrays.
asList((
Class<?>[])
parameterArray.
invoke(
methodType,
NO_ARGUMENTS));
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.reflect.MethodType#parameterArray",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.reflect.MethodType#parameterArray",
exception.
getCause());
}
}
@
Override
public
Class<?>
lookupType(
Object lookup) {
try {
return (
Class<?>)
lookupClass.
invoke(
lookup,
NO_ARGUMENTS);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.reflect.MethodHandles.Lookup#lookupClass",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.reflect.MethodHandles.Lookup#lookupClass",
exception.
getCause());
}
}
}
/**
* A dispatcher for introspecting a {@code java.lang.invoke.MethodHandle} instance on a virtual machine that officially supports this
* introspection, i.e. Java versions 8+.
*/
@
HashCodeAndEqualsPlugin.
Enhance
class
ForJava8CapableVm extends
AbstractBase {
/**
* A reference to the {@code java.lang.invoke.MethodHandles.Lookup#revealDirect} method.
*/
private final
Method revealDirect;
/**
* Creates a dispatcher for a modern VM.
*
* @param publicLookup A reference to {@code java.lang.invoke.MethodHandles#publicLookup}.
* @param getName A reference to {@code java.lang.invoke.MethodHandleInfo#getName}.
* @param getDeclaringClass A reference to {@code java.lang.invoke.MethodHandleInfo#getDeclaringClass}.
* @param getReferenceKind A reference to {@code java.lang.invoke.MethodHandleInfo#getReferenceKind}.
* @param getMethodType A reference to {@code java.lang.invoke.MethodHandleInfo#getMethodType}.
* @param returnType A reference to {@code java.lang.invoke.MethodType#returnType}.
* @param parameterArray A reference to {@code java.lang.invoke.MethodType#parameterArray}.
* @param lookupClass A reference to {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
* @param revealDirect A reference to the {@code java.lang.invoke.MethodHandles.Lookup#revealDirect} method.
*/
protected
ForJava8CapableVm(
Method publicLookup,
Method getName,
Method getDeclaringClass,
Method getReferenceKind,
Method getMethodType,
Method returnType,
Method parameterArray,
Method lookupClass,
Method revealDirect) {
super(
publicLookup,
getName,
getDeclaringClass,
getReferenceKind,
getMethodType,
returnType,
parameterArray,
lookupClass);
this.
revealDirect =
revealDirect;
}
@
Override
public
Object reveal(
Object lookup,
Object methodHandle) {
try {
return
revealDirect.
invoke(
lookup,
methodHandle);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodHandles.Lookup#revealDirect",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodHandles.Lookup#revealDirect",
exception.
getCause());
}
}
@
Override
public
Dispatcher initialize() {
return this;
}
}
/**
* A dispatcher that extracts the information of a method handle by using private APIs that are available in Java 7+.
*/
@
HashCodeAndEqualsPlugin.
Enhance
class
ForJava7CapableVm extends
AbstractBase implements
PrivilegedAction<
Dispatcher> {
/**
* A reference to the {@code java.lang.invoke.MethodInfo} constructor.
*/
private final
Constructor<?>
methodInfo;
/**
* Creates a dispatcher for an intermediate VM.
*
* @param publicLookup A reference to {@code java.lang.invoke.MethodHandles#publicLookup}.
* @param getName A reference to {@code java.lang.invoke.MethodHandleInfo#getName}.
* @param getDeclaringClass A reference to {@code java.lang.invoke.MethodHandleInfo#getDeclaringClass}.
* @param getReferenceKind A reference to {@code java.lang.invoke.MethodHandleInfo#getReferenceKind}.
* @param getMethodType A reference to {@code java.lang.invoke.MethodHandleInfo#getMethodType}.
* @param returnType A reference to {@code java.lang.invoke.MethodType#returnType}.
* @param parameterArray A reference to {@code java.lang.invoke.MethodType#parameterArray}.
* @param lookupClass A reference to {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method.
* @param methodInfo A reference to the {@code java.lang.invoke.MethodInfo} constructor.
*/
protected
ForJava7CapableVm(
Method publicLookup,
Method getName,
Method getDeclaringClass,
Method getReferenceKind,
Method getMethodType,
Method returnType,
Method parameterArray,
Method lookupClass,
Constructor<?>
methodInfo) {
super(
publicLookup,
getName,
getDeclaringClass,
getReferenceKind,
getMethodType,
returnType,
parameterArray,
lookupClass);
this.
methodInfo =
methodInfo;
}
@
Override
public
Dispatcher initialize() {
return
AccessController.
doPrivileged(this);
}
@
Override
public
Dispatcher run() {
// This is safe even in a multi-threaded environment as all threads set the instances accessible before invoking any methods.
// By always setting accessibility, the security manager is always triggered if this operation was illegal.
methodInfo.
setAccessible(true);
getName.
setAccessible(true);
getDeclaringClass.
setAccessible(true);
getReferenceKind.
setAccessible(true);
getMethodType.
setAccessible(true);
return this;
}
@
Override
public
Object reveal(
Object lookup,
Object methodHandle) {
try {
return
methodInfo.
newInstance(
methodHandle);
} catch (
IllegalAccessException exception) {
throw new
IllegalStateException("Cannot access java.lang.invoke.MethodInfo()",
exception);
} catch (
InvocationTargetException exception) {
throw new
IllegalStateException("Error invoking java.lang.invoke.MethodInfo()",
exception.
getCause());
} catch (
InstantiationException exception) {
throw new
IllegalStateException("Error constructing java.lang.invoke.MethodInfo",
exception);
}
}
}
/**
* A dispatcher that does not support method handles at all.
*/
enum
ForLegacyVm implements
Initializable {
/**
* The singleton instance.
*/
INSTANCE;
@
Override
public
Dispatcher initialize() {
throw new
IllegalStateException("Unsupported type on current JVM: java.lang.invoke.MethodHandle");
}
@
Override
public
Object publicLookup() {
throw new
IllegalStateException("Unsupported type on current JVM: java.lang.invoke.MethodHandle");
}
@
Override
public
Class<?>
lookupType(
Object lookup) {
throw new
IllegalStateException("Unsupported type on current JVM: java.lang.invoke.MethodHandle");
}
}
}
/**
* A representation of a method handle's type.
*/
public enum
HandleType {
/**
* A handle representing an invokevirtual invocation.
*/
INVOKE_VIRTUAL(
Opcodes.
H_INVOKEVIRTUAL),
/**
* A handle representing an invokestatic invocation.
*/
INVOKE_STATIC(
Opcodes.
H_INVOKESTATIC),
/**
* A handle representing an invokespecial invocation for a non-constructor.
*/
INVOKE_SPECIAL(
Opcodes.
H_INVOKESPECIAL),
/**
* A handle representing an invokeinterface invocation.
*/
INVOKE_INTERFACE(
Opcodes.
H_INVOKEINTERFACE),
/**
* A handle representing an invokespecial invocation for a constructor.
*/
INVOKE_SPECIAL_CONSTRUCTOR(
Opcodes.
H_NEWINVOKESPECIAL),
/**
* A handle representing a write of a non-static field invocation.
*/
PUT_FIELD(
Opcodes.
H_PUTFIELD),
/**
* A handle representing a read of a non-static field invocation.
*/
GET_FIELD(
Opcodes.
H_GETFIELD),
/**
* A handle representing a write of a static field invocation.
*/
PUT_STATIC_FIELD(
Opcodes.
H_PUTSTATIC),
/**
* A handle representing a read of a static field invocation.
*/
GET_STATIC_FIELD(
Opcodes.
H_GETSTATIC);
/**
* The represented identifier.
*/
private final int
identifier;
/**
* Creates a new handle type.
*
* @param identifier The represented identifier.
*/
HandleType(int
identifier) {
this.
identifier =
identifier;
}
/**
* Extracts a handle type for invoking the given method.
*
* @param methodDescription The method for which a handle type should be found.
* @return The handle type for the given method.
*/
protected static
HandleType of(
MethodDescription.
InDefinedShape methodDescription) {
if (
methodDescription.
isStatic()) {
return
INVOKE_STATIC;
} else if (
methodDescription.
isPrivate()) {
return
INVOKE_SPECIAL;
} else if (
methodDescription.
isConstructor()) {
return
INVOKE_SPECIAL_CONSTRUCTOR;
} else if (
methodDescription.
getDeclaringType().
isInterface()) {
return
INVOKE_INTERFACE;
} else {
return
INVOKE_VIRTUAL;
}
}
/**
* Extracts a handle type for the given identifier.
*
* @param identifier The identifier to extract a handle type for.
* @return The representing handle type.
*/
protected static
HandleType of(int
identifier) {
for (
HandleType handleType :
HandleType.
values()) {
if (
handleType.
getIdentifier() ==
identifier) {
return
handleType;
}
}
throw new
IllegalArgumentException("Unknown handle type: " +
identifier);
}
/**
* Extracts a handle type for invoking the given method via invokespecial.
*
* @param methodDescription The method for which a handle type should be found.
* @return The handle type for the given method.
*/
protected static
HandleType ofSpecial(
MethodDescription.
InDefinedShape methodDescription) {
if (
methodDescription.
isStatic() ||
methodDescription.
isAbstract()) {
throw new
IllegalArgumentException("Cannot invoke " +
methodDescription + " via invokespecial");
}
return
methodDescription.
isConstructor()
?
INVOKE_SPECIAL_CONSTRUCTOR
:
INVOKE_SPECIAL;
}
/**
* Extracts a handle type for a getter of the given field.
*
* @param fieldDescription The field for which to create a getter handle.
* @return The corresponding handle type.
*/
protected static
HandleType ofGetter(
FieldDescription.
InDefinedShape fieldDescription) {
return
fieldDescription.
isStatic()
?
GET_STATIC_FIELD
:
GET_FIELD;
}
/**
* Extracts a handle type for a setter of the given field.
*
* @param fieldDescription The field for which to create a setter handle.
* @return The corresponding handle type.
*/
protected static
HandleType ofSetter(
FieldDescription.
InDefinedShape fieldDescription) {
return
fieldDescription.
isStatic()
?
PUT_STATIC_FIELD
:
PUT_FIELD;
}
/**
* Returns the represented identifier.
*
* @return The represented identifier.
*/
public int
getIdentifier() {
return
identifier;
}
}
}
/**
* Represents a dynamically resolved constant pool entry of a class file. This feature is supported for class files in version 11 and newer.
*/
@
SuppressWarnings("deprecation")
class
Dynamic implements
JavaConstant {
/**
* The {@code java.lang.invoke.ConstantBootstraps} class's internal name..
*/
private static final
String CONSTANT_BOOTSTRAPS = "java/lang/invoke/ConstantBootstraps";
/**
* The represented bootstrap value.
*/
private final net.bytebuddy.jar.asm.
ConstantDynamic value;
/**
* The represented value constant.
*/
private final
TypeDescription typeDescription;
/**
* Creates a new dynamic class pool entry.
*
* @param value The represented bootstrap value.
* @param typeDescription The represented value constant.
*/
protected
Dynamic(net.bytebuddy.jar.asm.
ConstantDynamic value,
TypeDescription typeDescription) {
this.
value =
value;
this.
typeDescription =
typeDescription;
}
/**
* Returns a constant {@code null} value of type {@link Object}.
*
* @return A dynamically resolved null constant.
*/
public static
Dynamic ofNullConstant() {
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic("nullConstant",
TypeDescription.
OBJECT.
getDescriptor(),
new
Handle(
Opcodes.
H_INVOKESTATIC,
CONSTANT_BOOTSTRAPS,
"nullConstant",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;",
false)),
TypeDescription.
OBJECT);
}
/**
* Returns a {@link Class} constant for a primitive type.
*
* @param type The primitive type to represent.
* @return A dynamically resolved primitive type constant.
*/
public static
JavaConstant ofPrimitiveType(
Class<?>
type) {
return
ofPrimitiveType(
TypeDescription.
ForLoadedType.
of(
type));
}
/**
* Returns a {@link Class} constant for a primitive type.
*
* @param typeDescription The primitive type to represent.
* @return A dynamically resolved primitive type constant.
*/
public static
JavaConstant ofPrimitiveType(
TypeDescription typeDescription) {
if (!
typeDescription.
isPrimitive()) {
throw new
IllegalArgumentException("Not a primitive type: " +
typeDescription);
}
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic(
typeDescription.
getDescriptor(),
TypeDescription.
CLASS.
getDescriptor(),
new
Handle(
Opcodes.
H_INVOKESTATIC,
CONSTANT_BOOTSTRAPS,
"primitiveClass",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Class;",
false)),
TypeDescription.
CLASS);
}
/**
* Returns a {@link Enum} value constant.
*
* @param enumeration The enumeration value to represent.
* @return A dynamically resolved enumeration constant.
*/
public static
JavaConstant ofEnumeration(
Enum<?>
enumeration) {
return
ofEnumeration(new
EnumerationDescription.
ForLoadedEnumeration(
enumeration));
}
/**
* Returns a {@link Enum} value constant.
*
* @param enumerationDescription The enumeration value to represent.
* @return A dynamically resolved enumeration constant.
*/
public static
JavaConstant ofEnumeration(
EnumerationDescription enumerationDescription) {
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic(
enumerationDescription.
getValue(),
enumerationDescription.
getEnumerationType().
getDescriptor(),
new
Handle(
Opcodes.
H_INVOKESTATIC,
CONSTANT_BOOTSTRAPS,
"enumConstant",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Enum;",
false)),
enumerationDescription.
getEnumerationType());
}
/**
* Returns a {@code static}, {@code final} field constant.
*
* @param field The field to represent a value of.
* @return A dynamically resolved field value constant.
*/
public static
Dynamic ofField(
Field field) {
return
ofField(new
FieldDescription.
ForLoadedField(
field));
}
/**
* Returns a {@code static}, {@code final} field constant.
*
* @param fieldDescription The field to represent a value of.
* @return A dynamically resolved field value constant.
*/
public static
Dynamic ofField(
FieldDescription.
InDefinedShape fieldDescription) {
if (!
fieldDescription.
isStatic() || !
fieldDescription.
isFinal()) {
throw new
IllegalArgumentException("Field must be static and final: " +
fieldDescription);
}
boolean
selfDeclared =
fieldDescription.
getType().
isPrimitive()
?
fieldDescription.
getType().
asErasure().
asBoxed().
equals(
fieldDescription.
getType().
asErasure())
:
fieldDescription.
getDeclaringType().
equals(
fieldDescription.
getType().
asErasure());
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic(
fieldDescription.
getInternalName(),
fieldDescription.
getDescriptor(),
new
Handle(
Opcodes.
H_INVOKESTATIC,
CONSTANT_BOOTSTRAPS,
"getStaticFinal",
selfDeclared
? "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"
: "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;",
false),
selfDeclared
? new
Object[0]
: new
Object[]{
Type.
getType(
fieldDescription.
getDeclaringType().
getDescriptor())}),
fieldDescription.
getType().
asErasure());
}
/**
* Represents a constant that is resolved by invoking a {@code static} factory method.
*
* @param method The method to invoke to create the represented constant value.
* @param rawArgument The method's constant arguments.
* @return A dynamic constant that is resolved by the supplied factory method.
*/
public static
Dynamic ofInvocation(
Method method,
Object...
rawArgument) {
return
ofInvocation(
method,
Arrays.
asList(
rawArgument));
}
/**
* Represents a constant that is resolved by invoking a {@code static} factory method.
*
* @param method The method to invoke to create the represented constant value.
* @param rawArguments The method's constant arguments.
* @return A dynamic constant that is resolved by the supplied factory method.
*/
public static
Dynamic ofInvocation(
Method method,
List<?>
rawArguments) {
return
ofInvocation(new
MethodDescription.
ForLoadedMethod(
method),
rawArguments);
}
/**
* Represents a constant that is resolved by invoking a constructor.
*
* @param constructor The constructor to invoke to create the represented constant value.
* @param rawArgument The constructor's constant arguments.
* @return A dynamic constant that is resolved by the supplied constuctor.
*/
public static
Dynamic ofInvocation(
Constructor<?>
constructor,
Object...
rawArgument) {
return
ofInvocation(
constructor,
Arrays.
asList(
rawArgument));
}
/**
* Represents a constant that is resolved by invoking a constructor.
*
* @param constructor The constructor to invoke to create the represented constant value.
* @param rawArguments The constructor's constant arguments.
* @return A dynamic constant that is resolved by the supplied constuctor.
*/
public static
Dynamic ofInvocation(
Constructor<?>
constructor,
List<?>
rawArguments) {
return
ofInvocation(new
MethodDescription.
ForLoadedConstructor(
constructor),
rawArguments);
}
/**
* Represents a constant that is resolved by invoking a {@code static} factory method or a constructor.
*
* @param methodDescription The method or constructor to invoke to create the represented constant value.
* @param rawArgument The method's or constructor's constant arguments.
* @return A dynamic constant that is resolved by the supplied factory method or constructor.
*/
public static
Dynamic ofInvocation(
MethodDescription.
InDefinedShape methodDescription,
Object...
rawArgument) {
return
ofInvocation(
methodDescription,
Arrays.
asList(
rawArgument));
}
/**
* Represents a constant that is resolved by invoking a {@code static} factory method or a constructor.
*
* @param methodDescription The method or constructor to invoke to create the represented constant value.
* @param rawArguments The method's or constructor's constant arguments.
* @return A dynamic constant that is resolved by the supplied factory method or constructor.
*/
public static
Dynamic ofInvocation(
MethodDescription.
InDefinedShape methodDescription,
List<?>
rawArguments) {
if (!
methodDescription.
isConstructor() &&
methodDescription.
getReturnType().
represents(void.class)) {
throw new
IllegalArgumentException("Bootstrap method is no constructor or non-void static factory: " +
methodDescription);
} else if (
methodDescription.
getParameters().
size() + (
methodDescription.
isStatic() ||
methodDescription.
isConstructor() ? 0 : 1) !=
rawArguments.
size()) {
throw new
IllegalArgumentException("Cannot assign " +
rawArguments + " to " +
methodDescription);
}
Iterator<
TypeDescription>
iterator = (
methodDescription.
isStatic() ||
methodDescription.
isConstructor()
?
methodDescription.
getParameters().
asTypeList().
asErasures()
:
CompoundList.
of(
methodDescription.
getDeclaringType(),
methodDescription.
getParameters().
asTypeList().
asErasures())).
iterator();
List<
Object>
arguments = new
ArrayList<
Object>(
rawArguments.
size());
for (
Object argument :
rawArguments) {
if (
argument == null) {
argument =
Dynamic.
ofNullConstant();
} else if (
argument instanceof
Class) {
argument = ((
Class<?>)
argument).
isPrimitive()
?
ofPrimitiveType((
Class<?>)
argument)
:
TypeDescription.
ForLoadedType.
of((
Class<?>)
argument);
} else if (
argument instanceof
TypeDescription && ((
TypeDescription)
argument).
isPrimitive()) {
argument =
ofPrimitiveType((
TypeDescription)
argument);
} else if (
JavaType.
METHOD_HANDLE.
getTypeStub().
isInstance(
argument)) {
argument =
MethodHandle.
ofLoaded(
argument);
} else if (
JavaType.
METHOD_TYPE.
getTypeStub().
isInstance(
argument)) {
argument =
MethodType.
ofLoaded(
argument);
}
TypeDescription targetType;
if (
argument instanceof
JavaConstant) {
targetType = ((
JavaConstant)
argument).
getType();
} else if (
argument instanceof
TypeDescription) {
targetType =
TypeDescription.
CLASS;
} else {
targetType =
TypeDescription.
ForLoadedType.
of(
argument.
getClass());
}
if (!
targetType.
asBoxed().
isAssignableTo(
iterator.
next().
asBoxed())) {
throw new
IllegalArgumentException("Cannot assign argument of type " +
targetType + " to " +
methodDescription);
}
arguments.
add(
argument);
}
Object[]
asmifiedArgument = new
Object[
arguments.
size() + 1];
asmifiedArgument[0] = new
Handle(
methodDescription.
isConstructor() ?
Opcodes.
H_NEWINVOKESPECIAL :
Opcodes.
H_INVOKESTATIC,
methodDescription.
getDeclaringType().
getInternalName(),
methodDescription.
getInternalName(),
methodDescription.
getDescriptor(),
false);
int
index = 1;
for (
Object argument :
arguments) {
if (
argument instanceof
TypeDescription) {
argument =
Type.
getType(((
TypeDescription)
argument).
getDescriptor());
} else if (
argument instanceof
JavaConstant) {
argument = ((
JavaConstant)
argument).
asConstantPoolValue();
}
asmifiedArgument[
index++] =
argument;
}
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic("invoke",
(
methodDescription.
isConstructor()
?
methodDescription.
getDeclaringType()
:
methodDescription.
getReturnType().
asErasure()).
getDescriptor(),
new
Handle(
Opcodes.
H_INVOKESTATIC,
CONSTANT_BOOTSTRAPS,
"invoke",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
false),
asmifiedArgument),
methodDescription.
isConstructor() ?
methodDescription.
getDeclaringType() :
methodDescription.
getReturnType().
asErasure());
}
/**
* Resolves a var handle constant for a field.
*
* @param field The field to represent a var handle for.
* @return A dynamic constant that represents the created var handle constant.
*/
public static
JavaConstant ofVarHandle(
Field field) {
return
ofVarHandle(new
FieldDescription.
ForLoadedField(
field));
}
/**
* Resolves a var handle constant for a field.
*
* @param fieldDescription The field to represent a var handle for.
* @return A dynamic constant that represents the created var handle constant.
*/
public static
JavaConstant ofVarHandle(
FieldDescription.
InDefinedShape fieldDescription) {
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic(
fieldDescription.
getInternalName(),
JavaType.
VAR_HANDLE.
getTypeStub().
getDescriptor(),
new
Handle(
Opcodes.
H_INVOKESTATIC,
CONSTANT_BOOTSTRAPS,
fieldDescription.
isStatic()
? "staticFieldVarHandle"
: "fieldVarHandle",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;",
false),
Type.
getType(
fieldDescription.
getDeclaringType().
getDescriptor()),
Type.
getType(
fieldDescription.
getType().
asErasure().
getDescriptor())),
JavaType.
VAR_HANDLE.
getTypeStub());
}
/**
* Resolves a var handle constant for an array.
*
* @param type The array type for which the var handle is resolved.
* @return A dynamic constant that represents the created var handle constant.
*/
public static
JavaConstant ofArrayVarHandle(
Class<?>
type) {
return
ofArrayVarHandle(
TypeDescription.
ForLoadedType.
of(
type));
}
/**
* Resolves a var handle constant for an array.
*
* @param typeDescription The array type for which the var handle is resolved.
* @return A dynamic constant that represents the created var handle constant.
*/
public static
JavaConstant ofArrayVarHandle(
TypeDescription typeDescription) {
if (!
typeDescription.
isArray()) {
throw new
IllegalArgumentException("Not an array type: " +
typeDescription);
}
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic("arrayVarHandle",
JavaType.
VAR_HANDLE.
getTypeStub().
getDescriptor(),
new
Handle(
Opcodes.
H_INVOKESTATIC,
CONSTANT_BOOTSTRAPS,
"arrayVarHandle",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;",
false),
Type.
getType(
typeDescription.
getDescriptor())),
JavaType.
VAR_HANDLE.
getTypeStub());
}
/**
* Binds the supplied bootstrap method for the resolution of a dynamic constant.
*
* @param name The name of the bootstrap constant that is provided to the bootstrap method or constructor.
* @param method The bootstrap method to invoke.
* @param rawArgument The arguments for the bootstrap method represented as primitive wrapper types,
* {@link String}, {@link TypeDescription} or {@link JavaConstant} values or their loaded forms.
* @return A dynamic constant that represents the bootstrapped method's result.
*/
public static
Dynamic bootstrap(
String name,
Method method,
Object...
rawArgument) {
return
bootstrap(
name,
method,
Arrays.
asList(
rawArgument));
}
/**
* Binds the supplied bootstrap method for the resolution of a dynamic constant.
*
* @param name The name of the bootstrap constant that is provided to the bootstrap method or constructor.
* @param method The bootstrap method to invoke.
* @param rawArguments The arguments for the bootstrap method represented as primitive wrapper types,
* {@link String}, {@link TypeDescription} or {@link JavaConstant} values or their loaded forms.
* @return A dynamic constant that represents the bootstrapped method's result.
*/
public static
Dynamic bootstrap(
String name,
Method method,
List<?>
rawArguments) {
return
bootstrap(
name, new
MethodDescription.
ForLoadedMethod(
method),
rawArguments);
}
/**
* Binds the supplied bootstrap constructor for the resolution of a dynamic constant.
*
* @param name The name of the bootstrap constant that is provided to the bootstrap method or constructor.
* @param constructor The bootstrap constructor to invoke.
* @param rawArgument The arguments for the bootstrap constructor represented as primitive wrapper types,
* {@link String}, {@link TypeDescription} or {@link JavaConstant} values or their loaded forms.
* @return A dynamic constant that represents the bootstrapped constructor's result.
*/
public static
Dynamic bootstrap(
String name,
Constructor<?>
constructor,
Object...
rawArgument) {
return
bootstrap(
name,
constructor,
Arrays.
asList(
rawArgument));
}
/**
* Binds the supplied bootstrap constructor for the resolution of a dynamic constant.
*
* @param name The name of the bootstrap constant that is provided to the bootstrap method or constructor.
* @param constructor The bootstrap constructor to invoke.
* @param rawArguments The arguments for the bootstrap constructor represented as primitive wrapper types,
* {@link String}, {@link TypeDescription} or {@link JavaConstant} values or their loaded forms.
* @return A dynamic constant that represents the bootstrapped constructor's result.
*/
public static
Dynamic bootstrap(
String name,
Constructor<?>
constructor,
List<?>
rawArguments) {
return
bootstrap(
name, new
MethodDescription.
ForLoadedConstructor(
constructor),
rawArguments);
}
/**
* Binds the supplied bootstrap method or constructor for the resolution of a dynamic constant.
*
* @param name The name of the bootstrap constant that is provided to the bootstrap method or constructor.
* @param bootstrapMethod The bootstrap method or constructor to invoke.
* @param rawArgument The arguments for the bootstrap method or constructor represented as primitive wrapper types,
* {@link String}, {@link TypeDescription} or {@link JavaConstant} values or their loaded forms.
* @return A dynamic constant that represents the bootstrapped method's or constructor's result.
*/
public static
Dynamic bootstrap(
String name,
MethodDescription.
InDefinedShape bootstrapMethod,
Object...
rawArgument) {
return
bootstrap(
name,
bootstrapMethod,
Arrays.
asList(
rawArgument));
}
/**
* Binds the supplied bootstrap method or constructor for the resolution of a dynamic constant.
*
* @param name The name of the bootstrap constant that is provided to the bootstrap method or constructor.
* @param bootstrapMethod The bootstrap method or constructor to invoke.
* @param rawArguments The arguments for the bootstrap method or constructor represented as primitive wrapper types,
* {@link String}, {@link TypeDescription} or {@link JavaConstant} values or their loaded forms.
* @return A dynamic constant that represents the bootstrapped method's or constructor's result.
*/
public static
Dynamic bootstrap(
String name,
MethodDescription.
InDefinedShape bootstrapMethod,
List<?>
rawArguments) {
if (
name.
isEmpty() ||
name.
contains(".")) {
throw new
IllegalArgumentException("Not a valid field name: " +
name);
}
List<
Object>
arguments = new
ArrayList<
Object>(
rawArguments.
size());
for (
Object argument :
rawArguments) {
if (
argument == null) {
argument =
ofNullConstant();
} else if (
argument instanceof
Class) {
argument = ((
Class<?>)
argument).
isPrimitive()
?
ofPrimitiveType((
Class<?>)
argument)
:
TypeDescription.
ForLoadedType.
of((
Class<?>)
argument);
} else if (
argument instanceof
TypeDescription && ((
TypeDescription)
argument).
isPrimitive()) {
argument =
ofPrimitiveType((
TypeDescription)
argument);
} else if (
JavaType.
METHOD_HANDLE.
getTypeStub().
isInstance(
argument)) {
argument =
MethodHandle.
ofLoaded(
argument);
} else if (
JavaType.
METHOD_TYPE.
getTypeStub().
isInstance(
argument)) {
argument =
MethodType.
ofLoaded(
argument);
}
arguments.
add(
argument);
}
if (!
bootstrapMethod.
isConstantBootstrap(
arguments)) {
throw new
IllegalArgumentException("Not a valid bootstrap method " +
bootstrapMethod + " for " +
arguments);
}
Object[]
asmifiedArgument = new
Object[
arguments.
size()];
int
index = 0;
for (
Object argument :
arguments) {
if (
argument instanceof
TypeDescription) {
argument =
Type.
getType(((
TypeDescription)
argument).
getDescriptor());
} else if (
argument instanceof
JavaConstant) {
argument = ((
JavaConstant)
argument).
asConstantPoolValue();
}
asmifiedArgument[
index++] =
argument;
}
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic(
name,
(
bootstrapMethod.
isConstructor()
?
bootstrapMethod.
getDeclaringType()
:
bootstrapMethod.
getReturnType().
asErasure()).
getDescriptor(),
new
Handle(
bootstrapMethod.
isConstructor() ?
Opcodes.
H_NEWINVOKESPECIAL :
Opcodes.
H_INVOKESTATIC,
bootstrapMethod.
getDeclaringType().
getInternalName(),
bootstrapMethod.
getInternalName(),
bootstrapMethod.
getDescriptor(),
false),
asmifiedArgument),
bootstrapMethod.
isConstructor()
?
bootstrapMethod.
getDeclaringType()
:
bootstrapMethod.
getReturnType().
asErasure());
}
/**
* Resolves this {@link Dynamic} constant to resolve the returned instance to the supplied type. The type must be a subtype of the
* bootstrap method's return type. Constructors cannot be resolved to a different type.
*
* @param type The type to resolve the bootstrapped value to.
* @return This dynamic constant but resolved to the supplied type.
*/
public
JavaConstant withType(
Class<?>
type) {
return
withType(
TypeDescription.
ForLoadedType.
of(
type));
}
/**
* Resolves this {@link Dynamic} constant to resolve the returned instance to the supplied type. The type must be a subtype of the
* bootstrap method's return type. Constructors cannot be resolved to a different type.
*
* @param typeDescription The type to resolve the bootstrapped value to.
* @return This dynamic constant but resolved to the supplied type.
*/
public
JavaConstant withType(
TypeDescription typeDescription) {
if (
typeDescription.
represents(void.class)) {
throw new
IllegalArgumentException("Constant value cannot represent void");
} else if (
value.
getBootstrapMethod().
getName().
equals(
MethodDescription.
CONSTRUCTOR_INTERNAL_NAME)
? !this.
typeDescription.
isAssignableTo(
typeDescription)
: (!
typeDescription.
asBoxed().
isInHierarchyWith(this.
typeDescription.
asBoxed()))) {
throw new
IllegalArgumentException(
typeDescription + " is not compatible with bootstrapped type " + this.
typeDescription);
}
return new
Dynamic(new net.bytebuddy.jar.asm.
ConstantDynamic(
value.
getName(),
typeDescription.
getDescriptor(),
value.
getBootstrapMethod(),
value.
getBootstrapMethodArguments()),
typeDescription);
}
@
Override
public
Object asConstantPoolValue() {
return
value;
}
@
Override
public
TypeDescription getType() {
return
typeDescription;
}
@
Override
public int
hashCode() {
int
result =
value.
hashCode();
result = 31 *
result +
typeDescription.
hashCode();
return
result;
}
@
Override
public boolean
equals(
Object other) {
if (this ==
other) {
return true;
} else if (
other == null ||
getClass() !=
other.
getClass()) {
return false;
}
Dynamic dynamic = (
Dynamic)
other;
return
value.
equals(
dynamic.
value) &&
typeDescription.
equals(
dynamic.
typeDescription);
}
}
}