Obtaining the Class method using build-time generated metadata

4 views
Skip to first unread message

Craig Neuwirt

unread,
Dec 2, 2018, 10:24:55 AM12/2/18
to ClassGraph-Users
Hello,

   I am using ClassGraph to generate build-time class/method information for an Android application.  Is it possible to translate the class name and stringified method signature into a java.lang.Class.getMethod call?  To avoid the high cost of visiting all method in a class, I would like to target an exact method as identified by my ClassGraph query

thanks,
  craig

Luke Hutchison

unread,
Dec 2, 2018, 12:17:51 PM12/2/18
to Craig Neuwirt, classgra...@googlegroups.com
There's no automated way, but as the result of the scan, you can save the name of the method and the names of the (type-erased/bare) parameter types of the method, in some sort of file. Then at runtime you will need to load the class of each of the parameters, and pass that into Class::getMethod.

Build-time is easy -- just write out the types from ClassGraph's MethodInfo and MethodParameterInfo objects.

The tricky parts at runtime would be:

(1) Dealing with primitive parameters (which have type e.g. int.class or Integer.TYPE as their type). Just set up a switch statement that returns int.class if the parameter type string obtained during build time is "int", etc.

(2) Dealing with arrays, which have type e.g. x.y.z.MyType[][][].class or int[][].class, which is a bit fiddly to get a Class<?> reference for... you have to do the following:

            // First get a Class<?> reference for the component type (the type before the "[][]..." array brackets,
            // e.g. bareTypeName = "x.y.z.MyType"), just as with non-array types. The loadClassByName() method
            // should have special cases for primitive types, as in point (1) above.
            Class<?> componentType = (Class<?>) loadClassByName(bareTypeName);

            // Build an array to hold the size of each dimension, filled with zeroes
            int[] dims = (int[]) Array.newInstance(int.class, numArrayDims);

            // Build a zero-sized array of the required number of dimensions, using the component type
            Object arrayInstance = Array.newInstance(componentType, dims);

            // Get the class of the array type
            Class<?> arrayType = arrayInstance.getClass();


If you use the raw type descriptor string for a type, e.g. "[[[Lx.y.z.MyType;" for x.y.z.MyType.class , or "[[I" for int.class , then it's a lot easier to load classes: just do:

            Class<?> twoDIntArrayType = class.forName("[[I");
            Class<?> threeDMyTypeArrayType = class.forName("[[[Lx.y.z.MyType;");

But right now ClassGraph doesn't expose these internal type descriptor strings in the API. I could add an API call to return these directly, I guess, or you could just turn the type strings returned by ClassGraph into this form (e.g. "x.y.z.MyType[][][]" -> "[[[Lx.y.z.MyType;", which shouldn't be too difficult).

Hope this helps!



--
You received this message because you are subscribed to the Google Groups "ClassGraph-Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to classgraph-use...@googlegroups.com.
To post to this group, send email to classgra...@googlegroups.com.
Visit this group at https://groups.google.com/group/classgraph-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/classgraph-users/3cd2399c-b9ca-40d4-8d8e-e9e2c250395b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Craig Neuwirt

unread,
Dec 2, 2018, 1:06:33 PM12/2/18
to ClassGraph-Users
Hey Luke,

Thanks for there quick response. That’s the approach that I plan to take and appreciate the confirmation. My only concern comes from claims that calling Class. getMethod is O(n) and requires visiting all methods even after a match is found to accommodate Java’s covariant result support. If this is the case, do you think it will yield better performance if I request individual methods or do one iteration of all of them?

Thanks

Luke Hutchison

unread,
Dec 2, 2018, 1:48:11 PM12/2/18
to Craig Neuwirt, classgra...@googlegroups.com
Oh, I didn't know about the O(N) performance of getMethod, and that's really unfortunate. I assumed it would use a HashMap or something (maybe a "perfect hashmap", since all the methods are known in advance), since that's O(1), but probably the Java designers were counting on the number of methods in a class being relatively small, and comparable to the complexity of hashing parameter types, looking up a hash bucket, and possibly chaining (in case of collisions).

Actually, since for a given class, the number of methods in a given class is fixed, you can think of Class::getMethod as being "O(1) per class", but O(N) across all classes...

I share your aversion to anything taking longer than O(1) or O[log(N)], but unless the number of methods you are looking up is huge, maybe O(N) for a small value of N is a cost that it would be OK to pay.

If you find you are looking up the same method many times, you could try caching the mapping from the type signature string to the method reference in your own hashmap, and see if it's faster than looking it up using Class::getMethod. I wrote a JSON parser for ClassGraph though, and used this technique internally to cache type lookups, but I found that actually it was faster just to use the reflection API, so I got rid of most of the caching. Your mileage may vary.

The alternative to using the reflection API is to iterate through all N methods, as you suggest, and for each one, match it to the M method lookups you need to perform against that class. Even assuming M <= N, if you have deduplicated method lookups, M is still O(N) -- so you are left with a total time of O(N^2) for M lookups of N methods using the Java API, or O(N + N log N) for a smart implementation that "zips together" a sorted list of the M method lookups against a sorted list of the N method definitions. You basically use a running pointer through sorted lists on the two sides of the matching problem, and step one or the other forward depending on whether there is a gap in the sorted order of one list vs. the other. This may be more complexity than you want to deal with though.

Hope this helps!
Luke



--
You received this message because you are subscribed to the Google Groups "ClassGraph-Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to classgraph-use...@googlegroups.com.
To post to this group, send email to classgra...@googlegroups.com.
Visit this group at https://groups.google.com/group/classgraph-users.
Reply all
Reply to author
Forward
0 new messages