aboutsummaryrefslogtreecommitdiff
path: root/sandbox
diff options
context:
space:
mode:
authorMichael Hoisie <hoisie@google.com>2024-03-28 10:21:29 -0700
committerCopybara-Service <copybara-worker@google.com>2024-03-28 10:22:16 -0700
commitf3df9dbeb82697467beef8d888b4b9df28270f7f (patch)
tree73be3d66b8a148b9d0fa49aaf5346b57ac96a566 /sandbox
parente1dffaa95595f57e4f08a7bfb86c004409f35e58 (diff)
downloadrobolectric-f3df9dbeb82697467beef8d888b4b9df28270f7f.tar.gz
Update ShadowWrangler to iterate through methods using getDeclaredMethods
Previously, in ShadowWrangler, shadow method lookup was performed using ShadowClass.findDeclaredMethod. It was called once to look for an exact match of a shadow method, and sometimes called again to check for a looseSignatures match. There are plans to add new features and capabilities to the way that shadow methods are matched. For example: * looseSignatures being replaced with a more minimal @ClassName("internal.type") annotation. * If the signature of a method changes across SDK levels, we could introduce different method names that map to the same method name. However, to search for methods that cannot be matched using ShadowClass.findDeclaredMethod, it is required to iterate over all candidate methods using ShadowClass.findDeclaredMethods. There were some questions about the performance of using ShadowClass.findDeclaredMethods + iteration. However, after some preliminary benchmarks, this approach is surprisingly approximately 25% faster than using ShadowClass.findDeclaredMethod. It is perhaps due to the internal caching of ShadowClass.findDeclaredMethods. With this change, it will be possible to perform more advanced filtering and searching for methods. For #8841 PiperOrigin-RevId: 619979740
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java74
1 files changed, 39 insertions, 35 deletions
diff --git a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java
index 1c6d8c19c..ab480d69a 100644
--- a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java
+++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java
@@ -18,6 +18,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Priority;
@@ -305,13 +306,8 @@ public class ShadowWrangler implements ClassHandler {
Class<?>[] types,
ShadowInfo shadowInfo,
Class<?> shadowClass) {
- Method method = findShadowMethodDeclaredOnClass(shadowClass, name, types);
-
- if (method == null && shadowInfo.looseSignatures) {
- Class<?>[] genericTypes = MethodType.genericMethodType(types.length).parameterArray();
- method = findShadowMethodDeclaredOnClass(shadowClass, name, genericTypes);
- }
-
+ Method method =
+ findShadowMethodDeclaredOnClass(shadowClass, name, types, shadowInfo.looseSignatures);
if (method != null) {
return method;
} else {
@@ -334,41 +330,49 @@ public class ShadowWrangler implements ClassHandler {
}
private Method findShadowMethodDeclaredOnClass(
- Class<?> shadowClass, String methodName, Class<?>[] paramClasses) {
- try {
- Method method = shadowClass.getDeclaredMethod(methodName, paramClasses);
-
- // todo: allow per-version overloading
- // if (method == null) {
- // String methodPrefix = name + "$$";
- // for (Method candidateMethod : shadowClass.getDeclaredMethods()) {
- // if (candidateMethod.getName().startsWith(methodPrefix)) {
- //
- // }
- // }
- // }
-
- if (isValidShadowMethod(method)) {
- method.setAccessible(true);
- return method;
- } else {
- return null;
+ Class<?> shadowClass, String methodName, Class<?>[] paramClasses, boolean looseSignatures) {
+ Method foundMethod = null;
+ for (Method method : shadowClass.getDeclaredMethods()) {
+ if (!method.getName().equals(methodName)
+ || method.getParameterCount() != paramClasses.length) {
+ continue;
}
- } catch (NoSuchMethodException e) {
- return null;
- }
- }
+ if (!Modifier.isPublic(method.getModifiers())
+ && !Modifier.isProtected(method.getModifiers())) {
+ continue;
+ }
- private boolean isValidShadowMethod(Method method) {
- int modifiers = method.getModifiers();
- if (!Modifier.isPublic(modifiers) && !Modifier.isProtected(modifiers)) {
- return false;
+ if (Arrays.equals(method.getParameterTypes(), paramClasses)
+ && shadowMatcher.matches(method)) {
+ // Found an exact match, we can exit early.
+ foundMethod = method;
+ break;
+ }
+ if (looseSignatures) {
+ boolean allParameterTypesAreObject = true;
+ for (Class<?> paramClass : method.getParameterTypes()) {
+ if (!paramClass.equals(Object.class)) {
+ allParameterTypesAreObject = false;
+ break;
+ }
+ }
+ if (allParameterTypesAreObject && shadowMatcher.matches(method)) {
+ // Found a looseSignatures match, but continue looking for an exact match.
+ foundMethod = method;
+ }
+ }
}
- return shadowMatcher.matches(method);
+ if (foundMethod != null) {
+ foundMethod.setAccessible(true);
+ return foundMethod;
+ } else {
+ return null;
+ }
}
+
@Override
public Object intercept(String signature, Object instance, Object[] params, Class theClass)
throws Throwable {