beanshell2
beanshell2 copied to clipboard
BSH calling Java calls randomly an ambiguous method if var args are involved
What steps will reproduce the problem?
1. Modify VarargsTest as below, adding
calling_java_varargs_or_without_should_choose_the_simplest() and the two
overloaded methods in ClassWithVarargMethods
2. Run in Eclipse the tests from VarargsTest. My environment is eclipse 4.2
under XP 32bits and JDK 1.7.0_7.
What is the expected output? What do you see instead?
The new test should succeed calling the simplest method being a match for two
strings (returning 2). In my case it fails (returning 3). If I swap the two
overloaded methods it succeeds.
If it works for you initially, swap them, retry... Junit 4.10 and J7 also
introduces some randomness in the order the tests are executed.
Anyway. It can fail.
Yes the two methods are technically a valid match for
helper.twoChoices("one","two").
However, like in Java you would expect the best match to be called.
What I would expect here would be to have the BSF code getting a list of all
the matching methods and taking the closest match, not the first. It seems that
filtering by argument types works well.
If multiple methods are a match then take the one with the least number of
arguments.
The current behavior is random as the first match is the good one.
If you have more than one choice then I do not know.
However on my production system, in some cases the first method is called, in
some other the second without even restarting the VM !!!
The reason is that the reflection API does not guaranty at all in which order
the (matching/all) methods are returned for a class.
Picking the one with the smaller number of arguments may not be totally perfect
but this will bring a little bit of repeatability.
Thanks
public class VarargsTest {
...
@Test
public void calling_java_varargs_or_without_should_choose_the_simplest() throws
Exception {
final Interpreter interpreter = new Interpreter();
interpreter.set("helper", new ClassWithVarargMethods());
@SuppressWarnings({"unchecked"})
final int argPassedIn = (int) interpreter.eval("helper.twoChoices(\"one\",\"two\")");
Assert.assertEquals("Not picking the simplest choice",2,argPassedIn);
}
public static class ClassWithVarargMethods {
...
/** I DO NOT expect string/string/varargs to be called */
public int twoChoices(final String string1, final String string2, final Object ... args) {
return 3;
}
/** I expect string/string to be called */
public int twoChoices(final String string1, final String string2) {
return 2;
}
...
}
Original issue reported on code.google.com by [email protected] on 12 Nov 2012 at 9:43
Looking at it more closely in my case (2 static methods but the bug can be
reproduced with member methods) the stack is :
- bsh.Reflect.gatherMethodsRecursive(baseClass, methodName, types.length,
publicMethods, nonPublicMethods) returns the 2 methods "inputDialog" in a
random order (reflection makes no guaranty on the order)
- the 2 methods are passed in to bsh.Reflect.findMostSpecificMethod(Class[]
idealMatch, List<Method> methods)
- this one calls bsh.Reflect.findMostSpecificSignature(Class[] idealMatch,
Class[][] candidates) and this one sometimes makes the bad choice.
Regarding the unit test upper, you can add a giant loop around (e.g. 10000
loops) to increase the chance of failure and you should see that sometimes the
test is OK and sometimes fails.
Original comment by [email protected] on 14 Nov 2012 at 2:32