Skip to content

Instantly share code, notes, and snippets.

@wreulicke
Last active January 1, 2018 21:10
Show Gist options
  • Save wreulicke/5d5e84763a63d52132516dd8eddca64d to your computer and use it in GitHub Desktop.
Save wreulicke/5d5e84763a63d52132516dd8eddca64d to your computer and use it in GitHub Desktop.
Get method name from method reference
package com.github.wreulicke;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.function.Consumer;
import java.util.function.Function;
import org.junit.Test;
import sun.misc.Unsafe;
public class GetMethodNameTest {
private static Method GET_CONSTANT_POOL;
private static Method GET_CONSTANT_POOL_SIZE;
private static Method GET_CONSTANT_POOL_METHOD_AT;
private static Unsafe UNSAFE;
static {
double JAVA_VERSION = Double.parseDouble(System.getProperty("java.specification.version", "0"));
try {
UNSAFE = AccessController.doPrivileged(new PrivilegedExceptionAction<Unsafe>() {
@Override
public Unsafe run() throws Exception {
final Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
}
});
GET_CONSTANT_POOL = Class.class.getDeclaredMethod("getConstantPool");
String constantPoolName =
JAVA_VERSION < 9 ? "sun.reflect.ConstantPool" : "jdk.internal.reflect.ConstantPool";
Class<?> constantPoolClass = Class.forName(constantPoolName);
GET_CONSTANT_POOL_SIZE = constantPoolClass.getDeclaredMethod("getSize");
GET_CONSTANT_POOL_METHOD_AT = constantPoolClass.getDeclaredMethod("getMethodAt", int.class);
Field overrideField = AccessibleObject.class.getDeclaredField("override");
long overrideFieldOffset = UNSAFE.objectFieldOffset(overrideField);
UNSAFE.putBoolean(GET_CONSTANT_POOL, overrideFieldOffset, true);
UNSAFE.putBoolean(GET_CONSTANT_POOL_SIZE, overrideFieldOffset, true);
UNSAFE.putBoolean(GET_CONSTANT_POOL_METHOD_AT, overrideFieldOffset, true);
} catch (Exception e) {
e.printStackTrace();
}
}
// thanks to https://github.com/jhalterman/typetools
@Test
public void test() throws Exception {
Consumer<String> lambda1 = System.out::println;
Function<String, String> lambda2 = String::new;
assertTrue("println".equals(getMethodName(lambda1)));
assertTrue("new".equals(getMethodName(lambda2)));
}
private static String getMethodName(Object methodRef) throws Exception {
Class<?> type = methodRef.getClass();
Object constant = GET_CONSTANT_POOL.invoke(type);
for (int i = 0; i < getConstantPoolSize(constant); i++) {
Member member = getConstantPoolMethodAt(constant, i);
if (member == null
|| (member.getDeclaringClass().getName().equals("java.lang.invoke.SerializedLambda")) // ignore SerializedLambda
|| member.getDeclaringClass().isAssignableFrom(type))
continue;
if (member instanceof Constructor) {
return "new";
}
if (member instanceof Method) {
return member.getName();
}
}
return null;
}
private static int getConstantPoolSize(Object constantPool) {
try {
return (Integer) GET_CONSTANT_POOL_SIZE.invoke(constantPool);
} catch (Exception ignore) {
return 0;
}
}
private static Member getConstantPoolMethodAt(Object constantPool, int i) {
try {
return (Member) GET_CONSTANT_POOL_METHOD_AT.invoke(constantPool, i);
} catch (Exception ignore) {
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment