Skip to content

Commit

Permalink
temp try for no allocation to get single method
Browse files Browse the repository at this point in the history
  • Loading branch information
liach committed Nov 5, 2024
1 parent c33a8f5 commit 789c72c
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 102 deletions.
77 changes: 46 additions & 31 deletions src/java.base/share/classes/java/lang/Class.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,8 @@
import java.util.stream.Collectors;

import jdk.internal.constant.ConstantUtils;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.loader.BootLoader;
import jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.misc.PreviewFeatures;
import jdk.internal.misc.Unsafe;
import jdk.internal.module.Resources;
import jdk.internal.reflect.CallerSensitive;
Expand Down Expand Up @@ -2458,7 +2456,10 @@ public Method getMethod(String name, Class<?>... parameterTypes)
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
Method method = getMethod0(name, parameterTypes);
Method method = getMethodsRecursive(
name,
parameterTypes == null ? EMPTY_CLASS_ARRAY : parameterTypes,
/* includeStatic */ true, /* publicOnly */ true);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
Expand Down Expand Up @@ -2911,7 +2912,7 @@ public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
if (sm != null) {
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
Expand Down Expand Up @@ -2954,8 +2955,8 @@ List<Method> getDeclaredPublicMethods(String name, Class<?>... parameterTypes) {
* the specified name and parameters, or null if not found
*/
Method findMethod(boolean publicOnly, String name, Class<?>... parameterTypes) {
PublicMethods.MethodList res = getMethodsRecursive(name, parameterTypes, true, publicOnly);
return res == null ? null : getReflectionFactory().copyMethod(res.getMostSpecific());
var res = getMethodsRecursive(name, parameterTypes, true, publicOnly);
return res == null ? null : getReflectionFactory().copyMethod(res);
}

/**
Expand Down Expand Up @@ -3761,12 +3762,14 @@ private Field getField0(String name) {
// This method does not copy the returned Method object!
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes)
Class<?>[] parameterTypes,
boolean includeStatic)
{
ReflectionFactory fact = getReflectionFactory();
Method res = null;
for (Method m : methods) {
if (m.getName().equals(name)
if ((includeStatic || !Modifier.isStatic(m.getModifiers()))
&& m.getName().equals(name)
&& arrayContentsEq(parameterTypes,
fact.getExecutableSharedParameterTypes(m))
&& (res == null
Expand All @@ -3782,25 +3785,14 @@ && arrayContentsEq(parameterTypes,
// Returns a "root" Method object. This Method object must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyMethod.
private Method getMethod0(String name, Class<?>[] parameterTypes) {
PublicMethods.MethodList res = getMethodsRecursive(
name,
parameterTypes == null ? EMPTY_CLASS_ARRAY : parameterTypes,
/* includeStatic */ true, /* publicOnly */ true);
return res == null ? null : res.getMostSpecific();
}

