Skip to content

Instantly share code, notes, and snippets.

@Xyene
Last active August 23, 2016 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Xyene/1454c8794948a7b8513d69235e01b4c9 to your computer and use it in GitHub Desktop.
Save Xyene/1454c8794948a7b8513d69235e01b4c9 to your computer and use it in GitHub Desktop.

init.yml specifies signatures for all methods, e.g.:

    int findSwapPairs(int, int[], int, int[])
    functions:
        findSwapPairs: (I[II[I[I[I[I)I

There are two types of functions: functions that the grader calls from C->Java, and functions that Java calls that are implemented in C. The latter is slightly easier to discuss.

We can autogenrate Java classes binding those methods statically, e.g.:

public class foo {
    static native void findSwapPairs(int _0, int[] _1, int _2, int[] _3, int[] _4, int[] _5, int[] _6);
}

We can then generate JNI headers as javah would:

#include <jni.h>

#ifndef _Included_foo
#define _Included_foo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test
 * Method:    findSwapPairs
 * Signature: (I[II[I[I[I[I)I
 */
JNIEXPORT jint JNICALL Java_foo_findSwapPairs
  (JNIEnv *, jclass, jint, jintArray, jint, jintArray, jintArray, jintArray, jintArray);

#ifdef __cplusplus
}
#endif
#endif

And then write bridge calls between an implementation of Java_foo_findSwapPairs to the grader. These shouldn't be difficult if we limit ourselves to primitive types.

Pseudo-C example:

Java_foo_findSwapPairs implementation
int* c_1 = i_arr_from_java(_1);
int ret = findSwapPairs(..., c_1, ...) // This is the grader function call
c_arr_to_java(c_1, _1)
return ret;

This handles functions the grader implements. Functions the grader calls from Java are more tricky, since they need access to the jclass instance. Apart from that, another set of bridging functions must be implemented.

How do we get this into the scope of a Java submission, though? We could request them to import static grader.* for the grader functions, and have them extend/implement grader to implement the calls. Upon further reflection, a better solution becomes apparent: use a different entry point. Have a small bootloader class, something among the lines of:

import {user_classname};

public class BootstrapSiggrader {
    // Auto-generated code
    static native void findSwapPairs(int _0, int[] _1, int _2, int[] _3, int[] _4, int[] _5, int[] _6);
    
    static native void __setJavaClass(Class clazz);
    
    static {
        System.loadLibrary("{generated_grader_binary_path}");
    }
    
    public static void main(String[] argv) {
        __setJavaClass({user_classname}.class);
        {user_classname}.main(argv);
    }
}

This handles the binding of the Java calls to native code (via __setJavaClass, the native grader code now has a jclass instance which it can use to invoke functions on).

All that's left is getting the grader-implemented functions into the scope of the Java submission. One solution is to simply prepend an import static BootstrapSiggrader.*;, but that messes up line numbers. We also have one more issue - because the user implements no kind of interface, if they're missing a function we'll know very late in the game.

The last issue, at least, we can implement with an ASM post-process: simply iterate over the methods of the class to ensure the necessary methods are implemented.

In case it isn't obvious by now, this whole thing sounds like a great bundle of not-fun.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment