However, while finding a best way to call native libraries with in the programmable framework of MeTA Studio (what?? ... yes, but for reasons behind this you will have to wait for subsequent posts, when I get some stuff working) I stumbled upon a JDK incubation project called JNA. JNA, in fact, provides a very clean way to call native libraries without going through the pain of generating JNI stubs.
In fact, its pretty cool. For instance, you can call a C function
sqrt
like this:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public interface CLibrary extends Library {
double sqrt(double v);
}
// and use it as:
CLibrary clib = (CLibrary) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "m"),
CLibrary.class);
System.out.println(clib.sqrt(2));
Look ma, no JNI, pure Java!
Now, the above code worked fine if I compile using standard Java complier. But failed to run at all via the BeanShell interpreter with the following error:
java.lang.ClassFormatError: Illegal class modifiers in class CLibrary: 0x201
At first glance, I had absolutely no idea of what this meant. Long time ago, when I was in my graduation, I had fiddled around a bit with JVM specification in the hope to write a JVM for a 16-bit DOS environment. Needless to say, it never materialised ;-) But this some how made me check back the JVM class format again to look for possible solution. I also checked the BeanShell code that was generating the bytecodes, and essentially tracked it down to the following lines in
bsh.ClassGeneratorUtil
public byte [] generateClass() {
int classMods = getASMModifiers( classModifiers ) | ACC_PUBLIC;
if ( isInterface )
classMods |= ACC_INTERFACE;
...
This too made not much sense, until I read the following in JVM class format specification:
"If the ACC_INTERFACE flag of this class file is set, its ACC_ABSTRACT flag must also be set (§2.13.1) and its ACC_PUBLIC flag may be set. Such a class file may not have any of the other flags in Table 4.1 set."
So the solution was simply to modify
classMods |= ACC_INTERFACE | ACC_ABSTRACT;
which worked great, except that I got another error:
java.lang.ClassFormatError: Interfaces must have java.lang.Object as superclass in class CLibrary
It appeared that BeanShell somehow did not do this correctly, and possibly the ASM interface would also have changed. I was not sure of the reason behind this, but I knew how to fix this :
if ( isInterface ) {
classMods |= ACC_INTERFACE | ACC_ABSTRACT;
try {
interfaces = new Class[]{Class.forName(superClassName.replace('/', '.'))};
} catch(Exception e) {
e.printStackTrace();
throw new UnsupportedOperationException("Super class " +
superClassName + " is not varifiable for interface : " + fqClassName);
}
superClassName = "java/lang/Object";
}
....
The above seems to do the trick, though am not very sure if this is the correct way to do it. And well in the end, I enjoyed doing this :-) .. and now calling native methods from with in MeTA Studio scripting environment is a lot easier and intuitive, well should I say its pretty cool ;-)
Note that all the source changes for the above are available from MeTA Studio SVN repository: http://code.google.com/p/metastudio/source/checkout
The above changes are not yet reflected in the binary package. But I hope to provide a full binary update too in coming days.
Have fun ;-)
No comments:
Post a Comment