// Returns a list of "root" Method objects. These Method objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyMethod.
private PublicMethods.MethodList getMethodsRecursive(String name,
Class<?>[] parameterTypes,
boolean includeStatic,
boolean publicOnly) {
// includeStatic == false means we are recursing superinterfaces
private Method getMethodsRecursive(String name,
Class<?>[] parameterTypes,
boolean includeStatic,
boolean publicOnly) {
// 1st check declared methods
Method[] methods = privateGetDeclaredMethods(publicOnly);
PublicMethods.MethodList res = PublicMethods.MethodList
.filter(methods, name, parameterTypes, includeStatic);
var res = searchMethods(methods, name, parameterTypes, includeStatic);
// if there is at least one match among declared methods, we need not
// search any further as such match surely overrides matching methods
// declared in superclass(es) or interface(s).
Expand All @@ -3809,22 +3801,45 @@ private PublicMethods.MethodList getMethodsRecursive(String name,
}

// if there was no match among declared methods,
// we must consult the superclass (if any) recursively...
Class<?> sc = getSuperclass();
if (sc != null) {
res = sc.getMethodsRecursive(name, parameterTypes, includeStatic, publicOnly);
// we must consult the superclass (if any) recursively... if we are not recursing an interface
Class<?> sc;
if (includeStatic && (sc = getSuperclass()) != null) {
res = sc.getMethodsRecursive(name, parameterTypes, true, publicOnly);
}

// ...and coalesce the superclass methods with methods obtained
// from directly implemented interfaces excluding static methods...
for (Class<?> intf : getInterfaces(/* cloneArray */ false)) {
res = PublicMethods.MethodList.merge(
res, intf.getMethodsRecursive(name, parameterTypes, /* includeStatic */ false, publicOnly));
res = selectSingleMethod(res, intf.getMethodsRecursive(name, parameterTypes, /* includeStatic */ false, publicOnly));
}

return res;
}

private static Method selectSingleMethod(Method existing, Method candidate) {
// name and param types already match if non-null
if (candidate == null) return existing;
if (existing == null) return candidate;
// 1. More specific return type
var exRet = existing.getReturnType();
var canRet = candidate.getReturnType();
if (exRet != canRet)
return exRet.isAssignableFrom(canRet) ? candidate : existing;
// Return types must be the same by this point
// 2. Class first, then more specific declaring interface
var exOwner = existing.getDeclaringClass();
var canOwner = candidate.getDeclaringClass();
assert exOwner != canOwner;
// Class over interface
var exInterface = exOwner.isInterface();
if (exInterface != canOwner.isInterface()) // One class, one interface
return exInterface ? candidate : existing;
// More specific or first appearing interface (class already short-circuits)
if (exOwner.isAssignableFrom(canOwner))
return candidate;
return existing;
}

// Returns a "root" Constructor object. This Constructor object must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyConstructor.
Expand Down
73 changes: 2 additions & 71 deletions src/java.base/share/classes/java/lang/PublicMethods.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -27,7 +27,6 @@
import jdk.internal.reflect.ReflectionFactory;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.Arrays;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -101,16 +100,6 @@ private static final class Key {
ptypes = reflectionFactory.getExecutableSharedParameterTypes(method);
}

static boolean matches(Method method,
String name, // may not be interned
Class<?>[] ptypes) {
return method.getName().equals(name) &&
Arrays.equals(
reflectionFactory.getExecutableSharedParameterTypes(method),
ptypes
);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -131,54 +120,14 @@ public int hashCode() {
* Node of a inked list containing Method(s) sharing the same
* (name, parameter types) tuple.
*/
static final class MethodList {
private static final class MethodList {
Method method;
MethodList next;

private MethodList(Method method) {
this.method = method;
}

/**
* @return the head of a linked list containing given {@code methods}
* filtered by given method {@code name}, parameter types
* {@code ptypes} and including or excluding static methods as
* requested by {@code includeStatic} flag.
*/
static MethodList filter(Method[] methods, String name,
Class<?>[] ptypes, boolean includeStatic) {
MethodList head = null, tail = null;
for (Method method : methods) {
if ((includeStatic || !Modifier.isStatic(method.getModifiers())) &&
Key.matches(method, name, ptypes)) {
if (tail == null) {
head = tail = new MethodList(method);
} else {
tail = tail.next = new MethodList(method);
}
}
}
return head;
}

/**
* This method should only be called with the {@code head} (possibly null)
* of a list of Method(s) that share the same (method name, parameter types)
* and another {@code methodList} that also contains Method(s) with the
* same and equal (method name, parameter types) as the 1st list.
* It modifies the 1st list and returns the head of merged list
* containing only the most specific methods for each signature
* (i.e. return type). The returned head of the merged list may or
* may not be the same as the {@code head} of the given list.
* The given {@code methodList} is not modified.
*/
static MethodList merge(MethodList head, MethodList methodList) {
for (MethodList ml = methodList; ml != null; ml = ml.next) {
head = merge(head, ml.method);
}
return head;
}

private static MethodList merge(MethodList head, Method method) {
Class<?> dclass = method.getDeclaringClass();
Class<?> rtype = method.getReturnType();
Expand Down Expand Up @@ -252,23 +201,5 @@ private int length() {
}
return len;
}

/**
* @return 1st method in list with most specific return type
*/
Method getMostSpecific() {
Method m = method;
Class<?> rt = m.getReturnType();
for (MethodList ml = next; ml != null; ml = ml.next) {
Method m2 = ml.method;
Class<?> rt2 = m2.getReturnType();
if (rt2 != rt && rt.isAssignableFrom(rt2)) {
// found more specific return type
m = m2;
rt = rt2;
}
}
return m;
}
}
}

0 comments on commit 789c72c

Please sign in to comment.