initial commit
This commit is contained in:
+256
@@ -0,0 +1,256 @@
|
||||
// Array marshalling between Odin slices and Java arrays.
|
||||
//
|
||||
// GENERATED by jnigen. Do not edit.
|
||||
//
|
||||
// Every to_* returns a LOCAL ref the caller must free; every from_* allocates
|
||||
// with the given allocator and the caller owns the result.
|
||||
package jni
|
||||
|
||||
import "shared:android"
|
||||
|
||||
to_bool_array :: proc(e: Env, vals: []bool) -> android.jbooleanArray {
|
||||
arr := e^^.NewBooleanArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetBooleanArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jboolean)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_bool_array :: proc(e: Env, arr: android.jbooleanArray, allocator := context.allocator) -> []bool {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]bool, n, allocator)
|
||||
e^^.GetBooleanArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jboolean)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
to_i8_array :: proc(e: Env, vals: []i8) -> android.jbyteArray {
|
||||
arr := e^^.NewByteArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetByteArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jbyte)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_i8_array :: proc(e: Env, arr: android.jbyteArray, allocator := context.allocator) -> []i8 {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]i8, n, allocator)
|
||||
e^^.GetByteArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jbyte)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
to_u16_array :: proc(e: Env, vals: []u16) -> android.jcharArray {
|
||||
arr := e^^.NewCharArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetCharArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jchar)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_u16_array :: proc(e: Env, arr: android.jcharArray, allocator := context.allocator) -> []u16 {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]u16, n, allocator)
|
||||
e^^.GetCharArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jchar)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
to_i16_array :: proc(e: Env, vals: []i16) -> android.jshortArray {
|
||||
arr := e^^.NewShortArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetShortArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jshort)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_i16_array :: proc(e: Env, arr: android.jshortArray, allocator := context.allocator) -> []i16 {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]i16, n, allocator)
|
||||
e^^.GetShortArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jshort)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
to_i32_array :: proc(e: Env, vals: []i32) -> android.jintArray {
|
||||
arr := e^^.NewIntArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetIntArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jint)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_i32_array :: proc(e: Env, arr: android.jintArray, allocator := context.allocator) -> []i32 {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]i32, n, allocator)
|
||||
e^^.GetIntArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jint)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
to_i64_array :: proc(e: Env, vals: []i64) -> android.jlongArray {
|
||||
arr := e^^.NewLongArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetLongArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jlong)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_i64_array :: proc(e: Env, arr: android.jlongArray, allocator := context.allocator) -> []i64 {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]i64, n, allocator)
|
||||
e^^.GetLongArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jlong)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
to_f32_array :: proc(e: Env, vals: []f32) -> android.jfloatArray {
|
||||
arr := e^^.NewFloatArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetFloatArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jfloat)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_f32_array :: proc(e: Env, arr: android.jfloatArray, allocator := context.allocator) -> []f32 {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]f32, n, allocator)
|
||||
e^^.GetFloatArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jfloat)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
to_f64_array :: proc(e: Env, vals: []f64) -> android.jdoubleArray {
|
||||
arr := e^^.NewDoubleArray(e, android.jsize(len(vals)))
|
||||
if arr == nil || len(vals) == 0 {
|
||||
return arr
|
||||
}
|
||||
// Identical layout, so the slice can be handed over without a copy.
|
||||
e^^.SetDoubleArrayRegion(e, arr, 0, android.jsize(len(vals)), cast([^]android.jdouble)raw_data(vals))
|
||||
return arr
|
||||
}
|
||||
|
||||
from_f64_array :: proc(e: Env, arr: android.jdoubleArray, allocator := context.allocator) -> []f64 {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]f64, n, allocator)
|
||||
e^^.GetDoubleArrayRegion(e, arr, 0, android.jsize(n), cast([^]android.jdouble)raw_data(out))
|
||||
return out
|
||||
}
|
||||
|
||||
// Object arrays. `elem_class` is the array's component type.
|
||||
to_object_array :: proc(e: Env, vals: []android.jobject, elem_class: android.jclass) -> android.jobjectArray {
|
||||
arr := e^^.NewObjectArray(e, android.jsize(len(vals)), elem_class, nil)
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
for v, i in vals {
|
||||
e^^.SetObjectArrayElement(e, arr, android.jsize(i), v)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
from_object_array :: proc(e: Env, arr: android.jobjectArray, allocator := context.allocator) -> []android.jobject {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]android.jobject, n, allocator)
|
||||
for i in 0 ..< n {
|
||||
out[i] = e^^.GetObjectArrayElement(e, arr, android.jsize(i))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// String arrays need per-element conversion in both directions.
|
||||
to_string_array :: proc(e: Env, vals: []string) -> android.jobjectArray {
|
||||
cls := class_ref(e, "java/lang/String")
|
||||
if cls == nil {
|
||||
return nil
|
||||
}
|
||||
arr := e^^.NewObjectArray(e, android.jsize(len(vals)), cls, nil)
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
for v, i in vals {
|
||||
s := to_jstring(e, v)
|
||||
e^^.SetObjectArrayElement(e, arr, android.jsize(i), android.jobject(s))
|
||||
free_local(e, android.jobject(s))
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
from_string_array :: proc(e: Env, arr: android.jobjectArray, allocator := context.allocator) -> []string {
|
||||
if arr == nil {
|
||||
return nil
|
||||
}
|
||||
n := int(e^^.GetArrayLength(e, android.jarray(arr)))
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, n, allocator)
|
||||
for i in 0 ..< n {
|
||||
el := e^^.GetObjectArrayElement(e, arr, android.jsize(i))
|
||||
out[i] = from_jstring(e, android.jstring(el), allocator)
|
||||
free_local(e, el)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
// Runtime support for generated JNI bindings: VM/env acquisition, exception
|
||||
// handling, cached class lookup, and string marshalling.
|
||||
//
|
||||
package jni
|
||||
|
||||
import "core:strings"
|
||||
import "shared:android"
|
||||
|
||||
Env :: ^android.JNIEnv
|
||||
|
||||
@(private)
|
||||
vm: ^android.JavaVM
|
||||
|
||||
// Records the process JavaVM. Call once from android_main before any binding.
|
||||
init :: proc(app: ^android.android_app) -> bool {
|
||||
if app == nil || app.activity == nil {
|
||||
return false
|
||||
}
|
||||
vm = app.activity.vm
|
||||
return vm != nil
|
||||
}
|
||||
|
||||
// The Activity instance, usable anywhere a Context is expected.
|
||||
activity :: proc(app: ^android.android_app) -> android.jobject {
|
||||
if app == nil || app.activity == nil {
|
||||
return nil
|
||||
}
|
||||
return app.activity.clazz
|
||||
}
|
||||
|
||||
// The engine's loop runs on a glue-spawned thread, not the one holding
|
||||
// activity.env, so the env has to come from the VM and the thread attached on
|
||||
// first use. The thread lives as long as the app, so it is never detached.
|
||||
@(thread_local)
|
||||
tls_env: Env
|
||||
|
||||
env :: proc() -> Env {
|
||||
if tls_env != nil {
|
||||
return tls_env
|
||||
}
|
||||
if vm == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e: Env
|
||||
if vm^^.GetEnv(vm, cast(^rawptr)&e, android.JNI_VERSION_1_6) == android.JNI_EDETACHED {
|
||||
if vm^^.AttachCurrentThread(vm, &e, nil) != android.JNI_OK {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
tls_env = e
|
||||
return e
|
||||
}
|
||||
|
||||
//
|
||||
// Exceptions
|
||||
//
|
||||
|
||||
// Reports whether a Java exception is pending, printing and clearing it if so.
|
||||
// Left pending, an exception makes every later JNI call on this thread fail.
|
||||
failed :: proc(e: Env) -> bool {
|
||||
if e == nil {
|
||||
return true
|
||||
}
|
||||
if e^^.ExceptionCheck(e) == 0 {
|
||||
return false
|
||||
}
|
||||
e^^.ExceptionDescribe(e)
|
||||
e^^.ExceptionClear(e)
|
||||
return true
|
||||
}
|
||||
|
||||
//
|
||||
// Lookup
|
||||
//
|
||||
|
||||
// Resolves a class and pins it with a global ref. Generated code caches the
|
||||
// result in a static, so a class is only ever looked up once. A local ref would
|
||||
// go stale the moment the native frame returns.
|
||||
class_ref :: proc(e: Env, name: cstring) -> android.jclass {
|
||||
local := e^^.FindClass(e, name)
|
||||
if local == nil || failed(e) {
|
||||
return nil
|
||||
}
|
||||
global := e^^.NewGlobalRef(e, android.jobject(local))
|
||||
e^^.DeleteLocalRef(e, android.jobject(local))
|
||||
return android.jclass(global)
|
||||
}
|
||||
|
||||
method_id :: proc(e: Env, cls: android.jclass, name, sig: cstring) -> android.jmethodID {
|
||||
id := e^^.GetMethodID(e, cls, name, sig)
|
||||
if failed(e) {
|
||||
return nil
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
static_method_id :: proc(e: Env, cls: android.jclass, name, sig: cstring) -> android.jmethodID {
|
||||
id := e^^.GetStaticMethodID(e, cls, name, sig)
|
||||
if failed(e) {
|
||||
return nil
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
field_id :: proc(e: Env, cls: android.jclass, name, sig: cstring) -> android.jfieldID {
|
||||
id := e^^.GetFieldID(e, cls, name, sig)
|
||||
if failed(e) {
|
||||
return nil
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
static_field_id :: proc(e: Env, cls: android.jclass, name, sig: cstring) -> android.jfieldID {
|
||||
id := e^^.GetStaticFieldID(e, cls, name, sig)
|
||||
if failed(e) {
|
||||
return nil
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
free_local :: proc(e: Env, obj: android.jobject) {
|
||||
if obj != nil {
|
||||
e^^.DeleteLocalRef(e, obj)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Strings
|
||||
//
|
||||
|
||||
to_jstring :: proc(e: Env, s: string) -> android.jstring {
|
||||
c := strings.clone_to_cstring(s, context.temp_allocator)
|
||||
return e^^.NewStringUTF(e, c)
|
||||
}
|
||||
|
||||
from_jstring :: proc(e: Env, s: android.jstring, allocator := context.allocator) -> string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
utf := e^^.GetStringUTFChars(e, s, nil)
|
||||
if utf == nil {
|
||||
return ""
|
||||
}
|
||||
out := strings.clone(string(utf), allocator)
|
||||
e^^.ReleaseStringUTFChars(e, s, utf)
|
||||
return out
|
||||
}
|
||||
|
||||
//
|
||||
// Local reference frames
|
||||
//
|
||||
// Every object returned by a binding is a LOCAL ref, and locals are only
|
||||
// reclaimed when control returns to Java -- which never happens on a native
|
||||
// thread that owns its own loop. A sequence of object-returning calls made
|
||||
// per-frame will therefore exhaust the local ref table (512 entries) and abort
|
||||
// the process. Scope such a sequence in a frame:
|
||||
//
|
||||
// if !jni.push_frame(env) { return false }
|
||||
// defer jni.pop_frame(env)
|
||||
//
|
||||
// Anything still needed after pop_frame must be a primitive, a copied string,
|
||||
// or passed to pop_frame as `keep` to be promoted into the outer frame.
|
||||
//
|
||||
|
||||
push_frame :: proc(e: Env, capacity: i32 = 16) -> bool {
|
||||
if e == nil {
|
||||
return false
|
||||
}
|
||||
return e^^.PushLocalFrame(e, android.jint(capacity)) == android.JNI_OK
|
||||
}
|
||||
|
||||
pop_frame :: proc(e: Env, keep: android.jobject = nil) -> android.jobject {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
return e^^.PopLocalFrame(e, keep)
|
||||
}
|
||||
Reference in New Issue
Block a user