JNIEXPORT {{ jni_return_type }} JNICALL {{ jni_name }}(JNIEnv *env, jclass cls{% if include_handle %}, jlong handle{% endif %}{{ jni_params }}) {
{%- if include_handle %}
{%- if return_is_unit %}
    if (handle == 0) return;
{%- elif return_is_direct %}
    if (handle == 0) return 0;
{%- else %}
    if (handle == 0) return NULL;
{%- endif %}
{%- endif %}
{%- for param in params %}
{%- if param.is_string() %}
    const char* _{{ param.name }}_c = {{ param.name }} ? (*env)->GetStringUTFChars(env, {{ param.name }}, NULL) : NULL;
{%- endif %}
{%- if param.is_primitive_array() %}
    uintptr_t _{{ param.name }}_len = (uintptr_t)(*env)->GetArrayLength(env, {{ param.name }});
    {{ param.array_c_type() }}* _{{ param.name }}_ptr = ({{ param.array_c_type() }}*)(*env)->GetPrimitiveArrayCritical(env, {{ param.name }}, NULL);
{%- endif %}
{%- if param.is_buffer() %}
    jlong _{{ param.name }}_size = (*env)->GetDirectBufferCapacity(env, {{ param.name }});
    uint8_t* _{{ param.name }}_ptr = (uint8_t*)(*env)->GetDirectBufferAddress(env, {{ param.name }});
    uintptr_t _{{ param.name }}_len = (_{{ param.name }}_ptr && _{{ param.name }}_size > 0) ? (uintptr_t)_{{ param.name }}_size : 0;
{%- endif %}

{%- endfor %}
{%- if return_is_unit %}
    {{ ffi_name }}({% if include_handle %}(void*)handle{% if params.len() > 0 %}, {% endif %}{% endif %}{% for param in params %}{{ param.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- elif return_is_direct %}
    {{ jni_c_return_type }} _result = {{ ffi_name }}({% if include_handle %}(void*)handle{% if params.len() > 0 %}, {% endif %}{% endif %}{% for param in params %}{{ param.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- else %}
    FfiBuf_u8 _buf = {{ ffi_name }}({% if include_handle %}(void*)handle{% if params.len() > 0 %}, {% endif %}{% endif %}{% for param in params %}{{ param.ffi_arg() }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endif %}
{%- for param in params %}
{%- if param.is_primitive_array() %}
    (*env)->ReleasePrimitiveArrayCritical(env, {{ param.name }}, _{{ param.name }}_ptr, {{ param.array_release_mode() }});
{%- endif %}
{%- if param.is_string() %}
    if ({{ param.name }}) (*env)->ReleaseStringUTFChars(env, {{ param.name }}, _{{ param.name }}_c);
{%- endif %}

{%- endfor %}
{%- if return_is_direct %}
    return {{ jni_result_cast }}_result;
{%- elif !return_is_unit %}
    if (_buf.ptr == NULL) return NULL;
    return (*env)->NewDirectByteBuffer(env, (void*)_buf.ptr, (jlong)_buf.len);
{%- endif %}
}
