From b5d97bc020b962a33c5d7e5d75e43458ed7e75f1 Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Fri, 26 Jun 2020 15:26:00 -0400 Subject: [PATCH 1/2] feat: implement calling more non-static method types --- gojni.h | 7 +++++++ jni.c | 28 ++++++++++++++++++++++++++++ jni.go | 45 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/gojni.h b/gojni.h index b4c0e78..2d0cb86 100644 --- a/gojni.h +++ b/gojni.h @@ -29,6 +29,13 @@ __attribute__ ((visibility ("hidden"))) void _jni_CallStaticVoidMethodA(JNIEnv * __attribute__ ((visibility ("hidden"))) jobject _jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); __attribute__ ((visibility ("hidden"))) jint _jni_CallIntMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); __attribute__ ((visibility ("hidden"))) void _jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); +__attribute__ ((visibility ("hidden"))) jboolean _jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); +__attribute__ ((visibility ("hidden"))) jbyte _jni_CallByteMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); +__attribute__ ((visibility ("hidden"))) jchar _jni_CallCharMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); +__attribute__ ((visibility ("hidden"))) jshort _jni_CallShortMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); +__attribute__ ((visibility ("hidden"))) jlong _jni_CallLongMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); +__attribute__ ((visibility ("hidden"))) jfloat _jni_CallFloatMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); +__attribute__ ((visibility ("hidden"))) jdouble _jni_CallDoubleMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); __attribute__ ((visibility ("hidden"))) jbyteArray _jni_NewByteArray(JNIEnv *env, jsize length); __attribute__ ((visibility ("hidden"))) jbyte *_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr); __attribute__ ((visibility ("hidden"))) void _jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *elems, jint mode); diff --git a/jni.c b/jni.c index dea749d..ef7ee95 100644 --- a/jni.c +++ b/jni.c @@ -134,6 +134,34 @@ void _jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *ar (*env)->CallVoidMethodA(env, obj, method, args); } +jboolean _jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallBooleanMethodA(env, obj, method, args); +} + +jbyte _jni_CallByteMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallByteMethodA(env, obj, method, args); +} + +jchar _jni_CallCharMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallCharMethodA(env, obj, method, args); +} + +jshort _jni_CallShortMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallShortMethodA(env, obj, method, args); +} + +jlong _jni_CallLongMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallLongMethodA(env, obj, method, args); +} + +jfloat _jni_CallFloatMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallFloatMethodA(env, obj, method, args); +} + +jdouble _jni_CallDoubleMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallDoubleMethodA(env, obj, method, args); +} + jbyteArray _jni_NewByteArray(JNIEnv *env, jsize length) { return (*env)->NewByteArray(env, length); } diff --git a/jni.go b/jni.go index 3134210..c900a30 100644 --- a/jni.go +++ b/jni.go @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. - // Package jni implements various helper functions for communicating with the // Android JVM though JNI. package jni @@ -44,7 +43,7 @@ type ( ) const ( - TRUE = C.JNI_TRUE + TRUE = C.JNI_TRUE FALSE = C.JNI_FALSE ) @@ -155,6 +154,48 @@ func CallIntMethod(e Env, obj Object, method MethodID, args ...Value) (int32, er return int32(res), exception(e) } +// CallBooleanMethod calls a method on an object, returning a bool. +func CallBooleanMethod(e Env, obj Object, method MethodID, args ...Value) (bool, error) { + res := C._jni_CallBooleanMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args)) + return res == TRUE, exception(e) +} + +// CallByteMethod calls a method on an object, returning a byte. +func CallByteMethod(e Env, obj Object, method MethodID, args ...Value) (byte, error) { + res := C._jni_CallByteMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args)) + return byte(res), exception(e) +} + +// CallCharMethod calls a method on an object, returning a rune. +func CallCharMethod(e Env, obj Object, method MethodID, args ...Value) (rune, error) { + res := C._jni_CallCharMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args)) + return rune(res), exception(e) +} + +// CallShortMethod calls a method on an object, returning an int32. +func CallShortMethod(e Env, obj Object, method MethodID, args ...Value) (int16, error) { + res := C._jni_CallShortMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args)) + return int16(res), exception(e) +} + +// CallLongMethod calls a method on an object, returning an int64. +func CallLongMethod(e Env, obj Object, method MethodID, args ...Value) (int64, error) { + res := C._jni_CallLongMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args)) + return int64(res), exception(e) +} + +// CallFloatMethod calls a method on an object, returning a float32. +func CallFloatMethod(e Env, obj Object, method MethodID, args ...Value) (float32, error) { + res := C._jni_CallFloatMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args)) + return float32(res), exception(e) +} + +// CallDoubleMethod calls a method on an object, returning a float64. +func CallDoubleMethod(e Env, obj Object, method MethodID, args ...Value) (float64, error) { + res := C._jni_CallDoubleMethodA(e.env, C.jobject(obj), C.jmethodID(method), varArgs(args)) + return float64(res), exception(e) +} + // GetByteArrayElements returns the contents of the array. func GetByteArrayElements(e Env, jarr ByteArray) []byte { size := C._jni_GetArrayLength(e.env, C.jarray(jarr)) From b74a17279b1f554d84c153100d63a1b5ad59b4b8 Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Fri, 26 Jun 2020 15:40:17 -0400 Subject: [PATCH 2/2] tests: add tests for calling new method types --- jni_test.go | 163 +++++++++++++++++++++++++++++++++++++++++++++- test/AClass.class | Bin 690 -> 1126 bytes test/AClass.java | 28 ++++++++ 3 files changed, 190 insertions(+), 1 deletion(-) diff --git a/jni_test.go b/jni_test.go index 1685fb7..8301719 100644 --- a/jni_test.go +++ b/jni_test.go @@ -9,7 +9,7 @@ import ( ) var ( - vm JVM + vm JVM vmOnce sync.Once ) @@ -145,6 +145,167 @@ func TestIntMethod(t *testing.T) { } } +func TestBooleanMethod(t *testing.T) { + err := Do(vm, func(env Env) error { + cls := FindClass(env, "test/AClass") + mid := GetMethodID(env, cls, "", "()V") + inst, err := NewObject(env, cls, mid) + mid = GetMethodID(env, cls, "GetBoolean", "()Z") + if mid == nil { + t.Errorf("MethodID is nil") + } + res, err := CallBooleanMethod(env, inst, mid) + if err != nil { + t.Errorf("Method invocation failed") + } + if !res { + t.Errorf("Method returned %v, not expected value of %v.", res, true) + } + return nil + }) + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func TestByteMethod(t *testing.T) { + err := Do(vm, func(env Env) error { + cls := FindClass(env, "test/AClass") + mid := GetMethodID(env, cls, "", "()V") + inst, err := NewObject(env, cls, mid) + mid = GetMethodID(env, cls, "GetByte", "()B") + if mid == nil { + t.Errorf("MethodID is nil") + } + res, err := CallByteMethod(env, inst, mid) + if err != nil { + t.Errorf("Method invocation failed") + } + if res != 127 { + t.Errorf("Method returned %d, not expected value of %d.", res, 127) + } + return nil + }) + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func TestCharMethod(t *testing.T) { + err := Do(vm, func(env Env) error { + cls := FindClass(env, "test/AClass") + mid := GetMethodID(env, cls, "", "()V") + inst, err := NewObject(env, cls, mid) + mid = GetMethodID(env, cls, "GetChar", "()C") + if mid == nil { + t.Errorf("MethodID is nil") + } + res, err := CallCharMethod(env, inst, mid) + if err != nil { + t.Errorf("Method invocation failed") + } + if res != 65432 { + t.Errorf("Method returned %d, not expected value of %d.", res, 65432) + } + return nil + }) + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func TestShortMethod(t *testing.T) { + err := Do(vm, func(env Env) error { + cls := FindClass(env, "test/AClass") + mid := GetMethodID(env, cls, "", "()V") + inst, err := NewObject(env, cls, mid) + mid = GetMethodID(env, cls, "GetShort", "()S") + if mid == nil { + t.Errorf("MethodID is nil") + } + res, err := CallShortMethod(env, inst, mid) + if err != nil { + t.Errorf("Method invocation failed") + } + if res != 512 { + t.Errorf("Method returned %d, not expected value of %d.", res, 512) + } + return nil + }) + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func TestLongMethod(t *testing.T) { + err := Do(vm, func(env Env) error { + cls := FindClass(env, "test/AClass") + mid := GetMethodID(env, cls, "", "()V") + inst, err := NewObject(env, cls, mid) + mid = GetMethodID(env, cls, "GetLong", "()J") + if mid == nil { + t.Errorf("MethodID is nil") + } + res, err := CallLongMethod(env, inst, mid) + if err != nil { + t.Errorf("Method invocation failed") + } + if res != 1<<33 { + t.Errorf("Method returned %d, not expected value of %d.", res, 1<<33) + } + return nil + }) + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func TestFloatMethod(t *testing.T) { + err := Do(vm, func(env Env) error { + cls := FindClass(env, "test/AClass") + mid := GetMethodID(env, cls, "", "()V") + inst, err := NewObject(env, cls, mid) + mid = GetMethodID(env, cls, "GetFloat", "()F") + if mid == nil { + t.Errorf("MethodID is nil") + } + res, err := CallFloatMethod(env, inst, mid) + if err != nil { + t.Errorf("Method invocation failed") + } + if res != 4.321 { + t.Errorf("Method returned %f, not expected value of %f.", res, 4.321) + } + return nil + }) + if err != nil { + t.Errorf("Error: %s", err) + } +} + +func TestDoubleMethod(t *testing.T) { + err := Do(vm, func(env Env) error { + cls := FindClass(env, "test/AClass") + mid := GetMethodID(env, cls, "", "()V") + inst, err := NewObject(env, cls, mid) + mid = GetMethodID(env, cls, "GetDouble", "()D") + if mid == nil { + t.Errorf("MethodID is nil") + } + res, err := CallDoubleMethod(env, inst, mid) + if err != nil { + t.Errorf("Method invocation failed") + } + if res != 5.4321 { + t.Errorf("Method returned %f, not expected value of %f.", res, 5.4321) + } + return nil + }) + if err != nil { + t.Errorf("Error: %s", err) + } +} + func TestVoidMethod(t *testing.T) { err := Do(vm, func(env Env) error { cls := FindClass(env, "test/AClass") diff --git a/test/AClass.class b/test/AClass.class index b7a71a57148d9b5ece864774dc192580f4e96473..608a39e722ca1b7473287c70df4816e0d42aa6a1 100644 GIT binary patch literal 1126 zcmZvcTTc^F6ouF6Ogp^+1;Gmx6hvr8p<<$mh8Un&Q$zHjh6kS<+6k15Gf6uo`sBO6 zz-OPm#9K^w@CW##)V0q{CSgL8&YZK)x7S%`@6-PId-@x|1KcSh1H*z@Mix1R%CU3e zG(5*YY&<(ScDtcMZnN+A!-on+t-fDC9t8`DX6w^ z<7G?;R33&-*zZP*2Jr8FmV0OTg}L?a(TCjD%;{@y>8Pk+HiMq4FtOM7-KV2h2kzjx zbKo&je(Z)FEz|btiB{SScFqLNTD!ha7j3_*p&WDcpvb{a5O}WR>(mztmW1AfRHoEU zG;ba`13hmlv6ye zWovEiF~8aM;;I%q!D!HRTYWiZDJtc z%hklpDh!R6Z2Jc+^W`~iCh2O5(0t-%o-@6NK z%4rgpQx`s`z%?Q^ZpL6W?lC2&zof_wGPja`Es>O4le6c}sr_Bvn|tKfupTQfvtj&E mB5%)C&+FEGSp1*Jqmi_Dftm8jReEQxM07xttGBtD!Nz}jr-WSq literal 690 zcmZut%Syvg5Iwg^lRl!Y_1zaJqBRS;@UI~5{W68Yro2kF9a8UfFC8E zds_pRZtl#TIp@ro`TqI*0&s|S33+T5VWO^{hK;6;mVlE=Uq(Tc$gv*_2v5NJ`&dU& z63Jr$qqBc4VD?7CP@r-iCE>;6{UCg}@&_?1&RHn?TEzL8(7_dtp0B^cm=#{2u##NL%jvEE85md|EwD{9A6ld1XDqG8$ooz0pRUnCz}oFNcj^`e#