Bug in Method.invoke() / Patch
enno at convergence.de
enno at convergence.de
Tue Jul 24 09:52:09 PDT 2001
Hi,
when executing the following small java-program, I noticed some
strange behaviour:
interface A {
void foo();
}
class B implements A {
public void foo() {
System.out.println("foo");
}
public static void main(String[] args) {
Object o = new B();
// now calling o.foo() using reflection
// this works fine, calls B.foo()
B.class.getMethod("foo", null).invoke( o, null );
// this will execute o.clone() instead!!!!
A.class.getMethod("foo", null).invoke( o, null );
}
}
According to the Java-Spezification, both reflective calls should
resolve to B.foo(). But the output is
foo
java.lang.reflect.InvocationTargetException: java.lang.CloneNotSupportedException: B
at java.lang.Object.clone(Object.java:native)
at java.lang.reflect.Method.invoke(Method.java:native)
at B.main(B.java:22)
As this didn't make any sense, I looked into the code of
libraries/clib/native/Method.c and found the following explanation:
when the Method-Object is constructed (via getMethod(...)), it saves
the class it's constructed from and it's "method slot"-number. So in
the second case, we have a Method that believes to be "Method #0 in
class A", while the first Method object is "Method #6 in B".
Unfortunately, when Method.invoke() is called, the following happens
(in a nutshell): the runtime class of the object-argument of
Method.invoke() and the slot number (index) in the Method are combined
to find the code to be executed: so in the first case, we call method
#6 in class B, which *really is* foo(). But in the second case we
execute method #0 (!!!!) in class B, which happens to be
object.clone(), leading to the shown exception.
Attached is a patch, which to my opinion should fix this (using
getInheritedMethodIndex() in classMethod.c), but I would appreciate
if somebody more familiar with this code could check my explanation/fix.
One more bug in the reflection code:
Using the interface A from above again, the following will erroneously *not* flag
an error:
Object cloneable = new Cloneable() {};
A.class.getMethod("foo", null).invoke( cloneable, null);
while Suns VM will throw
java.lang.IllegalArgumentException: object is not an instance of
declaring class
at D.main(D.java:12)
which is correct. The patch mentioned above will - as a side-effect -
also fix this. Anybody any comments on this?
I am working with the kaffe-version out of the CVS on Linux/i386.
Best regards and thanks for any comment ...
Enno Brehm
--
enno at convergence.de
( convergence )
( integrated media )
-------------- next part --------------
Index: Method.c
===================================================================
RCS file: /cvs/kaffe/kaffe/libraries/clib/native/Method.c,v
retrieving revision 1.30
diff -u -r1.30 Method.c
--- Method.c 2000/12/03 03:25:09 1.30
+++ Method.c 1999/12/20 22:42:06
@@ -281,6 +281,10 @@
rettype = 'L';
}
else { /* nonstatic method */
+ Hjava_lang_Class* objClazz = (*env)->GetObjectClass(env, obj);
+ if( !getInheritedMethodIndex( objClazz, meth )) {
+ SignalError("java.lang.IllegalArgumentException", "object is not an instance of declaring class");
+ }
switch (rettype) {
/* Why Call<Type>MethodA and not CallNonvirtual<Type>MethodA?
-------------- next part --------------
interface A {
void foo();
}
class B implements A {
public void foo() {
System.out.println("foo");
}
public static void main(String[] args)
throws Exception
{
Object o = new B();
// this works fine:
B.class.getMethod("foo", null).invoke( o, null );
// call foo() on o via reflection
// (will execute o.clone() instead!!!!)
A.class.getMethod("foo", null).invoke( o, null );
}
}
-------------- next part --------------
interface A {
void foo();
}
class D {
public static void main(String[] args)
throws Exception
{
Object o = new Cloneable() {};
A.class.getMethod("foo", null).invoke( o, null );
}
}
More information about the kaffe
mailing list