com.azure.core.implementation.ReflectionUtils Maven / Gradle / Ivy
Show all versions of azure-core Show documentation
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.core.implementation;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.logging.LogLevel;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.security.PrivilegedExceptionAction;
/**
* Utility methods that aid in performing reflective operations.
*/
@SuppressWarnings("deprecation")
public final class ReflectionUtils {
private static final ClientLogger LOGGER = new ClientLogger(ReflectionUtils.class);
private static final boolean MODULE_BASED;
private static final MethodHandle CLASS_GET_MODULE_METHOD_HANDLE;
private static final MethodHandle MODULE_IS_NAMED_METHOD_HANDLE;
private static final MethodHandle MODULE_ADD_READS_METHOD_HANDLE;
private static final MethodHandle METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE;
private static final MethodHandle MODULE_IS_OPEN_UNCONDITIONALLY_METHOD_HANDLE;
private static final MethodHandle MODULE_IS_OPEN_TO_OTHER_MODULE_METHOD_HANDLE;
private static final MethodHandles.Lookup LOOKUP;
private static final Object CORE_MODULE;
private static final MethodHandle JDK_INTERNAL_PRIVATE_LOOKUP_IN_CONSTRUCTOR;
static {
boolean moduleBased = false;
MethodHandle classGetModule = null;
MethodHandle moduleIsNamed = null;
MethodHandle moduleAddReads = null;
MethodHandle methodHandlesPrivateLookupIn = null;
MethodHandle moduleIsOpenUnconditionally = null;
MethodHandle moduleIsOpenToOtherModule = null;
MethodHandles.Lookup lookup = MethodHandles.lookup();
Object coreModule = null;
MethodHandle jdkInternalPrivateLookupInConstructor = null;
try {
Class> moduleClass = Class.forName("java.lang.Module");
classGetModule = lookup.unreflect(Class.class.getDeclaredMethod("getModule"));
moduleIsNamed = lookup.unreflect(moduleClass.getDeclaredMethod("isNamed"));
moduleAddReads = lookup.unreflect(moduleClass.getDeclaredMethod("addReads", moduleClass));
methodHandlesPrivateLookupIn = lookup.findStatic(MethodHandles.class, "privateLookupIn",
MethodType.methodType(MethodHandles.Lookup.class, Class.class, MethodHandles.Lookup.class));
moduleIsOpenUnconditionally = lookup.unreflect(moduleClass.getDeclaredMethod("isOpen", String.class));
moduleIsOpenToOtherModule = lookup.unreflect(
moduleClass.getDeclaredMethod("isOpen", String.class, moduleClass));
coreModule = classGetModule.invokeWithArguments(ReflectionUtils.class);
moduleBased = true;
} catch (Throwable throwable) {
if (throwable instanceof Error) {
throw (Error) throwable;
} else {
LOGGER.log(LogLevel.INFORMATIONAL,
() -> "Unable to create MethodHandles to use Java 9+ MethodHandles.privateLookupIn. "
+ "Will attempt to fallback to using the package-private constructor.", throwable);
}
}
if (!moduleBased) {
try {
Constructor privateLookupInConstructor =
MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
if (!privateLookupInConstructor.isAccessible()) {
privateLookupInConstructor.setAccessible(true);
}
jdkInternalPrivateLookupInConstructor = lookup.unreflectConstructor(privateLookupInConstructor);
} catch (ReflectiveOperationException ex) {
throw LOGGER.logExceptionAsError(
new RuntimeException("Unable to use package-private MethodHandles.Lookup constructor.", ex));
}
}
MODULE_BASED = moduleBased;
CLASS_GET_MODULE_METHOD_HANDLE = classGetModule;
MODULE_IS_NAMED_METHOD_HANDLE = moduleIsNamed;
MODULE_ADD_READS_METHOD_HANDLE = moduleAddReads;
METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE = methodHandlesPrivateLookupIn;
MODULE_IS_OPEN_UNCONDITIONALLY_METHOD_HANDLE = moduleIsOpenUnconditionally;
MODULE_IS_OPEN_TO_OTHER_MODULE_METHOD_HANDLE = moduleIsOpenToOtherModule;
LOOKUP = lookup;
CORE_MODULE = coreModule;
JDK_INTERNAL_PRIVATE_LOOKUP_IN_CONSTRUCTOR = jdkInternalPrivateLookupInConstructor;
}
/**
* Gets the {@link MethodHandles.Lookup} to use when performing reflective operations.
*
* If Java 8 is being used this will always return {@link MethodHandles.Lookup#publicLookup()} as Java 8 doesn't
* have module boundaries that will prevent reflective access to the {@code targetClass}.
*
* If Java 9 or above is being used this will return a {@link MethodHandles.Lookup} based on whether the module
* containing the {@code targetClass} exports the package containing the class. Otherwise, the
* {@link MethodHandles.Lookup} associated to {@code com.azure.core} will attempt to read the module containing
* {@code targetClass}.
*
* @param targetClass The {@link Class} that will need to be reflectively accessed.
* @return The {@link MethodHandles.Lookup} that will allow {@code com.azure.core} to access the {@code targetClass}
* reflectively.
* @throws Exception If the underlying reflective calls throw an exception.
*/
public static MethodHandles.Lookup getLookupToUse(Class> targetClass) throws Exception {
try {
if (MODULE_BASED) {
Object responseModule = CLASS_GET_MODULE_METHOD_HANDLE.invoke(targetClass);
// The unnamed module is opened unconditionally, have Core read it and use a private proxy lookup to
// enable all lookup scenarios.
if (!(boolean) MODULE_IS_NAMED_METHOD_HANDLE.invoke(responseModule)) {
MODULE_ADD_READS_METHOD_HANDLE.invokeWithArguments(CORE_MODULE, responseModule);
return performSafePrivateLookupIn(targetClass);
}
// If the response module is the Core module return the Core private lookup.
if (responseModule == CORE_MODULE) {
return LOOKUP;
}
// Next check if the target class module is opened either unconditionally or to Core's module. If so,
// also use a private proxy lookup to enable all lookup scenarios.
String packageName = targetClass.getPackage().getName();
if ((boolean) MODULE_IS_OPEN_UNCONDITIONALLY_METHOD_HANDLE
.invokeWithArguments(responseModule, packageName)
|| (boolean) MODULE_IS_OPEN_TO_OTHER_MODULE_METHOD_HANDLE
.invokeWithArguments(responseModule, packageName, CORE_MODULE)) {
MODULE_ADD_READS_METHOD_HANDLE.invokeWithArguments(CORE_MODULE, responseModule);
return performSafePrivateLookupIn(targetClass);
}
// Otherwise, return the public lookup as there are no specialty ways to access the other module.
return MethodHandles.publicLookup();
} else {
return (MethodHandles.Lookup) JDK_INTERNAL_PRIVATE_LOOKUP_IN_CONSTRUCTOR.invoke(targetClass);
}
} catch (Throwable throwable) {
// invoke(Class) throws a Throwable as the underlying method being called through reflection can throw
// anything, but the constructor being called is owned by the Java SDKs which won't throw Throwable. So,
// only Error needs to be inspected and handled specially, otherwise it can be assumed the Throwable is
// a type of Exception which can be thrown based on this method having Exception checked.
if (throwable instanceof Error) {
throw (Error) throwable;
} else {
throw (Exception) throwable;
}
}
}
@SuppressWarnings("removal")
private static MethodHandles.Lookup performSafePrivateLookupIn(Class> targetClass) throws Throwable {
// MethodHandles::privateLookupIn() throws SecurityException if denied by the security manager
if (System.getSecurityManager() == null) {
return (MethodHandles.Lookup) METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE
.invokeExact(targetClass, LOOKUP);
} else {
return java.security.AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
try {
return (MethodHandles.Lookup) METHOD_HANDLES_PRIVATE_LOOKUP_IN_METHOD_HANDLE
.invokeExact(targetClass, LOOKUP);
} catch (Throwable throwable) {
if (throwable instanceof Error) {
throw (Error) throwable;
} else {
throw (Exception) throwable;
}
}
});
}
}
public static boolean isModuleBased() {
return MODULE_BASED;
}
ReflectionUtils() {
}
}