Lemur Tweens.callMethod Primitive fix

“findMethod” that is used by Tweens.callMethod cannot find methods that have primitive parameters like int, float, …

→ int.class.isInstance always returns false
So this statement:

if (paramTypes[i].isInstance(args[i])) {

will always return false when the paramTypes[i] is int.class, float.class,…

So its not working with any method that uses primitives. As soon as you change the signature of the called method to the corresponding wrapped type (e.g Integer instead of int) it works fine. But this is ofc not possible in all cases.

This is one of the possible solutions to fix this:

private static final Map<Class<?>, Class<?>> PRIMITIVES_TO_WRAPPERS
               = new ImmutableMap.Builder<Class<?>, Class<?>>()
                       .put(boolean.class, Boolean.class)
                       .put(byte.class, Byte.class)
                       .put(char.class, Character.class)
                       .put(double.class, Double.class)
                       .put(float.class, Float.class)
                       .put(int.class, Integer.class)
                       .put(long.class, Long.class)
                       .put(short.class, Short.class)
                       .put(void.class, Void.class)
                       .build();
       
       private static Method findMethod(Class type, String name, Object... args) {
           for (Method m : type.getDeclaredMethods()) {
               if (!Objects.equals(m.getName(), name)) {
                   continue;
               }
               Class[] paramTypes = m.getParameterTypes();
               if (paramTypes.length != args.length) {
                   continue;
               }
               int matches = 0;
               for (int i = 0; i < args.length; i++) {
                   if( PRIMITIVES_TO_WRAPPERS.containsKey(paramTypes[i]) ) {
                       paramTypes[i] = PRIMITIVES_TO_WRAPPERS.get(paramTypes[i]);
                   }
                   if (paramTypes[i].isInstance(args[i])) {
                       matches++;
                   }
               }
               if (matches == args.length) {
                   return m;
               }
           }
           if (type.getSuperclass() != null) {
               return findMethod(type.getSuperclass(), name, args);
           }
           return null;
       }
1 Like

Can you show me how you were calling the method?

1 Like

e.g.:

Tweens.callMethod(control, "setEnabled", true);

1 Like

I think your patch can probably be simplified with:
http://google.github.io/guava/releases/19.0/api/docs/com/google/common/primitives/Primitives.html

…don’t have time to look in detail myself right now.

1 Like

I know, but initially I didn’t want to create an additional dependency for this little issue. But I now saw, that you already have the guava dependency. So here we go:

private static Method findMethod(Class type, String name, Object... args) {
            for (Method m : type.getDeclaredMethods()) {
                if (!Objects.equals(m.getName(), name)) {
                    continue;
                }
                Class[] paramTypes = m.getParameterTypes();
                if (paramTypes.length != args.length) {
                    continue;
                }
                int matches = 0;
                for (int i = 0; i < args.length; i++) {
                    if( paramTypes[i].isPrimitive() )
                        paramTypes[i] = Primitives.wrap( paramTypes[i] );
                    if (paramTypes[i].isInstance(args[i])) {
                        matches++;
                    }
                }
                if (matches == args.length) {
                    return m;
                }
            }
            if (type.getSuperclass() != null) {
                return findMethod(type.getSuperclass(), name, args);
            }
            return null;
        }
1 Like

I think it’s dangerous to modify the paramTypes array directly. It’s been a long time since I looked in the Java source code but I sort of remember it reusing them.

Now, saying that, I’ve been watching Java source code since 1.2… so who knows which version I’m remembering anyway.

Either way, it’s probably better to just grab the type once in the loop instead of four separate index lookups.

But no worries. I think I want to restructure this method a bit and break the args check out into its own method to clean this loop up and avoid the matches counting.

At least you have code that’s working for you in the mean time. Thanks for hunting this down.

2 Likes