blob: e2fdc342b13a21685433cc52be3f9563db8550c8 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build java android
// +build cgo
package main
import (
"fmt"
"runtime"
"unsafe"
)
// #include <stdlib.h>
// #include "jni_wrapper.h"
// #include "lib.h"
import "C"
func jGetMethodID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jmethodID {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
cSig := C.CString(sig)
defer C.free(unsafe.Pointer(cSig))
method := C.GetMethodID(env, cls, cName, cSig)
if method == nil {
panic(fmt.Sprintf("couldn't get method %q with signature %s", name, sig))
}
// Note: the validity of the method is bounded by the lifetime of the
// ClassLoader that did the loading of the class.
return method
}
func jGetFieldID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jfieldID {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
cSig := C.CString(sig)
defer C.free(unsafe.Pointer(cSig))
field := C.GetFieldID(env, cls, cName, cSig)
if field == nil {
panic(fmt.Sprintf("couldn't get field %q with signature %s", name, sig))
}
// Note: the validity of the field is bounded by the lifetime of the
// ClassLoader that did the loading of the class.
return field
}
func jGetStaticFieldID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jfieldID {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
cSig := C.CString(sig)
defer C.free(unsafe.Pointer(cSig))
field := C.GetStaticFieldID(env, cls, cName, cSig)
if field == nil {
panic(fmt.Sprintf("couldn't get field %q with signature %s", name, sig))
}
// Note: the validity of the field is bounded by the lifetime of the
// ClassLoader that did the loading of the class.
return field
}
// The function from below was hoisted from jni/util/util.go and adapted to not
// use custom types.
// GetEnv returns the Java environment for the running thread, creating a new
// one if it doesn't already exist. This method also returns a function which
// must be invoked when the returned environment is no longer needed. The
// returned environment can only be used by the thread that invoked this method,
// and the function must be invoked by the same thread as well.
func getEnv() (*C.JNIEnv, func()) {
// Lock the goroutine to the current OS thread. This is necessary as
// *C.JNIEnv must not be shared across threads. The scenario that can
// break this requirement is:
// - goroutine A executing on thread X, obtains a *C.JNIEnv pointer P.
// - goroutine A gets re-scheduled on thread Y, maintaining the P.
// - goroutine B starts executing on thread X, obtaining pointer P.
//
// By locking the goroutines to their OS thread while they hold the
// pointer to *C.JNIEnv, the above scenario can never occur.
runtime.LockOSThread()
var env *C.JNIEnv
if C.GetEnv(jVM, &env, C.JNI_VERSION_1_6) != C.JNI_OK {
// Couldn't get env; attach the thread. Note that we never
// detach the thread, so the next call to GetEnv on this thread
// will succeed. We also don't have to worry about calling
// DetachCurrentThread before the thread exits.
C.AttachCurrentThreadAsDaemon(jVM, &env, nil)
}
// GetEnv is called by Go code that wishes to call Java methods. In
// this case, JNI cannot automatically free unused local references.
// We must do it manually by pushing a new local reference frame. The
// frame will be popped in the env's cleanup function below, at which
// point JNI will free the unused references.
// http://developer.android.com/training/articles/perf-jni.html states
// that the JNI implementation is only required to provide a local
// reference table with a capacity of 16, so here we provide a table of
// that size.
localRefCapacity := 16
if newCapacity := C.PushLocalFrame(env, C.jint(localRefCapacity)); newCapacity < 0 {
panic("PushLocalFrame(" + string(localRefCapacity) + ") returned < 0 (was " + string(newCapacity) + ")")
}
return env, func() {
C.PopLocalFrame(env, nil)
runtime.UnlockOSThread()
}
}