VM cannot resolve inner class native functions?
Hello,
I am trying to wrap some legacy C++ libraries using JNI. I thought I would try to mimic the C++ class structure in java to keep things simple. However, I have discovered what appears to be a bug in how inner class native functions are resolved.
if I have something like:
package pkg;
public class Outer
{
public static class Inner
{
protected int m_nativeP;
public
Inner()
{
m_nativeP=
NewNative();
}
public static native int
NewNative();
}
Whenever I try to create an instance of Inner, the Java VM throws an exception indicating NewNative could not be found, even though I used javah to generate the signature for the implemenation (generated C function name looks something like Java_pkg_Outer_Inner_NewNative.) I did a nm on the shared library, and the native function is definitely in there.
If I move Inner to global scope everything works fine, but now the name scoping is different than it is in the C++ API I am wrapping.
I tried various combinations of making the inner class non-static/static and the native function non-static/static but it did not matter - as long as the class was nested, the name could not be resolved.
So, if anyone knows what the magic is to get this to work, or can confirm it is a bug, please advise. I am using the latest JDK 1.5-07 in conjunction with jbuilder 2006.
Thanks!
Ok, here is a boiled down example I made to demostrate the bug.
First, the Java (1.5.0_03-b07)
Outer.java
package jniinnerclassbug;
public class Outer
{
protected int m_nativeP;
public Outer()
{
m_nativeP =
NewNativeOuter();
}
private native static int
NewNativeOuter();
public static class Inner
{
protected int m_nativeP;
public Inner()
{
m_nativeP =
NewNativeInner();
}
private native static int
NewNativeInner();
}
}
BugTest.java
package jniinnerclassbug;
public class BugTest
{
public static void FlushMessage(String message)
{
System.out.println(message);
System.out.flush();
}
static
{
FlushMessage("Loading BugTest...");
System.loadLibrary("BugTest");
FlushMessage("Done");
}
public static void main(String[] args)
{
FlushMessage("Creating Outer...");
Outer outer = new Outer();
FlushMessage("Done");
FlushMessage("Creating Outer.Inner");
Outer.Inner inner = new Outer.Inner();
FlushMessage("Done");
}
}
Now, the native code (signatures generated using javah):
BugTest.C
#include "jni.h"
#include <iostream>
using namespace std;
/*
* Class:jniinnerclassbug_Outer_Inner
* Method:NewNativeInner
* Signature: ()I
*/
extern "C" JNIEXPORT jint JNICALL Java_jniinnerclassbug_Outer_Inner_NewNativeInner
(JNIEnv *, jclass)
{
cout << "Found NewNativeInner" << endl << flush;
return 0;
}
/*
* Class:jniinnerclassbug_Outer
* Method:NewNativeOuter
* Signature: ()I
*/
extern "C" JNIEXPORT jint JNICALL Java_jniinnerclassbug_Outer_NewNativeOuter
(JNIEnv *, jclass)
{
cout << "Found NewNativeOuter" << endl << flush;
return 0;
}
How I built the shared library (using Studio 11 C++/Solaris 9):
CC -G -I${JAVA_HOME}/include -I${JAVA_HOME}/include/solaris BugTest.C -lCstd -o libBugTest.so
Output of the java program BugTest when run:
/apps/Borland/JBuilder2006/jdk1.5/bin/java -classpath "/home/simeng/jbproject/JniInnerClassBug/classes:/apps/Borland/JBuilder2006/jdk1.5/lib/tools.jar:/apps/Borland/JBuilder2006/jdk1.5/lib/htmlconverter.jar:/apps/Borland/JBuilder2006/jdk1.5/lib/dt.jar:/apps/Borland/JBuilder2006/jdk1.5/lib/sa-jdi.jar:/apps/Borland/JBuilder2006/jdk1.5/lib/jconsole.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/ext/sunjce_provider.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/ext/sunpkcs11.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/ext/localedata.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/ext/dnsns.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/charsets.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/plugin.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/im/thaiim.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/im/indicim.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/jce.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/deploy.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/rt.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/jsse.jar:/apps/Borland/JBuilder2006/jdk1.5/jre/lib/javaws.jar" -D"java.library.path=/home/simeng/jbproject/JniInnerClassBug" jniinnerclassbug.BugTest
Loading BugTest...
Done
Creating Outer...
Found NewNativeOuter
Done
Creating Outer.Inner
Exception in thread "main" java.lang.UnsatisfiedLinkError: NewNativeInner
at jniinnerclassbug.Outer$Inner.NewNativeInner(Native Method)
at jniinnerclassbug.Outer$Inner.<init>(Outer.java:22)
at jniinnerclassbug.BugTest.main(BugTest.java:30)
Seems pretty clear to me this is a bug. Perhaps someone can tell me how to figure out the exact signature the VM is looking for when it generates the UnsatisfiedLinkError.
Thanks,
Mike