Replace reflection usage with MethodHandles when performance matters (#10097)

Motivation:

As we have java8 as a minimum target we can use MethodHandles. We should do so when we expect to have a method called multiple times.

Modifications:

- Replace usage of reflection with MethodHandles where it makes sense
- Remove some code which was there to support java < 8

Result:

Faster code
This commit is contained in:
Norman Maurer 2020-03-11 21:04:40 +01:00 committed by GitHub
parent d639f55764
commit fd0d06ee39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 232 additions and 273 deletions

View File

@ -20,7 +20,6 @@ import static java.util.Objects.requireNonNull;
import io.netty.buffer.ByteBuf;
import io.netty.util.ByteProcessor;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.zip.Adler32;
import java.util.zip.CRC32;
@ -33,43 +32,34 @@ import java.util.zip.Checksum;
* byte array ({@link ByteBuf#hasArray()} is {@code true}) or not.
*/
abstract class ByteBufChecksum implements Checksum {
private static final Method ADLER32_UPDATE_METHOD;
private static final Method CRC32_UPDATE_METHOD;
static {
// See if we can use fast-path when using ByteBuf that is not heap based as Adler32 and CRC32 added support
// for update(ByteBuffer) in JDK8.
ADLER32_UPDATE_METHOD = updateByteBuffer(new Adler32());
CRC32_UPDATE_METHOD = updateByteBuffer(new CRC32());
}
private final ByteProcessor updateProcessor = value -> {
update(value);
return true;
};
private static Method updateByteBuffer(Checksum checksum) {
try {
Method method = checksum.getClass().getDeclaredMethod("update", ByteBuffer.class);
method.invoke(checksum, ByteBuffer.allocate(1));
return method;
} catch (Throwable ignore) {
return null;
}
}
static ByteBufChecksum wrapChecksum(Checksum checksum) {
requireNonNull(checksum, "checksum");
if (checksum instanceof ByteBufChecksum) {
return (ByteBufChecksum) checksum;
}
if (checksum instanceof Adler32 && ADLER32_UPDATE_METHOD != null) {
return new ReflectiveByteBufChecksum(checksum, ADLER32_UPDATE_METHOD);
if (checksum instanceof Adler32) {
return new OptimizedByteBufChecksum<Adler32>((Adler32) checksum) {
@Override
public void update(ByteBuffer b) {
checksum.update(b);
}
};
}
if (checksum instanceof CRC32 && CRC32_UPDATE_METHOD != null) {
return new ReflectiveByteBufChecksum(checksum, CRC32_UPDATE_METHOD);
if (checksum instanceof CRC32) {
return new OptimizedByteBufChecksum<CRC32>((CRC32) checksum) {
@Override
public void update(ByteBuffer b) {
checksum.update(b);
}
};
}
return new SlowByteBufChecksum(checksum);
return new SlowByteBufChecksum<>(checksum);
}
/**
@ -83,12 +73,9 @@ abstract class ByteBufChecksum implements Checksum {
}
}
private static final class ReflectiveByteBufChecksum extends SlowByteBufChecksum {
private final Method method;
ReflectiveByteBufChecksum(Checksum checksum, Method method) {
private abstract static class OptimizedByteBufChecksum<T extends Checksum> extends SlowByteBufChecksum<T> {
OptimizedByteBufChecksum(T checksum) {
super(checksum);
this.method = method;
}
@Override
@ -97,19 +84,21 @@ abstract class ByteBufChecksum implements Checksum {
update(b.array(), b.arrayOffset() + off, len);
} else {
try {
method.invoke(checksum, CompressionUtil.safeNioBuffer(b, off, len));
update(CompressionUtil.safeNioBuffer(b, off, len));
} catch (Throwable cause) {
throw new Error();
}
}
}
public abstract void update(ByteBuffer b);
}
private static class SlowByteBufChecksum extends ByteBufChecksum {
private static class SlowByteBufChecksum<T extends Checksum> extends ByteBufChecksum {
protected final Checksum checksum;
protected final T checksum;
SlowByteBufChecksum(Checksum checksum) {
SlowByteBufChecksum(T checksum) {
this.checksum = checksum;
}

View File

@ -18,8 +18,10 @@ package io.netty.util.internal;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
@ -33,15 +35,15 @@ import java.security.PrivilegedAction;
*/
final class CleanerJava6 implements Cleaner {
private static final long CLEANER_FIELD_OFFSET;
private static final Method CLEAN_METHOD;
private static final Field CLEANER_FIELD;
private static final MethodHandle CLEAN_HANDLE;
private static final MethodHandle CLEANER_FIELD_HANDLE;
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava6.class);
static {
long fieldOffset;
Method clean;
Field cleanerField;
MethodHandle cleanHandle = null;
MethodHandle cleanerFieldHandle = null;
Throwable error = null;
final ByteBuffer direct = ByteBuffer.allocateDirect(1);
try {
@ -62,7 +64,8 @@ final class CleanerJava6 implements Cleaner {
throw (Throwable) mayBeCleanerField;
}
cleanerField = (Field) mayBeCleanerField;
Field cleanerField = (Field) mayBeCleanerField;
MethodHandles.Lookup lookup = MethodHandles.lookup();
final Object cleaner;
@ -74,15 +77,16 @@ final class CleanerJava6 implements Cleaner {
} else {
fieldOffset = -1;
cleaner = cleanerField.get(direct);
cleanerFieldHandle = lookup.unreflectGetter(cleanerField);
}
clean = cleaner.getClass().getDeclaredMethod("clean");
clean.invoke(cleaner);
cleanHandle = lookup.findVirtual(cleaner.getClass(), "clean", MethodType.methodType(void.class));
cleanHandle.invoke(cleaner);
} catch (Throwable t) {
// We don't have ByteBuffer.cleaner().
fieldOffset = -1;
clean = null;
cleanerFieldHandle = null;
cleanHandle = null;
error = t;
cleanerField = null;
}
if (error == null) {
@ -90,13 +94,13 @@ final class CleanerJava6 implements Cleaner {
} else {
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
}
CLEANER_FIELD = cleanerField;
CLEANER_FIELD_HANDLE = cleanerFieldHandle;
CLEANER_FIELD_OFFSET = fieldOffset;
CLEAN_METHOD = clean;
CLEAN_HANDLE = cleanHandle;
}
static boolean isSupported() {
return CLEANER_FIELD_OFFSET != -1 || CLEANER_FIELD != null;
return CLEANER_FIELD_OFFSET != -1 || CLEANER_FIELD_HANDLE != null;
}
@Override
@ -129,17 +133,17 @@ final class CleanerJava6 implements Cleaner {
}
}
private static void freeDirectBuffer0(ByteBuffer buffer) throws Exception {
private static void freeDirectBuffer0(ByteBuffer buffer) throws Throwable {
final Object cleaner;
// If CLEANER_FIELD_OFFSET == -1 we need to use reflection to access the cleaner, otherwise we can use
// sun.misc.Unsafe.
if (CLEANER_FIELD_OFFSET == -1) {
cleaner = CLEANER_FIELD.get(buffer);
cleaner = CLEANER_FIELD_HANDLE.invoke(buffer);
} else {
cleaner = PlatformDependent0.getObject(buffer, CLEANER_FIELD_OFFSET);
}
if (cleaner != null) {
CLEAN_METHOD.invoke(cleaner);
CLEAN_HANDLE.invoke(cleaner);
}
}
}

View File

@ -18,8 +18,10 @@ package io.netty.util.internal;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
@ -30,34 +32,40 @@ import java.security.PrivilegedAction;
final class CleanerJava9 implements Cleaner {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(CleanerJava9.class);
private static final Method INVOKE_CLEANER;
private static final MethodHandle INVOKE_CLEANER_HANDLE;
static {
final Method method;
final MethodHandle invokeCleanerHandle;
final Throwable error;
if (PlatformDependent0.hasUnsafe()) {
final ByteBuffer buffer = ByteBuffer.allocateDirect(1);
Object maybeInvokeMethod = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
Object maybeInvokeMethodHandle = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
// See https://bugs.openjdk.java.net/browse/JDK-8171377
Method m = PlatformDependent0.UNSAFE.getClass().getDeclaredMethod(
"invokeCleaner", ByteBuffer.class);
m.invoke(PlatformDependent0.UNSAFE, buffer);
MethodHandle m = lookup.findVirtual(
PlatformDependent0.UNSAFE.getClass(),
"invokeCleaner",
MethodType.methodType(void.class, ByteBuffer.class)).bindTo(PlatformDependent0.UNSAFE);
m.invokeExact(buffer);
return m;
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
return e;
} catch (Throwable cause) {
PlatformDependent0.throwException(cause);
return cause;
}
});
if (maybeInvokeMethod instanceof Throwable) {
method = null;
error = (Throwable) maybeInvokeMethod;
if (maybeInvokeMethodHandle instanceof Throwable) {
invokeCleanerHandle = null;
error = (Throwable) maybeInvokeMethodHandle;
} else {
method = (Method) maybeInvokeMethod;
invokeCleanerHandle = (MethodHandle) maybeInvokeMethodHandle;
error = null;
}
} else {
method = null;
invokeCleanerHandle = null;
error = new UnsupportedOperationException("sun.misc.Unsafe unavailable");
}
if (error == null) {
@ -65,11 +73,11 @@ final class CleanerJava9 implements Cleaner {
} else {
logger.debug("java.nio.ByteBuffer.cleaner(): unavailable", error);
}
INVOKE_CLEANER = method;
INVOKE_CLEANER_HANDLE = invokeCleanerHandle;
}
static boolean isSupported() {
return INVOKE_CLEANER != null;
return INVOKE_CLEANER_HANDLE != null;
}
@Override
@ -78,7 +86,7 @@ final class CleanerJava9 implements Cleaner {
// See https://bugs.openjdk.java.net/browse/JDK-8191053.
if (System.getSecurityManager() == null) {
try {
INVOKE_CLEANER.invoke(PlatformDependent0.UNSAFE, buffer);
INVOKE_CLEANER_HANDLE.invokeExact(buffer);
} catch (Throwable cause) {
PlatformDependent0.throwException(cause);
}
@ -90,9 +98,11 @@ final class CleanerJava9 implements Cleaner {
private static void freeDirectBufferPrivileged(final ByteBuffer buffer) {
Exception error = AccessController.doPrivileged((PrivilegedAction<Exception>) () -> {
try {
INVOKE_CLEANER.invoke(PlatformDependent0.UNSAFE, buffer);
INVOKE_CLEANER_HANDLE.invokeExact(PlatformDependent0.UNSAFE, buffer);
} catch (InvocationTargetException | IllegalAccessException e) {
return e;
} catch (Throwable cause) {
PlatformDependent.throwException(cause);
}
return null;
});

View File

@ -19,6 +19,9 @@ import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import sun.misc.Unsafe;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@ -38,14 +41,13 @@ final class PlatformDependent0 {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
private static final long ADDRESS_FIELD_OFFSET;
private static final long BYTE_ARRAY_BASE_OFFSET;
private static final Constructor<?> DIRECT_BUFFER_CONSTRUCTOR;
private static final MethodHandle DIRECT_BUFFER_CONSTRUCTOR_HANDLE;
private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
private static final Method ALLOCATE_ARRAY_METHOD;
private static final MethodHandle ALLOCATE_ARRAY_HANDLE;
private static final int JAVA_VERSION = javaVersion0();
private static final boolean IS_ANDROID = isAndroid0();
private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
private static final Object INTERNAL_UNSAFE;
private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
static final Unsafe UNSAFE;
@ -66,16 +68,14 @@ final class PlatformDependent0 {
static {
final ByteBuffer direct;
Field addressField = null;
Method allocateArrayMethod = null;
Throwable unsafeUnavailabilityCause = null;
MethodHandle allocateArrayHandle = null;
Throwable unsafeUnavailabilityCause;
Unsafe unsafe;
Object internalUnsafe = null;
if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
direct = null;
addressField = null;
unsafe = null;
internalUnsafe = null;
} else {
direct = ByteBuffer.allocateDirect(1);
@ -189,13 +189,13 @@ final class PlatformDependent0 {
ADDRESS_FIELD_OFFSET = -1;
BYTE_ARRAY_BASE_OFFSET = -1;
UNALIGNED = false;
DIRECT_BUFFER_CONSTRUCTOR = null;
ALLOCATE_ARRAY_METHOD = null;
DIRECT_BUFFER_CONSTRUCTOR_HANDLE = null;
ALLOCATE_ARRAY_HANDLE = null;
} else {
Constructor<?> directBufferConstructor;
MethodHandle directBufferConstructorHandle;
long address = -1;
try {
final Object maybeDirectBufferConstructor =
final Object maybeDirectBufferConstructorHandle =
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
try {
final Constructor<?> constructor =
@ -204,34 +204,34 @@ final class PlatformDependent0 {
if (cause != null) {
return cause;
}
return constructor;
} catch (NoSuchMethodException | SecurityException e) {
return MethodHandles.lookup().unreflectConstructor(constructor);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {
return e;
}
});
if (maybeDirectBufferConstructor instanceof Constructor<?>) {
if (maybeDirectBufferConstructorHandle instanceof MethodHandle) {
address = UNSAFE.allocateMemory(1);
// try to use the constructor now
try {
((Constructor<?>) maybeDirectBufferConstructor).newInstance(address, 1);
directBufferConstructor = (Constructor<?>) maybeDirectBufferConstructor;
directBufferConstructorHandle = (MethodHandle) maybeDirectBufferConstructorHandle;
directBufferConstructorHandle.invoke(address, 1);
logger.debug("direct buffer constructor: available");
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
directBufferConstructor = null;
} catch (Throwable ignore) {
directBufferConstructorHandle = null;
}
} else {
logger.debug(
"direct buffer constructor: unavailable",
(Throwable) maybeDirectBufferConstructor);
directBufferConstructor = null;
(Throwable) maybeDirectBufferConstructorHandle);
directBufferConstructorHandle = null;
}
} finally {
if (address != -1) {
UNSAFE.freeMemory(address);
}
}
DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
DIRECT_BUFFER_CONSTRUCTOR_HANDLE = directBufferConstructorHandle;
ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
final boolean unaligned;
@ -300,24 +300,25 @@ final class PlatformDependent0 {
}
});
if (!(maybeException instanceof Throwable)) {
internalUnsafe = maybeException;
final Object finalInternalUnsafe = internalUnsafe;
final Object finalInternalUnsafe = maybeException;
maybeException = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
try {
return finalInternalUnsafe.getClass().getDeclaredMethod(
"allocateUninitializedArray", Class.class, int.class);
} catch (NoSuchMethodException | SecurityException e) {
return MethodHandles.lookup().findVirtual(finalInternalUnsafe.getClass(),
"allocateUninitializedArray",
MethodType.methodType(byte[].class, Class.class, int.class))
.bindTo(finalInternalUnsafe);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {
return e;
}
});
if (maybeException instanceof Method) {
if (maybeException instanceof MethodHandle) {
try {
Method m = (Method) maybeException;
MethodHandle m = (MethodHandle) maybeException;
byte[] bytes = (byte[]) m.invoke(finalInternalUnsafe, byte.class, 8);
assert bytes.length == 8;
allocateArrayMethod = m;
} catch (IllegalAccessException | InvocationTargetException e) {
allocateArrayHandle = m;
} catch (Throwable e) {
maybeException = e;
}
}
@ -332,13 +333,11 @@ final class PlatformDependent0 {
} else {
logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
}
ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
ALLOCATE_ARRAY_HANDLE = allocateArrayHandle;
}
INTERNAL_UNSAFE = internalUnsafe;
logger.debug("java.nio.DirectByteBuffer.<init>(long, int): {}",
DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
DIRECT_BUFFER_CONSTRUCTOR_HANDLE != null ? "available" : "unavailable");
}
static boolean isExplicitNoUnsafe() {
@ -387,7 +386,7 @@ final class PlatformDependent0 {
}
static boolean hasDirectBufferNoCleanerConstructor() {
return DIRECT_BUFFER_CONSTRUCTOR != null;
return DIRECT_BUFFER_CONSTRUCTOR_HANDLE != null;
}
static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
@ -402,13 +401,16 @@ final class PlatformDependent0 {
}
static boolean hasAllocateArrayMethod() {
return ALLOCATE_ARRAY_METHOD != null;
return ALLOCATE_ARRAY_HANDLE != null;
}
static byte[] allocateUninitializedArray(int size) {
try {
return (byte[]) ALLOCATE_ARRAY_METHOD.invoke(INTERNAL_UNSAFE, byte.class, size);
} catch (IllegalAccessException | InvocationTargetException e) {
return (byte[]) ALLOCATE_ARRAY_HANDLE.invoke(byte.class, size);
} catch (Throwable e) {
if (e instanceof Error) {
throw (Error) e;
}
throw new Error(e);
}
}
@ -417,7 +419,7 @@ final class PlatformDependent0 {
ObjectUtil.checkPositiveOrZero(capacity, "capacity");
try {
return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.newInstance(address, capacity);
return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR_HANDLE.invoke(address, capacity);
} catch (Throwable cause) {
// Not expected to ever throw!
if (cause instanceof Error) {

View File

@ -16,8 +16,9 @@
package io.netty.handler.ssl;
import javax.net.ssl.SSLEngine;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
/**
* Contains methods that can be used to detect if conscrypt is usable.
@ -25,14 +26,17 @@ import java.lang.reflect.Method;
final class Conscrypt {
// This class exists to avoid loading other conscrypt related classes using features only available in JDK8+,
// because we need to maintain JDK6+ runtime compatibility.
private static final Method IS_CONSCRYPT_SSLENGINE = loadIsConscryptEngine();
private static final MethodHandle IS_CONSCRYPT_SSLENGINE = loadIsConscryptEngine();
private static final boolean CAN_INSTANCE_PROVIDER = canInstanceProvider();
private static Method loadIsConscryptEngine() {
private static MethodHandle loadIsConscryptEngine() {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class<?> conscryptClass = Class.forName("org.conscrypt.Conscrypt", true,
ConscryptAlpnSslEngine.class.getClassLoader());
return conscryptClass.getMethod("isConscrypt", SSLEngine.class);
return lookup.findStatic(conscryptClass, "isConscrypt",
MethodType.methodType(boolean.class, SSLEngine.class));
} catch (Throwable ignore) {
// Conscrypt was not loaded.
return null;
@ -43,7 +47,8 @@ final class Conscrypt {
try {
Class<?> providerClass = Class.forName("org.conscrypt.OpenSSLProvider", true,
ConscryptAlpnSslEngine.class.getClassLoader());
providerClass.newInstance();
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup.findConstructor(providerClass, MethodType.methodType(void.class)).invoke();
return true;
} catch (Throwable ignore) {
return false;
@ -62,13 +67,16 @@ final class Conscrypt {
}
private static boolean isConscryptEngine(SSLEngine engine) {
try {
return (Boolean) IS_CONSCRYPT_SSLENGINE.invoke(null, engine);
} catch (IllegalAccessException ignore) {
return false;
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex);
if (IS_CONSCRYPT_SSLENGINE != null) {
try {
return (boolean) IS_CONSCRYPT_SSLENGINE.invokeExact(engine);
} catch (IllegalAccessException ignore) {
return false;
} catch (Throwable cause) {
throw new RuntimeException(cause);
}
}
return false;
}
private Conscrypt() { }

View File

@ -19,7 +19,9 @@ package io.netty.handler.ssl;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import java.lang.reflect.Method;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.List;
@ -32,44 +34,52 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
final class Java9SslUtils {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Java9SslUtils.class);
private static final Method SET_APPLICATION_PROTOCOLS;
private static final Method GET_APPLICATION_PROTOCOL;
private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL;
private static final Method SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
private static final MethodHandle SET_APPLICATION_PROTOCOLS;
private static final MethodHandle GET_APPLICATION_PROTOCOL;
private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL;
private static final MethodHandle SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
static {
Method getHandshakeApplicationProtocol = null;
Method getApplicationProtocol = null;
Method setApplicationProtocols = null;
Method setHandshakeApplicationProtocolSelector = null;
Method getHandshakeApplicationProtocolSelector = null;
MethodHandle getHandshakeApplicationProtocol;
MethodHandle getApplicationProtocol;
MethodHandle setApplicationProtocols;
MethodHandle setHandshakeApplicationProtocolSelector;
MethodHandle getHandshakeApplicationProtocolSelector;
try {
SSLContext context = SSLContext.getInstance(JdkSslContext.PROTOCOL);
context.init(null, null, null);
SSLEngine engine = context.createSSLEngine();
getHandshakeApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
SSLEngine.class.getMethod("getHandshakeApplicationProtocol"));
getHandshakeApplicationProtocol.invoke(engine);
getApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
SSLEngine.class.getMethod("getApplicationProtocol"));
getApplicationProtocol.invoke(engine);
MethodHandles.Lookup lookup = MethodHandles.lookup();
getHandshakeApplicationProtocol =
AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocol",
MethodType.methodType(String.class)));
getHandshakeApplicationProtocol.invokeExact(engine);
setApplicationProtocols = AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
SSLParameters.class.getMethod("setApplicationProtocols", String[].class));
setApplicationProtocols.invoke(engine.getSSLParameters(), new Object[]{EmptyArrays.EMPTY_STRINGS});
getApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
lookup.findVirtual(SSLEngine.class, "getApplicationProtocol",
MethodType.methodType(String.class)));
getApplicationProtocol.invokeExact(engine);
setApplicationProtocols = AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
lookup.findVirtual(SSLParameters.class, "setApplicationProtocols",
MethodType.methodType(void.class, String[].class)));
setApplicationProtocols.invokeExact(engine.getSSLParameters(), EmptyArrays.EMPTY_STRINGS);
setHandshakeApplicationProtocolSelector =
AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
SSLEngine.class.getMethod("setHandshakeApplicationProtocolSelector", BiFunction.class));
setHandshakeApplicationProtocolSelector.invoke(engine,
AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
lookup.findVirtual(SSLEngine.class, "setHandshakeApplicationProtocolSelector",
MethodType.methodType(void.class, BiFunction.class)));
setHandshakeApplicationProtocolSelector.invokeExact(engine,
(BiFunction<SSLEngine, List<String>, String>) (sslEngine, strings) -> null);
getHandshakeApplicationProtocolSelector =
AccessController.doPrivileged((PrivilegedExceptionAction<Method>) () ->
SSLEngine.class.getMethod("getHandshakeApplicationProtocolSelector"));
getHandshakeApplicationProtocolSelector.invoke(engine);
AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocolSelector",
MethodType.methodType(BiFunction.class)));
getHandshakeApplicationProtocolSelector.invokeExact(engine);
} catch (Throwable t) {
logger.error("Unable to initialize Java9SslUtils, but the detected javaVersion was: {}",
PlatformDependent.javaVersion(), t);
@ -95,20 +105,20 @@ final class Java9SslUtils {
static String getApplicationProtocol(SSLEngine sslEngine) {
try {
return (String) GET_APPLICATION_PROTOCOL.invoke(sslEngine);
return (String) GET_APPLICATION_PROTOCOL.invokeExact(sslEngine);
} catch (UnsupportedOperationException ex) {
throw ex;
} catch (Exception ex) {
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}
static String getHandshakeApplicationProtocol(SSLEngine sslEngine) {
try {
return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invoke(sslEngine);
return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invokeExact(sslEngine);
} catch (UnsupportedOperationException ex) {
throw ex;
} catch (Exception ex) {
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}
@ -118,10 +128,10 @@ final class Java9SslUtils {
String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS);
try {
SET_APPLICATION_PROTOCOLS.invoke(parameters, new Object[]{protocolArray});
SET_APPLICATION_PROTOCOLS.invokeExact(parameters, protocolArray);
} catch (UnsupportedOperationException ex) {
throw ex;
} catch (Exception ex) {
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
engine.setSSLParameters(parameters);
@ -130,21 +140,22 @@ final class Java9SslUtils {
static void setHandshakeApplicationProtocolSelector(
SSLEngine engine, BiFunction<SSLEngine, List<String>, String> selector) {
try {
SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine, selector);
SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine, selector);
} catch (UnsupportedOperationException ex) {
throw ex;
} catch (Exception ex) {
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}
@SuppressWarnings("unchecked")
static BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) {
try {
return (BiFunction<SSLEngine, List<String>, String>)
GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine);
GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine);
} catch (UnsupportedOperationException ex) {
throw ex;
} catch (Exception ex) {
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}

View File

@ -810,6 +810,11 @@
<version>1.0</version>
</signature>
<ignores>
<!--
We need to whitelist MethodHandle as animal-sniffer can not handle it correctly
https://github.com/mojohaus/animal-sniffer/issues/67
-->
<ignore>java.lang.invoke.MethodHandle</ignore>
<ignore>sun.misc.Unsafe</ignore>
<ignore>sun.misc.Cleaner</ignore>
<ignore>sun.nio.ch.DirectBuffer</ignore>

View File

@ -19,7 +19,9 @@ import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
@ -34,10 +36,10 @@ public final class DnsServerAddressStreamProviders {
private static final InternalLogger LOGGER =
InternalLoggerFactory.getInstance(DnsServerAddressStreamProviders.class);
private static final Constructor<? extends DnsServerAddressStreamProvider> STREAM_PROVIDER_CONSTRUCTOR;
private static final MethodHandle STREAM_PROVIDER_CONSTRUCTOR_HANDLE;
static {
Constructor<? extends DnsServerAddressStreamProvider> constructor = null;
MethodHandle constructorHandle = null;
if (PlatformDependent.isOsx()) {
try {
// As MacOSDnsServerAddressStreamProvider is contained in another jar which depends on this jar
@ -56,20 +58,22 @@ public final class DnsServerAddressStreamProviders {
@SuppressWarnings("unchecked")
Class<? extends DnsServerAddressStreamProvider> providerClass =
(Class<? extends DnsServerAddressStreamProvider>) maybeProvider;
MethodHandles.Lookup lookup = MethodHandles.lookup();
Method method = providerClass.getMethod("ensureAvailability");
method.invoke(null);
constructor = providerClass.getConstructor();
constructor.newInstance();
constructorHandle = lookup.findConstructor(providerClass, MethodType.methodType(void.class));
constructorHandle.invoke();
} else if (!(maybeProvider instanceof ClassNotFoundException)) {
throw (Throwable) maybeProvider;
}
} catch (Throwable cause) {
LOGGER.debug(
"Unable to use MacOSDnsServerAddressStreamProvider, fallback to system defaults", cause);
constructor = null;
constructorHandle = null;
}
}
STREAM_PROVIDER_CONSTRUCTOR = constructor;
STREAM_PROVIDER_CONSTRUCTOR_HANDLE = constructorHandle;
}
private DnsServerAddressStreamProviders() {
@ -83,11 +87,13 @@ public final class DnsServerAddressStreamProviders {
* configuration.
*/
public static DnsServerAddressStreamProvider platformDefault() {
if (STREAM_PROVIDER_CONSTRUCTOR != null) {
if (STREAM_PROVIDER_CONSTRUCTOR_HANDLE != null) {
try {
return STREAM_PROVIDER_CONSTRUCTOR.newInstance();
return (DnsServerAddressStreamProvider) STREAM_PROVIDER_CONSTRUCTOR_HANDLE.invoke();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
// ignore
} catch (Throwable cause) {
PlatformDependent.throwException(cause);
}
}
return unixDefault();

View File

@ -19,13 +19,13 @@ import io.netty.channel.ChannelException;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.DatagramChannelConfig;
import io.netty.channel.socket.DefaultDatagramChannelConfig;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SocketUtils;
import java.lang.reflect.Method;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.StandardSocketOptions;
import java.nio.channels.DatagramChannel;
import java.util.Enumeration;
import java.util.Map;
@ -35,82 +35,6 @@ import java.util.Map;
*/
class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
private static final Object IP_MULTICAST_TTL;
private static final Object IP_MULTICAST_IF;
private static final Object IP_MULTICAST_LOOP;
private static final Method GET_OPTION;
private static final Method SET_OPTION;
static {
ClassLoader classLoader = PlatformDependent.getClassLoader(DatagramChannel.class);
Class<?> socketOptionType = null;
try {
socketOptionType = Class.forName("java.net.SocketOption", true, classLoader);
} catch (Exception e) {
// Not Java 7+
}
Class<?> stdSocketOptionType = null;
try {
stdSocketOptionType = Class.forName("java.net.StandardSocketOptions", true, classLoader);
} catch (Exception e) {
// Not Java 7+
}
Object ipMulticastTtl = null;
Object ipMulticastIf = null;
Object ipMulticastLoop = null;
Method getOption = null;
Method setOption = null;
if (socketOptionType != null) {
try {
ipMulticastTtl = stdSocketOptionType.getDeclaredField("IP_MULTICAST_TTL").get(null);
} catch (Exception e) {
throw new Error("cannot locate the IP_MULTICAST_TTL field", e);
}
try {
ipMulticastIf = stdSocketOptionType.getDeclaredField("IP_MULTICAST_IF").get(null);
} catch (Exception e) {
throw new Error("cannot locate the IP_MULTICAST_IF field", e);
}
try {
ipMulticastLoop = stdSocketOptionType.getDeclaredField("IP_MULTICAST_LOOP").get(null);
} catch (Exception e) {
throw new Error("cannot locate the IP_MULTICAST_LOOP field", e);
}
Class<?> networkChannelClass = null;
try {
networkChannelClass = Class.forName("java.nio.channels.NetworkChannel", true, classLoader);
} catch (Throwable ignore) {
// Not Java 7+
}
if (networkChannelClass == null) {
getOption = null;
setOption = null;
} else {
try {
getOption = networkChannelClass.getDeclaredMethod("getOption", socketOptionType);
} catch (Exception e) {
throw new Error("cannot locate the getOption() method", e);
}
try {
setOption = networkChannelClass.getDeclaredMethod("setOption", socketOptionType, Object.class);
} catch (Exception e) {
throw new Error("cannot locate the setOption() method", e);
}
}
}
IP_MULTICAST_TTL = ipMulticastTtl;
IP_MULTICAST_IF = ipMulticastIf;
IP_MULTICAST_LOOP = ipMulticastLoop;
GET_OPTION = getOption;
SET_OPTION = setOption;
}
private final DatagramChannel javaChannel;
NioDatagramChannelConfig(NioDatagramChannel channel, DatagramChannel javaChannel) {
@ -120,13 +44,21 @@ class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
@Override
public int getTimeToLive() {
return (Integer) getOption0(IP_MULTICAST_TTL);
try {
return javaChannel.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
} catch (IOException e) {
throw new ChannelException(e);
}
}
@Override
public DatagramChannelConfig setTimeToLive(int ttl) {
setOption0(IP_MULTICAST_TTL, ttl);
return this;
try {
javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_TTL, ttl);
return this;
} catch (IOException e) {
throw new ChannelException(e);
}
}
@Override
@ -153,24 +85,40 @@ class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
@Override
public NetworkInterface getNetworkInterface() {
return (NetworkInterface) getOption0(IP_MULTICAST_IF);
try {
return javaChannel.getOption(StandardSocketOptions.IP_MULTICAST_IF);
} catch (IOException e) {
throw new ChannelException(e);
}
}
@Override
public DatagramChannelConfig setNetworkInterface(NetworkInterface networkInterface) {
setOption0(IP_MULTICAST_IF, networkInterface);
return this;
try {
javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
return this;
} catch (IOException e) {
throw new ChannelException(e);
}
}
@Override
public boolean isLoopbackModeDisabled() {
return (Boolean) getOption0(IP_MULTICAST_LOOP);
try {
return javaChannel.getOption(StandardSocketOptions.IP_MULTICAST_LOOP);
} catch (IOException e) {
throw new ChannelException(e);
}
}
@Override
public DatagramChannelConfig setLoopbackModeDisabled(boolean loopbackModeDisabled) {
setOption0(IP_MULTICAST_LOOP, loopbackModeDisabled);
return this;
try {
javaChannel.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, loopbackModeDisabled);
return this;
} catch (IOException e) {
throw new ChannelException(e);
}
}
@Override
@ -184,30 +132,6 @@ class NioDatagramChannelConfig extends DefaultDatagramChannelConfig {
((NioDatagramChannel) channel).clearReadPending0();
}
private Object getOption0(Object option) {
if (GET_OPTION == null) {
throw new UnsupportedOperationException();
} else {
try {
return GET_OPTION.invoke(javaChannel, option);
} catch (Exception e) {
throw new ChannelException(e);
}
}
}
private void setOption0(Object option, Object value) {
if (SET_OPTION == null) {
throw new UnsupportedOperationException();
} else {
try {
SET_OPTION.invoke(javaChannel, option, value);
} catch (Exception e) {
throw new ChannelException(e);
}
}
}
@Override
public <T> boolean setOption(ChannelOption<T> option, T value) {
if (option instanceof NioChannelOption) {