Skip to content

Commit 30dae39

Browse files
committed
Revert d03e1ca (#345) which causes rev.dep. failure
1 parent 0d054a4 commit 30dae39

1 file changed

Lines changed: 34 additions & 124 deletions

File tree

src/java/RJavaTools.java

Lines changed: 34 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,13 @@
1717
// You should have received a copy of the GNU General Public License
1818
// along with rJava. If not, see <http://www.gnu.org/licenses/>.
1919

20-
import java.lang.invoke.MethodHandles;
21-
import java.lang.invoke.MethodHandles.Lookup;
22-
import java.lang.reflect.AccessibleObject;
23-
import java.lang.reflect.Constructor ;
24-
import java.lang.reflect.Executable;
20+
import java.lang.reflect.Method ;
2521
import java.lang.reflect.Field ;
22+
import java.lang.reflect.Constructor ;
2623
import java.lang.reflect.InvocationTargetException ;
27-
import java.lang.reflect.Member ;
28-
import java.lang.reflect.Method ;
2924
import java.lang.reflect.Modifier ;
30-
import java.util.ArrayDeque;
31-
import java.util.Arrays;
32-
import java.util.Deque;
33-
import java.util.HashSet;
25+
import java.lang.reflect.Member ;
26+
3427
import java.util.Vector ;
3528

3629

@@ -42,23 +35,6 @@
4235
*/
4336
public class RJavaTools {
4437

45-
/* dual path for Java 8 and 9+ to check actual accessibility */
46-
private static final Method CAN_ACCESS;
47-
/* Java 8 accessibility checker */
48-
private static final Lookup LOOKUP = MethodHandles.publicLookup();
49-
50-
static {
51-
Method canAccess = null;
52-
try {
53-
canAccess = Executable.class.getMethod("canAccess", Object.class);
54-
} catch (NoSuchMethodException e) {
55-
// Java 8-
56-
} catch (SecurityException e) {
57-
// Java 8-
58-
}
59-
CAN_ACCESS = canAccess;
60-
}
61-
6238
/**
6339
* Returns an inner class of the class with the given simple name
6440
*
@@ -352,97 +328,7 @@ public static boolean hasMethod(Object o, String name) {
352328
return classHasMethod(o.getClass(), name, false);
353329
}
354330

355-
/**
356-
* Tests whether a given executable is accessible without actually calling {@link AccessibleObject#isAccessible()} which is unreliable.
357-
*
358-
* @param executable the given method/constructor
359-
* @param o the target object
360-
* @return true if the method can be accessed from RJavaTools
361-
*/
362-
public static boolean canAccess(Executable executable, Object o){
363-
try {
364-
if (CAN_ACCESS != null) {
365-
/* Java 9+ path */
366-
return (boolean) (Boolean) CAN_ACCESS.invoke(executable, o);
367-
} else {
368-
/* Java 8 path */
369-
if (executable instanceof Method)
370-
LOOKUP.unreflect((Method) executable);
371-
else
372-
LOOKUP.unreflectConstructor((Constructor) executable);
373-
return true;
374-
}
375-
} catch (Exception e) {
376-
return false;
377-
}
378-
}
379331

380-
/**
381-
* Resolves a bridge method to the override one
382-
* @param bridgeMethod the potentially bridge method
383-
* @return a method that isn't a bridge method
384-
*/
385-
public static Method resolveBridge(Method bridgeMethod) {
386-
// accounts for bridge methods which may have slightly different parameters
387-
Method best = null;
388-
if (bridgeMethod.isBridge()) {
389-
Class<?>[] bridgeParams = bridgeMethod.getParameterTypes();
390-
// look for non-bridge methods in the same class that accepts wider parameters
391-
nextMethod:
392-
for (Method method: bridgeMethod.getDeclaringClass().getDeclaredMethods()) {
393-
if (!method.isBridge() && !method.isSynthetic() && method.getName().equals(bridgeMethod.getName())
394-
&& method.getParameterCount() == bridgeMethod.getParameterCount()) {
395-
Class<?>[] testParams = method.getParameterTypes();
396-
// Bridge params should be supertypes of target params (erasure widening).
397-
for (int i = 0; i < testParams.length; i++) {
398-
if (!testParams[i].isAssignableFrom(bridgeParams[i])) continue nextMethod;
399-
}
400-
if (bridgeMethod.getReturnType().isAssignableFrom(method.getReturnType()) && (best == null || isMoreSpecific(method, best))) best = method;
401-
}
402-
}
403-
}
404-
405-
return best == null ? bridgeMethod : best;
406-
}
407-
408-
/**
409-
* Finds a mathod that accepts the given instance and is accessible from RJavaTools class.
410-
* <p>Avoids calling isAccessible/setAccessible that fails on Java 17+
411-
*
412-
* @param method The most specific, possibly non-accessible, method
413-
* @param target the instance upon which the method is being invoked (null for static)
414-
* @return
415-
*/
416-
public static Method findAccessible(Method method, Object target) {
417-
if (canAccess(method, target)) return method;
418-
419-
Deque<Class<?>> supers = new ArrayDeque<Class<?>>();
420-
HashSet<Class<?>> visited = new HashSet<Class<?>>();
421-
supers.add(target.getClass());
422-
423-
while (!supers.isEmpty()) {
424-
Class<?> c = supers.pop();
425-
if (visited.add(c)) {
426-
Method superMethod;
427-
try {
428-
// first update the method to be non-bridge
429-
method = resolveBridge(method);
430-
superMethod = c.getMethod(method.getName(), method.getParameterTypes());
431-
/* if an accessible executable is found stop here */
432-
if (canAccess(superMethod, target)) return superMethod;
433-
if (!c.isInterface() && !c.isArray()) supers.add(c.getSuperclass());
434-
if (!c.isArray()) supers.addAll(Arrays.asList(c.getInterfaces()));
435-
} catch (NoSuchMethodException e1) {
436-
/* executable not found in current class (and any of its ancestors) */
437-
} catch (SecurityException e1) {
438-
/* executable not found in current class (and any of its ancestors) */
439-
}
440-
}
441-
}
442-
443-
/* No accessible executable found, fail later on invoke/newInstance */
444-
return method;
445-
}
446332

447333
/**
448334
* Object creator. Find the best constructor based on the parameter classes
@@ -457,12 +343,25 @@ public static Object newInstance( Class o_clazz, Object[] args, Class[] clazzes
457343

458344
Constructor cons = getConstructor( o_clazz, clazzes, is_null );
459345

346+
/* enforcing accessibility (workaround for bug 128) */
347+
boolean access = cons.isAccessible();
348+
if (!access) {
349+
try { /* since JDK-17 this may fail */
350+
cons.setAccessible( true );
351+
} catch (Throwable e) {
352+
access = true; /* nothing we can do, just let it fail below */
353+
}
354+
}
355+
460356
Object o;
461357
try{
462358
o = cons.newInstance( args ) ;
463359
} catch( InvocationTargetException e){
464360
/* the target exception is much more useful than the reflection wrapper */
465-
throw e.getCause() ;
361+
throw e.getTargetException() ;
362+
} finally{
363+
if (!access)
364+
cons.setAccessible( access );
466365
}
467366
return o;
468367
}
@@ -478,21 +377,32 @@ static boolean[] arg_is_null(Object[] args){
478377

479378
/**
480379
* Invoke a method of a given class
481-
* <p>First the appropriate method is resolved by getMethod.
482-
* <p>Then, if the method is not accessible, tries to find an accessible equivalent.
483-
* <p>Finally it invokes the method
380+
* <p>First the appropriate method is resolved by getMethod and
381+
* then invokes the method
484382
*/
485383
public static Object invokeMethod( Class o_clazz, Object o, String name, Object[] args, Class[] clazzes) throws Throwable {
486384

487385
Method m = getMethod( o_clazz, name, clazzes, arg_is_null(args) );
488-
m = findAccessible(m, o);
386+
387+
/* enforcing accessibility (workaround for bug 128) */
388+
boolean access = m.isAccessible();
389+
if (!access) {
390+
try { /* since JDK-17 this may fail */
391+
m.setAccessible( true );
392+
} catch (Throwable e) {
393+
access = true; /* nothing we can do, fail later with proper error ... */
394+
}
395+
}
489396

490397
Object out;
491398
try{
492399
out = m.invoke( o, args ) ;
493400
} catch( InvocationTargetException e){
494401
/* the target exception is much more useful than the reflection wrapper */
495-
throw e.getCause() ;
402+
throw e.getTargetException() ;
403+
} finally{
404+
if (!access)
405+
m.setAccessible( access );
496406
}
497407
return out ;
498408
}

0 commit comments

Comments
 (0)