CNI for C

Tom Tromey tromey@redhat.com
Thu Apr 8 04:29:00 GMT 2004


>>>>> "Anthony" == Anthony Green <green@redhat.com> writes:

Anthony> Calling ComputePrime forces the creation of Linker, which loads the
Anthony> prim.dylib shared library and looks for the C function ComputePrime. 
Anthony> So, there are a couple of tricks here (demangling, marshalling
Anthony> arguments), but nothing too bad.  This combined with static imports
Anthony> would make a nice dynamic interface to C from Java.

By "static imports" do you mean the new 1.5 feature of that name?
Yeah, that would work nicely.

We could do a little better than the example by not requiring magic
static fields and eliminating a minor non-portable construct:

    import gnu.gdirect.GDirect;
    public class Prime {
      static {
        // The "prime" is transformed via System.mapLibraryName.
        GDirect.link (Prime.class, "prime");
      }

      public static final long ComputePrime (short n);
    }

I believe this could be implemented in a way that is completely
portable across VMs.  All you need to do is use reflection to
determine what methods need to be implemented in the class, generate
closures on the fly with libffi, then register them with the JNI
RegisterNatives call.

One refinement of this idea is that for gcj your marshalling code
could use CNI instead of JNI to convert complex types.  We could also
add a way to tell libgcj not to use the more expensive JNI calling
convention when calling the closures.

Another refinement is to omit the closures and marshalling completely
if the Java method and the C function happen to have identical types
-- just register the C function directly with the runtime (this only
works in the gcj-with-special-tweaks case, since JNI always adds extra
arguments).  This won't happen if, e.g., there are structures
involved, but it will happen in plenty of simple cases.  Perhaps the
dumb ones used in benchmarks :-)

Things get trickier if you want to wrap a C++ API, because then you
need to understand name mangling, which is hard and non-portable.
(You can always expose the mangled names, but that has other obvious
problems.)

One subtlety avoided above is that the lifetime of the closures must
be tied to the lifetime of the class.  This is easy enough, though;
just have a PhantomReference for the class, and clean up when it is
collected.

There are also some questions about type conversions and
representation.  What does a pointer look like in Java?  (Try to
follow this classpath discussion on this :-) How about auto-converting
between char* and String?  Those are more or less easily solved
though, or perhaps tweakable via options to GDirect.link().

Tom



More information about the Java mailing list