0

So first some background:

I've been working on wrapping a C++ library for Java with the JNI, specifically the Squirrel scripting language. The problem comes when I need to pass a native function to the Squirrel Virtual Machine. Squirrel requires a function to be an SQFUNCTION defined as a function that has an HSQUIRRELVM as a parameter and returns an SQInteger, but keep in mind I'm wrapping this for Java. I can make C++ call a Java method from a jobject just fine, but I needed to wrap that call in a lambda function in order to actually pass it to Squirrel. Normally I'd put [=] as the lambda capture so it can reference my variables but, for some reason I'm entirely unsure of, capturing variables changes the type of the lambda function and it no longer is recognized as an SQFUNCTION. The most recent way I've decided to fix this with is either a constant vector or array so that the lambda can access that. I tell Squirrel where in the vector/array the object is stored and let the lambda get that value from Squirrel to access it. What's become the problem is this: There's an object in the correct slot, but it's not the object I put there.

The issue becomes that I'm not super experienced with C++ or the JNI and nothing I've searched for has told me what kind of issue this is. I've tried storing the object and pointers to the object, but both methods yeild the same result. I'm storing an instance of JSqTestFunc but the code is retrieving an instance of JSqVM. The only thing these two classes have in common other than interacting with Squirrel is that they extend Object, otherwise they're completely unrelated.

I guess my question should be in multiple parts:

  1. Is this a C++ problem or a JNI problem?
  2. How can I fix this?

I feel like it has to be a JNI problem but I can't rule out C++ being stupid to me either. I'm not familiar with how the JNI handles the jobject class and references to it, so maybe the jobject ends up storing a different class' data internally. I haven't found anything related to this or any problems in C++ array/vector storage either.

The C++ function looks like this:

static const int m_maxClosures = 8;
static int m_closures = 0;

static JNIEnv *m_envs[m_maxClosures];
static jobject m_objs[m_maxClosures];

JNIEXPORT void JNICALL Java_com_yourlocalfax_jsquirrel_Squirrel_sq_1newclosure_1native(JNIEnv *env, jclass c, jlong vmhandle, jobject func, jlong nfreevars) {
    HSQUIRRELVM v = fromPointerHandleToObject<HSQUIRRELVM>(vmhandle);

    int idx = m_closures;

    printf("Creating number %d closure of %d", idx, m_maxClosures);

    m_closures++;

    m_envs[idx] = env;
    m_objs[idx] = func;

    sq_pushinteger(v, idx);

    JNIEnv *e = m_envs[idx];
    jobject o = m_objs[idx];

    jobject clsObj = e->CallObjectMethod(o, e->GetMethodID(e->GetObjectClass(o), "getClass", "()Ljava/lang/Class;"));
    jstring strObj = (jstring)e->CallObjectMethod(clsObj, e->GetMethodID(e->GetObjectClass(clsObj), "getName", "()Ljava/lang/String;"));

    const char* str = e->GetStringUTFChars(strObj, NULL);
    printf("\nInitial calling class is: %s\n", str);
    e->ReleaseStringUTFChars(strObj, str);

    SQFUNCTION f = [](HSQUIRRELVM v) {
        print_args(v);
        squirrel_stack_trace(v);

        SQInteger i;

        sq_pushinteger(v, 0); // Push the index in the table TO GET
        sq_get(v, 1); // Push the index of the actual table
        sq_getinteger(v, -1, &i); // Get the newly pushed value (integer)
        //sq_getinteger(v, 2, &i);
        printf("Location Id is %d of %d", i, m_maxClosures);

        JNIEnv *e = m_envs[i];
        jobject o = m_objs[i];

        jobject clsObj = e->CallObjectMethod(o, e->GetMethodID(e->GetObjectClass(o), "getClass", "()Ljava/lang/Class;"));
        jstring strObj = (jstring)e->CallObjectMethod(clsObj, e->GetMethodID(e->GetObjectClass(clsObj), "getName", "()Ljava/lang/String;"));

        const char* str = e->GetStringUTFChars(strObj, NULL);
        printf("\nCalling class is: %s\n", str);
        e->ReleaseStringUTFChars(strObj, str);

        jclass cls = e->GetObjectClass(o);
        jmethodID m = e->GetMethodID(cls, "function", "(Lcom/yourlocalfax/jsquirrel/JSqVM;)I");
        //sq_pushinteger(v, e->CallIntMethod(o, m));
        return (SQInteger)0;
    };

    sq_newclosure(v, f, nfreevars + 1);
}

I should mention that the fromPointerHandleToObject hasn't failed me yet, I use it in every other function call and it works every time. If you'd still like to see the code for that, too, I can post it.

And the output is this:

Creating number 0 closure of 8
Initial calling class is: com.yourlocalfax.jsquirrel.test.JSqTestFunc
Location Id is 0 of 8
Calling class is: com.yourlocalfax.jsquirrel.JSqVM

As you can see, the index 0 of the jobject array stores a JSqTestFunc initially but a JSqVM when retrieved.

Any help at all, even a different method of doing this, is much appreciated as I've spend far too long and way too much effort trying to fix this. Thanks!

Abhishek Aryan
  • 19,936
  • 8
  • 46
  • 65

1 Answers1

4

I went ahead and researched more heavily after posting this and realized it was indeed a problem on the JNI side relating to local and global references. All I had to do was env->NewGlobalRef(object); and then store the object in the array. That solved it.

I'm going to leave this question and answer up in case it helps anyone in the future.