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

--
[EMAIL PROTECTED]
( convergence      )
( integrated media )

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?


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 );
    }
}


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 );
    }
}

Reply via email to