Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4491564

Classes not initialized by jni static field/method accessors

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P4 P4
    • 1.4.1
    • 1.4.0
    • hotspot
    • hopper
    • generic
    • solaris_2.6

      The description from Apple follows. The provided test case in fact shows that jni static field/method accessors do not
      cause the appropriate interface/class initialized. JLS 12.4.1 says that "A class or interface type T will be initialized immediately
      before the first occurrence of the following:
      ...
      T is a class and a static method declared by T is invoked.
      ....
      A static field declared by T is used and the reference to the field is not a compile-time constant"

      In both of these cases the initialization is not occuring prior to static field/method being used through the jni. Please see
      below. Reproducible on JDK 1.3.1/ JDK 1.4 / JDK 1.2.2

      ###@###.### 2001-08-13
      ==================================

      The problem is a violation of JLS 12.4.1. To reproduce use the code listed below (to get a tar of these files contact ###@###.###). Compile the java files, and compile TesterImp.c to a jnilib named libtester.<so, jnilib, or whatever it is on your platform>

      Run "java Tester" and it will explain what should happen and show that
      it is not. We have a source patch in hand for hotspot.


      5 files:

      #### Tester.java

      import java.lang.reflect.*;


      public class Tester implements libsubports {

          public static native String getVersionNativeReflect(Class the_if, Field jfid);
          public static native void callStaticMethod(Class the_clazz, Method the_method);

          public static void main(String[] args) {
      System.out.println("************************************************************************");
      System.out.println("This program will demonstrate that the Hotspot JVM fails to conform to\n" +
      "JLS (2nd ed) 12.4.1, regarding class initialization, in certain cases");
      System.out.println("************************************************************************\n");

      Tester t = new Tester();
      System.out.println("\n1) Testing a static field access.");
      t.testInterfaceField();
      System.out.println("\n2) Testing a static method call.");
      t.testStaticMethodCall();
          }


          private void testStaticMethodCall() {
      // get the class
      Class the_clazz = null;

      try {
      the_clazz = Class.forName("SomeClass", false, this.getClass().getClassLoader());
      }
      catch (ClassNotFoundException e) {
      System.out.println("SomeClass not found");
      System.exit(-1);
      }

      // get the method
      Method the_method = the_clazz.getDeclaredMethods()[0];

      System.out.println("A correct implementation will have the class initialize before executing the method:");
      callStaticMethod(the_clazz, the_method);
          }



          private void testInterfaceField() {
      Class the_if = null;
      Field jfid = null;

      // get the interface for reflection
      try {
      the_if = (Class)((Tester.class).getInterfaces()[0]);
      jfid = the_if.getField("JDirect_MacOSX");
      }
      catch (Exception e) {
      System.out.println( e.toString() );
      System.exit(1);
      }

      System.out.println("If the interface has been initialized properly, the following two will be identical. The second is correct.");
      System.out.println("Native, reflect: " + getVersionNativeReflect(the_if, jfid));
      System.out.println("Java, no reflect: " + JDirect_MacOSX);
          }



          static {
      System.loadLibrary("tester");
          }
      }


      ##### Tester.h

      /* DO NOT EDIT THIS FILE - it is machine generated */
      #include <jni.h>
      /* Header for class Tester */

      #ifndef _Included_Tester
      #define _Included_Tester
      #ifdef __cplusplus
      extern "C" {
      #endif
      /* Inaccessible static: class_00024LTester */
      /*
       * Class: Tester
       * Method: getVersionNativeReflect
       * Signature: (Ljava/lang/Class;Ljava/lang/reflect/Field;)Ljava/lang/String;
       */
      JNIEXPORT jstring JNICALL Java_Tester_getVersionNativeReflect
        (JNIEnv *, jclass, jclass, jobject);

      /*
       * Class: Tester
       * Method: callStaticMethod
       * Signature: (Ljava/lang/Class;Ljava/lang/reflect/Method;)V
       */
      JNIEXPORT void JNICALL Java_Tester_callStaticMethod
        (JNIEnv *, jclass, jclass, jobject);

      #ifdef __cplusplus
      }
      #endif
      #endif



      #### TesterImp.c

      #include <jni.h>
      #include "Tester.h"
      #include <stdio.h>
      #include <assert.h>


      /*
       * Class: Tester
       * Method: getVersionNativeReflect
       * Signature: (Ljava/lang/Class;Ljava/lang/reflect/Field;)Ljava/lang/String;
       */
      JNIEXPORT jstring JNICALL Java_Tester_getVersionNativeReflect
      (JNIEnv *env, jclass myclass, jclass the_if, jobject jfid)
      {
        jclass libsubports;
        jfieldID field_id;
        jobject toReturn;

        libsubports = the_if;
        assert(libsubports);
        
        field_id = (*env)->FromReflectedField(env, jfid);
        assert(field_id);

        toReturn = (*env)->GetStaticObjectField(env, libsubports, field_id);

        return (jstring)toReturn;
      }


      /*
       * Class: Tester
       * Method: callStaticMethod
       * Signature: (Ljava/lang/Class;Ljava/lang/reflect/Method;)V
       */
      JNIEXPORT void JNICALL Java_Tester_callStaticMethod
      (JNIEnv *env, jclass myclass, jclass target_class, jobject reflect_method)
      {
        jmethodID method_id;
          
        // get method id. note that calling GetStaticMethodID would initialize the
        // class.
        method_id = (*env)->FromReflectedMethod(env, reflect_method);
        assert(method_id); // null indicates failed
        
        (*env)->CallStaticVoidMethod(env, target_class, method_id);
        
      }

      #### SomeClass.java

      public class SomeClass {

          private static int field = 3;

          public static void doAction() {
      System.err.println("A static method of class SomeClass has been invoked");
      field = 2; // will cause initialization if hasn't yet occurred
          }

          static {
      System.err.println("The class SomeClass has been initialized");
          }
      }



      ##### libsubports.java

      public interface libsubports {
          public static final String JDirect_MacOSX = "xxx" + System.getProperty("java.version") + "yyy";
      }


            bobv Bob Vandette (Inactive)
            ksoshals Kirill Soshalskiy (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: