From 4b73ca431c82587c95586ea51adc164c713c3aca Mon Sep 17 00:00:00 2001 From: Max Weber Date: Mon, 21 Oct 2019 01:15:23 -0600 Subject: [PATCH] runelite-client: use privateLookupIn for invokespecialing defaults The private ctor we used for MethodHandles$Lookup is gone in jdk-14. --- .../config/ConfigInvocationHandler.java | 9 +-- .../runelite/client/eventbus/EventBus.java | 28 +------- .../net/runelite/client/util/ReflectUtil.java | 72 +++++++++++++++++++ 3 files changed, 76 insertions(+), 33 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java index ae0126cc89..b78117b8ce 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java @@ -26,12 +26,11 @@ package net.runelite.client.config; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Objects; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.util.ReflectUtil; @Slf4j class ConfigInvocationHandler implements InvocationHandler @@ -165,12 +164,8 @@ class ConfigInvocationHandler implements InvocationHandler static Object callDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { - // Call the default method implementation - https://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/ - Constructor constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class); - constructor.setAccessible(true); - Class declaringClass = method.getDeclaringClass(); - return constructor.newInstance(declaringClass, MethodHandles.Lookup.PUBLIC | MethodHandles.Lookup.PRIVATE) + return ReflectUtil.privateLookupIn(declaringClass) .unreflectSpecial(method, declaringClass) .bindTo(proxy) .invokeWithArguments(args); diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java index a28f2f4371..588752fd46 100644 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java +++ b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java @@ -34,8 +34,6 @@ import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.function.Consumer; @@ -45,6 +43,7 @@ import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import lombok.Value; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.util.ReflectUtil; @Slf4j @RequiredArgsConstructor @@ -141,7 +140,7 @@ public class EventBus try { - final MethodHandles.Lookup caller = privateLookupIn(clazz); + final MethodHandles.Lookup caller = ReflectUtil.privateLookupIn(clazz); final MethodType subscription = MethodType.methodType(void.class, parameterClazz); final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription); final CallSite site = LambdaMetafactory.metafactory( @@ -223,27 +222,4 @@ public class EventBus } } } - - private static MethodHandles.Lookup privateLookupIn(Class clazz) throws IllegalAccessException, NoSuchFieldException, InvocationTargetException - { - try - { - // Java 9+ has privateLookupIn method on MethodHandles, but since we are shipping and using Java 8 - // we need to access it via reflection. This is preferred way because it's Java 9+ public api and is - // likely to not change - final Method privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); - return (MethodHandles.Lookup) privateLookupIn.invoke(null, clazz, MethodHandles.lookup()); - } - catch (NoSuchMethodException e) - { - // In Java 8 we first do standard lookupIn class - final MethodHandles.Lookup lookupIn = MethodHandles.lookup().in(clazz); - - // and then we mark it as trusted for private lookup via reflection on private field - final Field modes = MethodHandles.Lookup.class.getDeclaredField("allowedModes"); - modes.setAccessible(true); - modes.setInt(lookupIn, -1); // -1 == TRUSTED - return lookupIn; - } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java new file mode 100644 index 0000000000..76530b6ab6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * Copyright (c) 2018, Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.util; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class ReflectUtil +{ + private ReflectUtil() + { + } + + public static MethodHandles.Lookup privateLookupIn(Class clazz) + { + try + { + // Java 9+ has privateLookupIn method on MethodHandles, but since we are shipping and using Java 8 + // we need to access it via reflection. This is preferred way because it's Java 9+ public api and is + // likely to not change + final Method privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); + return (MethodHandles.Lookup) privateLookupIn.invoke(null, clazz, MethodHandles.lookup()); + } + catch (InvocationTargetException | IllegalAccessException e) + { + throw new RuntimeException(e); + } + catch (NoSuchMethodException e) + { + try + { + // In Java 8 we first do standard lookupIn class + final MethodHandles.Lookup lookupIn = MethodHandles.lookup().in(clazz); + + // and then we mark it as trusted for private lookup via reflection on private field + final Field modes = MethodHandles.Lookup.class.getDeclaredField("allowedModes"); + modes.setAccessible(true); + modes.setInt(lookupIn, -1); // -1 == TRUSTED + return lookupIn; + } + catch (ReflectiveOperationException ex) + { + throw new RuntimeException(ex); + } + } + } +}