This is an automated email from the git hooks/post-receive script. tpot-guest pushed a commit to annotated tag upstream/1.0.10 in repository jffi-next.
commit a61b1fc42aa70add2ded067a36befdeabb98fd5e Author: Wayne Meissner <[email protected]> Date: Tue Apr 26 19:31:35 2011 +1000 Add native support for boolean, char arrays, handle pinned arrays properly, and add more exception checking where needed. --- jni/jffi/Array.c | 186 +++++++++++++++++++++++++++++++++++++++++------------- jni/jffi/Array.h | 22 +++++++ jni/jffi/Invoke.c | 77 +++++++++++++++++----- 3 files changed, 226 insertions(+), 59 deletions(-) diff --git a/jni/jffi/Array.c b/jni/jffi/Array.c index 7151b87..6d572ca 100644 --- a/jni/jffi/Array.c +++ b/jni/jffi/Array.c @@ -42,54 +42,42 @@ #include "Array.h" -#define ARRAY_NULTERMINATE com_kenai_jffi_ObjectBuffer_ZERO_TERMINATE -#define ARRAY_IN com_kenai_jffi_ObjectBuffer_IN -#define ARRAY_OUT com_kenai_jffi_ObjectBuffer_OUT -#define ARRAY_PINNED com_kenai_jffi_ObjectBuffer_PINNED -#define ARRAY_CLEAR com_kenai_jffi_ObjectBuffer_CLEAR #define ARGPRIM_MASK com_kenai_jffi_ObjectBuffer_PRIM_MASK #define ARGTYPE_MASK com_kenai_jffi_ObjectBuffer_TYPE_MASK #define ARGTYPE_SHIFT com_kenai_jffi_ObjectBuffer_TYPE_SHIFT #define ARGFLAGS_MASK com_kenai_jffi_ObjectBuffer_FLAGS_MASK -#define RELEASE(JTYPE, NTYPE) \ -static void release##JTYPE##ArrayHeap(JNIEnv *env, Array *array) \ -{ \ - (*env)->Set##JTYPE##ArrayRegion(env, array->array, array->offset, array->length, \ - (NTYPE *) array->elems); \ - free(array->elems); \ -} \ -static void free##JTYPE##Array(JNIEnv *env, Array *array) \ -{ \ - free(array->elems); \ -} \ -static void release##JTYPE##ArrayBuffer(JNIEnv *env, Array *array) \ -{ \ - (*env)->Set##JTYPE##ArrayRegion(env, array->array, array->offset, array->length, \ - (NTYPE *) array->elems); \ +static void +releaseHeapArray(JNIEnv* env, Array* array) +{ + free(array->elems); } -RELEASE(Byte, jbyte); -RELEASE(Short, jshort); -RELEASE(Int, jint); -RELEASE(Long, jlong); -RELEASE(Float, jfloat); -RELEASE(Double, jdouble); - #define isOut(flags) (((flags) & (ARRAY_IN | ARRAY_OUT)) != ARRAY_IN) #define isIn(flags) (((flags) & (ARRAY_IN | ARRAY_OUT)) != ARRAY_OUT) #define COPY_DATA(JTYPE, NTYPE, flags, obj, offset, length, array) do { \ if (isIn(flags)) { \ (*env)->Get##JTYPE##ArrayRegion(env, obj, offset, length, (NTYPE *) array->elems); \ + if (unlikely((*env)->ExceptionCheck(env) != JNI_FALSE)) return NULL; \ } else if (unlikely((flags & ARRAY_CLEAR) != 0)) { \ memset(array->elems, 0, length * sizeof(NTYPE)); \ } \ } while (0) +#define SET_COPYOUT(JTYPE, array, flags) \ + (array)->copyout = isOut(flags) \ + ? (void (*)(JNIEnv*, jobject, jsize, jsize, const void *))(*env)->Set##JTYPE##ArrayRegion \ + : NULL + +#define SET_COPYIN(JTYPE, array, flags) \ + (array)->copyin = isIn(flags) \ + ? (void (*)(JNIEnv*, jobject, jsize, jsize, void *))(*env)->Get##JTYPE##ArrayRegion \ + : NULL + #define GET_ARRAY_BUFFER(JTYPE, NTYPE, flags, obj, offset, length, array) do { \ COPY_DATA(JTYPE, NTYPE, flags, obj, offset, length, array); \ - (array)->release = isOut(flags) ? release##JTYPE##ArrayBuffer : NULL; \ + SET_COPYOUT(JTYPE, array, flags); \ } while (0) #define GET_ARRAY_HEAP(JTYPE, NTYPE, flags, obj, offset, length, array) do { \ @@ -100,50 +88,67 @@ RELEASE(Double, jdouble); return NULL; \ } \ COPY_DATA(JTYPE, NTYPE, flags, obj, offset, length, array); \ - (array)->release = isOut(flags) ? release##JTYPE##ArrayHeap : free##JTYPE##Array; \ + SET_COPYOUT(JTYPE, array, flags); \ } while(0) void* -jffi_getArrayHeap(JNIEnv* env, jobject buf, jsize offset, jsize length, int paramType, +jffi_getArrayHeap(JNIEnv* env, jobject buf, jsize offset, jsize length, int type, Array* array) { array->array = buf; array->offset = offset; array->length = length; + array->type = type; + array->copyin = NULL; + array->copyout = NULL; + array->release = releaseHeapArray; /* * Byte arrays are used for struct backing in both jaffl and jruby ffi, so * are the most likely path. */ - if (likely((paramType & ARGPRIM_MASK) == com_kenai_jffi_ObjectBuffer_BYTE)) { - GET_ARRAY_HEAP(Byte, jbyte, paramType, buf, offset, length, array); + if (likely((type & ARGPRIM_MASK) == com_kenai_jffi_ObjectBuffer_BYTE)) { + GET_ARRAY_HEAP(Byte, jbyte, type, buf, offset, length, array); // If the array was really a string, nul terminate it - if ((paramType & (ARRAY_NULTERMINATE | ARRAY_IN | ARRAY_OUT)) != ARRAY_OUT) { + if ((type & (ARRAY_NULTERMINATE | ARRAY_IN | ARRAY_OUT)) != ARRAY_OUT) { *(((char *) array->elems) + length) = '\0'; } } else { - switch (paramType & ARGPRIM_MASK) { + switch (type & ARGPRIM_MASK) { case com_kenai_jffi_ObjectBuffer_SHORT: - GET_ARRAY_HEAP(Short, jshort, paramType, buf, offset, length, array); + GET_ARRAY_HEAP(Short, jshort, type, buf, offset, length, array); break; + case com_kenai_jffi_ObjectBuffer_INT: - GET_ARRAY_HEAP(Int, jint, paramType, buf, offset, length, array); + GET_ARRAY_HEAP(Int, jint, type, buf, offset, length, array); break; + case com_kenai_jffi_ObjectBuffer_LONG: - GET_ARRAY_HEAP(Long, jlong, paramType, buf, offset, length, array); + GET_ARRAY_HEAP(Long, jlong, type, buf, offset, length, array); break; + case com_kenai_jffi_ObjectBuffer_FLOAT: - GET_ARRAY_HEAP(Float, jfloat, paramType, buf, offset, length, array); + GET_ARRAY_HEAP(Float, jfloat, type, buf, offset, length, array); break; + case com_kenai_jffi_ObjectBuffer_DOUBLE: - GET_ARRAY_HEAP(Double, jdouble, paramType, buf, offset, length, array); + GET_ARRAY_HEAP(Double, jdouble, type, buf, offset, length, array); + break; + + case com_kenai_jffi_ObjectBuffer_BOOLEAN: + GET_ARRAY_HEAP(Boolean, jboolean, type, buf, offset, length, array); + break; + + case com_kenai_jffi_ObjectBuffer_CHAR: + GET_ARRAY_HEAP(Char, jchar, type, buf, offset, length, array); break; + default: - throwException(env, IllegalArgument, "Invalid array type: %#x\n", paramType); + throwException(env, IllegalArgument, "invalid array type: %#x\n", type); return NULL; } } - + return array->elems; } @@ -154,6 +159,10 @@ jffi_getArrayBuffer(JNIEnv* env, jobject buf, jint offset, jint length, int type array->elems = buffer; array->offset = offset; array->length = length; + array->type = type; + array->release = NULL; + array->copyin = NULL; + array->copyout = NULL; switch (type & ARGPRIM_MASK) { case com_kenai_jffi_ObjectBuffer_BYTE: @@ -183,6 +192,14 @@ jffi_getArrayBuffer(JNIEnv* env, jobject buf, jint offset, jint length, int type case com_kenai_jffi_ObjectBuffer_DOUBLE: GET_ARRAY_BUFFER(Double, jdouble, type, buf, offset, length, array); break; + + case com_kenai_jffi_ObjectBuffer_BOOLEAN: + GET_ARRAY_BUFFER(Boolean, jboolean, type, buf, offset, length, array); + break; + + case com_kenai_jffi_ObjectBuffer_CHAR: + GET_ARRAY_BUFFER(Char, jchar, type, buf, offset, length, array); + break; default: throwException(env, IllegalArgument, "Invalid array type: %#x\n", type); return NULL; @@ -225,19 +242,100 @@ jffi_releaseCriticalArray(JNIEnv* env, Array *array) void* -jffi_getArrayCritical(JNIEnv* env, jobject buf, jsize offset, jsize length, int paramType, struct Array* array) +jffi_getArrayCritical(JNIEnv* env, jobject buf, jsize offset, jsize length, int type, struct Array* array) { array->array = buf; array->offset = offset; array->length = length; + array->type = type; + array->copyin = NULL; + array->copyout = NULL; + array->release = jffi_releaseCriticalArray; array->elems = (*env)->GetPrimitiveArrayCritical(env, array->array, NULL); if (unlikely(array->elems == NULL)) { - throwException(env, NullPointer, "could not access array"); + if (!(*env)->ExceptionCheck(env)) { + throwException(env, NullPointer, "failed to pin native array"); + } return NULL; } - array->release = jffi_releaseCriticalArray; return (char *) array->elems + offset; } +void +jffi_releaseArrays(JNIEnv *env, Array* arrays, int arrayCount) +{ + int aryIdx; + for (aryIdx = arrayCount - 1; aryIdx >= 0; aryIdx--) { + + Array* array = &arrays[aryIdx]; + if (IS_OUT_ARRAY(array->type) && array->copyout != NULL) { + if ((*env)->ExceptionCheck(env) == JNI_FALSE) { + (*array->copyout)(env, array->array, array->offset, array->length, array->elems); + } + } + + if (unlikely(array->release != NULL)) { + (*array->release)(env, array); + } + } +} + +#define INIT_ARRAY(array, JTYPE, componentSize_) do { \ + array->copyin = (void (*)(JNIEnv*, jobject, jsize, jsize, void *))(*env)->Get##JTYPE##ArrayRegion; \ + array->copyout = (void (*)(JNIEnv*, jobject, jsize, jsize, const void *))(*env)->Set##JTYPE##ArrayRegion; \ + array->bytesize = componentSize_ * array->length; \ + if ((array->type & ARRAY_NULTERMINATE) != 0) array->bytesize += componentSize_; \ +} while(0) + +bool +jffi_initArray(JNIEnv* env, jobject buf, jint offset, jint length, int type, struct Array* array) +{ + array->array = buf; + array->offset = offset; + array->length = length; + array->type = type; + array->release = NULL; + array->elems = NULL; + + switch (type & ARGPRIM_MASK) { + case com_kenai_jffi_ObjectBuffer_BYTE: + INIT_ARRAY(array, Byte, sizeof(jbyte)); + break; + + case com_kenai_jffi_ObjectBuffer_SHORT: + INIT_ARRAY(array, Short, sizeof(jshort)); + break; + + case com_kenai_jffi_ObjectBuffer_INT: + INIT_ARRAY(array, Int, sizeof(jint)); + break; + + case com_kenai_jffi_ObjectBuffer_LONG: + INIT_ARRAY(array, Long, sizeof(jlong)); + break; + + case com_kenai_jffi_ObjectBuffer_FLOAT: + INIT_ARRAY(array, Float, sizeof(jfloat)); + break; + + case com_kenai_jffi_ObjectBuffer_DOUBLE: + INIT_ARRAY(array, Double, sizeof(jdouble)); + break; + + case com_kenai_jffi_ObjectBuffer_BOOLEAN: + INIT_ARRAY(array, Boolean, sizeof(jboolean)); + break; + + case com_kenai_jffi_ObjectBuffer_CHAR: + INIT_ARRAY(array, Char, sizeof(jchar)); + break; + + default: + throwException(env, IllegalArgument, "invalid array type: %#x\n", type); + return false; + } + + return true; +} diff --git a/jni/jffi/Array.h b/jni/jffi/Array.h index e07fe71..494fc05 100644 --- a/jni/jffi/Array.h +++ b/jni/jffi/Array.h @@ -37,22 +37,44 @@ // WARNING! Do not change the layout of this struct, it may be used from assembler // typedef struct Array { + void (*copyin)(JNIEnv* env, jobject array, jsize start, jsize len, void *buf); + void (*copyout)(JNIEnv* env, jobject array, jsize start, jsize len, const void *buf); void (*release)(JNIEnv *env, struct Array *); jobject array; void* elems; int offset; int length; + int type; + int bytesize; // total size of array in bytes } Array; +extern bool jffi_getInitArray(JNIEnv* env, jobject buf, jint offset, jint length, int type, struct Array* array); extern void* jffi_getArrayBuffer(JNIEnv* env, jobject buf, jint offset, jint length, int type, struct Array* array, void* buffer); extern void* jffi_getArrayHeap(JNIEnv* env, jobject buf, jint offset, jint length, int type, struct Array* array); extern void* jffi_getArrayCritical(JNIEnv* env, jobject buf, jint offset, jint length, int type, struct Array* array); extern int jffi_arraySize(int length, int type); +extern void jffi_releaseArrays(JNIEnv* env, Array* arrays, int arrayCount); #include "com_kenai_jffi_ObjectBuffer.h" #define OBJ_INDEX_MASK com_kenai_jffi_ObjectBuffer_INDEX_MASK #define OBJ_INDEX_SHIFT com_kenai_jffi_ObjectBuffer_INDEX_SHIFT +#define ARRAY_NULTERMINATE com_kenai_jffi_ObjectBuffer_ZERO_TERMINATE +#define ARRAY_IN com_kenai_jffi_ObjectBuffer_IN +#define ARRAY_OUT com_kenai_jffi_ObjectBuffer_OUT +#define ARRAY_PINNED com_kenai_jffi_ObjectBuffer_PINNED +#define ARRAY_CLEAR com_kenai_jffi_ObjectBuffer_CLEAR + +#define IS_PINNED_ARRAY(flags) \ + (((flags) & (com_kenai_jffi_ObjectBuffer_ARRAY | com_kenai_jffi_ObjectBuffer_PINNED)) == (com_kenai_jffi_ObjectBuffer_ARRAY | com_kenai_jffi_ObjectBuffer_PINNED)) + +#define IS_UNPINNED_ARRAY(flags) \ + (((flags) & (com_kenai_jffi_ObjectBuffer_ARRAY | com_kenai_jffi_ObjectBuffer_PINNED)) == com_kenai_jffi_ObjectBuffer_ARRAY) + +#define IS_OUT_ARRAY(flags) (((flags) & (com_kenai_jffi_ObjectBuffer_ARRAY | ARRAY_IN | ARRAY_OUT)) != (com_kenai_jffi_ObjectBuffer_ARRAY | ARRAY_IN)) +#define IS_IN_ARRAY(flags) (((flags) & (com_kenai_jffi_ObjectBuffer_ARRAY | ARRAY_IN | ARRAY_OUT)) != (com_kenai_jffi_ObjectBuffer_ARRAY | ARRAY_IN)) + +#define RELEASE_ARRAYS(env, arrays, arrayCount) jffi_releaseArrays(env, arrays, arrayCount) #endif /* jffi_Array_h */ diff --git a/jni/jffi/Invoke.c b/jni/jffi/Invoke.c index e49c399..17968d4 100644 --- a/jni/jffi/Invoke.c +++ b/jni/jffi/Invoke.c @@ -52,6 +52,14 @@ #define MAX_STACK_ARRAY (1024) + +typedef struct Pinned { + jobject object; + jsize offset; + jsize length; + int type; +} Pinned; + #ifdef USE_RAW static void invokeArray(JNIEnv* env, jlong ctxAddress, jbyteArray paramBuffer, void* returnBuffer) @@ -220,9 +228,11 @@ invokeArrayWithObjects_(JNIEnv* env, jlong ctxAddress, jbyteArray paramBuffer, jbyte *tmpBuffer = NULL; void **ffiArgs = NULL; Array *arrays = NULL; - unsigned int i, arrayCount = 0, paramBytes = 0; + Pinned *pinned = NULL; + unsigned int i, arrayCount = 0, pinnedCount = 0, paramBytes = 0; arrays = alloca(objectCount * sizeof(Array)); + pinned = alloca(objectCount * sizeof(Pinned)); #if defined(USE_RAW) paramBytes = ctx->rawParameterSize; @@ -255,32 +265,45 @@ invokeArrayWithObjects_(JNIEnv* env, jlong ctxAddress, jbyteArray paramBuffer, if (unlikely(object == NULL)) { throwException(env, NullPointer, "null object for parameter %d", idx); goto cleanup; + } - } else if (unlikely((type & com_kenai_jffi_ObjectBuffer_PINNED) != 0)) { - - ptr = jffi_getArrayCritical(env, object, offset, length, type, &arrays[arrayCount]); - if (unlikely(ptr == NULL)) { - goto cleanup; - } - } else if (true && likely(length < MAX_STACK_ARRAY)) { + if (unlikely((type & com_kenai_jffi_ObjectBuffer_PINNED) != 0)) { + + // Record the pinned array, but the actual pinning will be done just before the ffi_call + Pinned* p = &pinned[pinnedCount++]; + p->object = object; + p->offset = offset; + p->length = length; + p->type = type; + ptr = NULL; + + } else if (likely(length < MAX_STACK_ARRAY)) { ptr = alloca(jffi_arraySize(length + 1, type)); if (unlikely(jffi_getArrayBuffer(env, object, offset, length, type, &arrays[arrayCount], ptr) == NULL)) { goto cleanup; } + ++arrayCount; + } else { ptr = jffi_getArrayHeap(env, object, offset, length, type, &arrays[arrayCount]); if (unlikely(ptr == NULL)) { goto cleanup; } + ++arrayCount; } - ++arrayCount; + break; case com_kenai_jffi_ObjectBuffer_BUFFER: + if (unlikely(object == NULL)) { + throwException(env, NullPointer, "null object for parameter %d", idx); + goto cleanup; + } + ptr = (*env)->GetDirectBufferAddress(env, object); if (unlikely(ptr == NULL)) { throwException(env, NullPointer, "Could not get direct Buffer address"); @@ -320,6 +343,35 @@ invokeArrayWithObjects_(JNIEnv* env, jlong ctxAddress, jbyteArray paramBuffer, } #endif } + + // + // Pin all the arrays just before calling the native function. + // + // Although hotspot allows it, other JVMs do not allow JNI operations + // once any array has been pinned, so pinning must be done last, just before + // the native function is called. + // + for (i = 0; i < pinnedCount; i++) { + Pinned* p = &pinned[i]; + Array* ary = &arrays[arrayCount]; + int idx = (p->type & com_kenai_jffi_ObjectBuffer_INDEX_MASK) >> com_kenai_jffi_ObjectBuffer_INDEX_SHIFT; + + void* ptr = jffi_getArrayCritical(env, p->object, p->offset, p->length, p->type, &arrays[arrayCount]); + if (unlikely(ptr == NULL)) { + goto cleanup; + } +#if defined(USE_RAW) + *((void **)(tmpBuffer + ctx->rawParamOffsets[idx])) = ptr; +#else + if (unlikely(ctx->cif.arg_types[idx]->type == FFI_TYPE_STRUCT)) { + ffiArgs[idx] = ptr; + } else { + *((void **) ffiArgs[idx]) = ptr; + } +#endif + ++arrayCount; + } + #if defined(USE_RAW) // // Special case for struct return values - unroll into a ptr array and @@ -340,12 +392,7 @@ invokeArrayWithObjects_(JNIEnv* env, jlong ctxAddress, jbyteArray paramBuffer, SAVE_ERRNO(ctx); cleanup: /* Release any array backing memory */ - for (i = 0; i < arrayCount; ++i) { - if (arrays[i].release != NULL) { - //printf("releasing array=%p\n", arrays[i].elems); - (*arrays[i].release)(env, &arrays[i]); - } - } + RELEASE_ARRAYS(env, arrays, arrayCount); } static void -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jffi-next.git _______________________________________________ pkg-java-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-java-commits

