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!

[1458 byte] By [mike_dalpeea] at [2007-10-3 1:45:29]
# 1
Oops, forgot to mention, this is all being done under Solaris 9. And the exception being thrown is UnsatisfiedLinkError.Message was edited by: mike_dalpeeMessage was edited by: mike_dalpee
mike_dalpeea at 2007-7-14 18:43:41 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 2
1- What is the build command(s) you are using.gcc ... ?2- Can you post a more complete printout of the error you have (the first lines of the stack trace).UnsatisfiedLinkError of what?
jfbrierea at 2007-7-14 18:43:41 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 3

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

mike_dalpeea at 2007-7-14 18:43:41 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 4

Yep, it was a bug - in the JDK 1.6 beta, javah generates a slightly different signature for an inner class. For my example it was:

JNICALL Java_jniinnerclassbug_Outer_00024Inner_NewNativeInner

Everything gets loaded now.Thanks to all who spent any time spent wading through my post.

mike_dalpeea at 2007-7-14 18:43:41 > top of Java-index,Java HotSpot Virtual Machine,Specifications...