javajnr

How to return by value from native function?


I have the following C++ method compiled using Visual Studio 2017:

extern "C" __declspec( dllexport )
Info* __stdcall GetInfo(InfoProvider* infoProvider)
{
   static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");

   Info info = new Info();
   Info->data1 = infoProvider->data1;
   Info->data2 = infoProvider->data2;

   return info;
}

In Java code, it is mapped by Java Native Runtime using interface method with following signature:

Info GetInfo(Pointer infoProvider);

final class Info extends Struct {

    public final Signed32 data1;

    public final Signed32 data2;

    public R2VInfo(final Runtime runtime) {
        super(runtime);

        data1 = new Signed32();
        data2 = new Signed32();
    }
}

It works.

The above C++ method causes memory leak, so I would like to change it to return result by value:

extern "C" __declspec( dllexport )
Info __stdcall GetInfo(InfoProvider* infoProvider)
{
   static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");

   Info info{};
   Info.data1 = infoProvider->data1;
   Info.data2 = infoProvider->data2;

   return info;
}

I use the same Java JNR mapping:

Info GetInfo(Pointer infoProvider);

But it does not work - Access Violation. Native method is invoked, but with some dandling pointer value.

How to return by value in JNR?


Solution

  • JNI build around old pure K&R C to be compatible with all available compilers. Returning a structure from a function was introduced in C89 and was fully implemented significantly later with C++ standard together. Today it is still possible to find such old C compiler in many java-friendly environments like small devices or sim-cards. So I don't think that JNI would be upgraded to C89 or even C99.

    For your case, I would recommend to write an additional C-code that handles calling of a library function. The code could be implemented in two ways:

    1. For the Info* __stdcall GetInfo(InfoProvider* infoProvider) you should write free-function like:
    extern "C" __declspec( dllexport )
    void __stdcall FreeInfo(Info* info)
    {
       static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");
    
       delete info;
    }
    
    1. For the Info __stdcall GetInfo(InfoProvider* infoProvider) you should write a wrapper:
    extern "C" __declspec( dllexport )
    void __stdcall GetInfo(InfoProvider* infoProvider, Info* info)
    {
       static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");
    
       Info infoProvider = GetInfo(infoProvider);
       info->data1 = infoProvider.data1;
       info->data2 = infoProvider.data2;
    }