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