Skip to content

Commit

Permalink
CLJ-2414 Fix reflective lookup to include interfaces which may contai…
Browse files Browse the repository at this point in the history
…n default methods

Signed-off-by: Stuart Halloway <stu@cognitect.com>
  • Loading branch information
puredanger authored and stuarthalloway committed Oct 9, 2018
1 parent 00d03ba commit dddc412
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 12 deletions.
44 changes: 33 additions & 11 deletions src/jvm/clojure/lang/Reflector.java
Expand Up @@ -19,9 +19,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

public class Reflector{
Expand Down Expand Up @@ -58,17 +56,41 @@ private static boolean canAccess(Method m, Object target) {
}
}

private static Collection<Class> interfaces(Class c) {
Set<Class> interfaces = new HashSet<Class>();
Deque<Class> toWalk = new ArrayDeque<Class>();
toWalk.addAll(Arrays.asList(c.getInterfaces()));
Class iface = toWalk.poll();
while (iface != null) {
interfaces.add(iface);
toWalk.addAll(Arrays.asList(iface.getInterfaces()));
iface = toWalk.poll();
}
return interfaces;
}

private static Method tryFindMethod(Class c, Method m) {
if(c == null) return null;
try {
return c.getMethod(m.getName(), m.getParameterTypes());
} catch(NoSuchMethodException e) {
return null;
}
}

private static Method toAccessibleSuperMethod(Method m, Object target) {
Method selected = m;
while(selected != null && !canAccess(selected, target)) {
Class<?> s = selected.getDeclaringClass().getSuperclass();
try {
selected = s.getMethod(m.getName(), m.getParameterTypes());
} catch(NoSuchMethodException e) {
// ignore
}
while(selected != null) {
if(canAccess(selected, target)) return selected;
selected = tryFindMethod(selected.getDeclaringClass().getSuperclass(), m);
}
return selected;

Collection<Class> interfaces = interfaces(m.getDeclaringClass());
for(Class c : interfaces) {
selected = tryFindMethod(c, m);
if(selected != null) return selected;
}
return null;
}

public static Object invokeInstanceMethod(Object target, String methodName, Object[] args) {
Expand Down
17 changes: 16 additions & 1 deletion test/clojure/test_clojure/reflect.clj
Expand Up @@ -36,4 +36,19 @@

(def inst (IBar$Factory/get))
(deftest invoking-nonpublic-super
(is (= "stuff" (.stuff inst))))
(is (= "stuff" (.stuff inst))))

(defn- checkCLJ2066 [f]
;; intentionally reflective call
(is (not (nil? (.createXMLStreamReader f (java.io.StringReader. ""))))))

(defn- checkCLJ2414 [p]
;; intentionally reflective call
(is (false? (.startsWith p "s"))))

(deftest invoke-checks-accessibility
;; CLJ-2066 - reflector finds method in private class. this is invokable, but an illegal access per modules
(checkCLJ2066 (javax.xml.stream.XMLInputFactory/newInstance))

;; CLJ-2414 - find default method on interface of inaccessible class
(checkCLJ2414 (java.nio.file.Paths/get "src" (into-array String []))))

0 comments on commit dddc412

Please sign in to comment.